Merge "Update bubble user education screens to use proper colors" into sc-dev
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 1bd90a8..cfcb4e7 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimPriv.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index 544bca02..0948e47 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/CtsShim.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 72386bb..db64475 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimPriv.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index 893eac2..80812df 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShim.apk"
}
diff --git a/Android.bp b/Android.bp
index 7c9cdcf..4a22369 100644
--- a/Android.bp
+++ b/Android.bp
@@ -313,6 +313,7 @@
libs: [
"app-compat-annotations",
"ext",
+ "framework-connectivity-annotations",
"framework-updatable-stubs-module_libs_api",
"unsupportedappusage",
],
@@ -326,6 +327,7 @@
"av-types-aidl-java",
"tv_tuner_resource_manager_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
+ "modules-utils-preconditions",
"modules-utils-os",
"framework-permission-aidl-java",
],
@@ -409,6 +411,7 @@
srcs: [
// TODO: remove these annotations as soon as we can use andoid.support.annotations.*
":framework-annotations",
+ ":modules-utils-preconditions-srcs",
"core/java/android/net/DhcpResults.java",
"core/java/android/util/IndentingPrintWriter.java",
"core/java/android/util/LocalLog.java",
@@ -416,7 +419,6 @@
"core/java/com/android/internal/util/IndentingPrintWriter.java",
"core/java/com/android/internal/util/IState.java",
"core/java/com/android/internal/util/MessageUtils.java",
- "core/java/com/android/internal/util/Preconditions.java",
"core/java/com/android/internal/util/RingBufferIndices.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
@@ -446,11 +448,11 @@
sdk_version: "module_current",
min_sdk_version: "30",
srcs: [
+ ":modules-utils-preconditions-srcs",
"core/java/android/os/HandlerExecutor.java",
"core/java/com/android/internal/util/AsyncChannel.java",
"core/java/com/android/internal/util/AsyncService.java",
"core/java/com/android/internal/util/Protocol.java",
- "core/java/com/android/internal/util/Preconditions.java",
"telephony/java/android/telephony/Annotation.java",
":net-utils-framework-wifi-common-srcs",
],
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index afd8e29..ac63653 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -144,7 +144,7 @@
while (state.keepRunning()) {
state.pauseTiming();
// Invalidate cache.
- resourcesManager.applyConfigurationToResourcesLocked(
+ resourcesManager.applyConfigurationToResources(
resourcesManager.getConfiguration(), null);
state.resumeTiming();
diff --git a/apct-tests/perftests/core/src/android/text/SystemFontsPerfTest.java b/apct-tests/perftests/core/src/android/text/SystemFontsPerfTest.java
new file mode 100644
index 0000000..5d744cd
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/SystemFontsPerfTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.graphics.fonts.SystemFonts;
+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;
+
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class SystemFontsPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void getAvailableFonts() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ SystemFonts.resetAvailableFonts();
+ System.gc();
+ state.resumeTiming();
+
+ SystemFonts.getAvailableFonts();
+ }
+ }
+}
diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp
index 87f65a9..c592d13 100644
--- a/apex/appsearch/Android.bp
+++ b/apex/appsearch/Android.bp
@@ -24,8 +24,8 @@
apex {
name: "com.android.appsearch",
manifest: "apex_manifest.json",
+ bootclasspath_fragments: ["com.android.appsearch-bootclasspath-fragment"],
java_libs: [
- "framework-appsearch",
"service-appsearch",
],
key: "com.android.appsearch.key",
@@ -45,3 +45,10 @@
// com.android.appsearch.pk8 (the private key)
certificate: "com.android.appsearch",
}
+
+// Encapsulate the contributions made by the com.android.appsearch to the bootclasspath.
+bootclasspath_fragment {
+ name: "com.android.appsearch-bootclasspath-fragment",
+ contents: ["framework-appsearch"],
+ apex_available: ["com.android.appsearch"],
+}
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index f92f44b..b8fce4f 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -52,14 +52,18 @@
java_sdk_library {
name: "framework-appsearch",
srcs: [":framework-appsearch-sources"],
- sdk_version: "core_platform", // TODO(b/146218515) should be module_current
- impl_only_libs: ["framework-minus-apex"], // TODO(b/146218515) should be removed
+ sdk_version: "module_current",
+ static_libs: [
+ // This list must be kept in sync with jarjar.txt
+ "modules-utils-preconditions",
+ ],
libs: ["unsupportedappusage"], // TODO(b/181887768) should be removed
defaults: ["framework-module-defaults"],
permitted_packages: ["android.app.appsearch"],
aidl: {
include_dirs: ["frameworks/base/core/java"], // TODO(b/146218515) should be removed
},
+ jarjar_rules: "jarjar-rules.txt",
apex_available: ["com.android.appsearch"],
unsafe_ignore_missing_latest_api: true, // TODO(b/146218515) should be removed
}
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 4dff436..30fb4c6 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -98,13 +98,13 @@
method @NonNull public android.app.appsearch.AppSearchSchema.DoublePropertyConfig.Builder setCardinality(int);
}
- public static final class AppSearchSchema.Int64PropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+ public static final class AppSearchSchema.LongPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
}
- public static final class AppSearchSchema.Int64PropertyConfig.Builder {
- ctor public AppSearchSchema.Int64PropertyConfig.Builder(@NonNull String);
- method @NonNull public android.app.appsearch.AppSearchSchema.Int64PropertyConfig build();
- method @NonNull public android.app.appsearch.AppSearchSchema.Int64PropertyConfig.Builder setCardinality(int);
+ public static final class AppSearchSchema.LongPropertyConfig.Builder {
+ ctor public AppSearchSchema.LongPropertyConfig.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.AppSearchSchema.LongPropertyConfig build();
+ method @NonNull public android.app.appsearch.AppSearchSchema.LongPropertyConfig.Builder setCardinality(int);
}
public abstract static class AppSearchSchema.PropertyConfig {
@@ -290,14 +290,12 @@
method @NonNull public String getDatabaseName();
method @NonNull public android.app.appsearch.GenericDocument getGenericDocument();
method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatchInfos();
- method @Deprecated @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
method @NonNull public String getPackageName();
method public double getRankingSignal();
}
public static final class SearchResult.Builder {
ctor public SearchResult.Builder(@NonNull String, @NonNull String);
- method @Deprecated @NonNull public android.app.appsearch.SearchResult.Builder addMatch(@NonNull android.app.appsearch.SearchResult.MatchInfo);
method @NonNull public android.app.appsearch.SearchResult.Builder addMatchInfo(@NonNull android.app.appsearch.SearchResult.MatchInfo);
method @NonNull public android.app.appsearch.SearchResult build();
method @NonNull public android.app.appsearch.SearchResult.Builder setGenericDocument(@NonNull android.app.appsearch.GenericDocument);
diff --git a/apex/appsearch/framework/jarjar-rules.txt b/apex/appsearch/framework/jarjar-rules.txt
new file mode 100644
index 0000000..50c3ee4
--- /dev/null
+++ b/apex/appsearch/framework/jarjar-rules.txt
@@ -0,0 +1,6 @@
+# Rename all com.android.internal.util classes to prevent class name collisions
+# between this module and the other versions of the utility classes linked into
+# the framework.
+
+# These must be kept in sync with the sources of framework-utils-appsearch
+rule com.android.internal.util.Preconditions* android.app.appsearch.internal.util.Preconditions@1
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index c4dd1a0..59b940a 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -18,6 +18,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SystemService;
+import android.app.appsearch.aidl.IAppSearchManager;
import android.content.Context;
import com.android.internal.util.Preconditions;
@@ -204,7 +205,7 @@
AppSearchSession.createSearchSession(
searchContext,
mService,
- mContext.getUserId(),
+ mContext.getUser().getIdentifier(),
getPackageName(),
executor,
callback);
@@ -227,7 +228,7 @@
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
GlobalSearchSession.createGlobalSearchSession(
- mService, mContext.getUserId(), getPackageName(), executor, callback);
+ mService, mContext.getUser().getIdentifier(), getPackageName(), executor, callback);
}
/** Returns the package name that should be used for uid verification. */
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
index ec0533e..7dc527a 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
@@ -17,6 +17,7 @@
import android.annotation.SystemApi;
import android.app.SystemServiceRegistry;
+import android.app.appsearch.aidl.IAppSearchManager;
import android.content.Context;
/**
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
index 0089c6d..353051c 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
@@ -23,6 +23,9 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.app.appsearch.exceptions.AppSearchException;
import android.os.Bundle;
import android.os.Parcel;
@@ -105,8 +108,8 @@
mUserId,
new IAppSearchResultCallback.Stub() {
@Override
- public void onResult(AppSearchResult result) {
- future.complete(result);
+ public void onResult(AppSearchResultParcel resultParcel) {
+ future.complete(resultParcel.getResult());
}
});
AppSearchResult<Void> result = future.get();
@@ -145,8 +148,8 @@
mService.putDocumentsFromFile(mPackageName, mDatabaseName, fileDescriptor, mUserId,
new IAppSearchResultCallback.Stub() {
@Override
- public void onResult(AppSearchResult result) {
- future.complete(result);
+ public void onResult(AppSearchResultParcel resultParcel) {
+ future.complete(resultParcel.getResult());
}
});
AppSearchResult<List<Bundle>> result = future.get();
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl
deleted file mode 100644
index 37ce990..0000000
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright 2020, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.app.appsearch;
-
-/** {@hide} */
-parcelable AppSearchResult<ValueType>;
\ No newline at end of file
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index c112d0e..43bf08d 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -19,6 +19,11 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.appsearch.aidl.AppSearchBatchResultParcel;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.app.appsearch.exceptions.AppSearchException;
import android.app.appsearch.util.SchemaMigrationUtil;
import android.compat.annotation.UnsupportedAppUsage;
@@ -85,15 +90,20 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<AppSearchResult<AppSearchSession>> callback) {
try {
- mService.initialize(mUserId, new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- executor.execute(() -> {
- if (result.isSuccess()) {
- callback.accept(
- AppSearchResult.newSuccessfulResult(AppSearchSession.this));
- } else {
- callback.accept(result);
- }
+ mService.initialize(mUserId,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
+ new IAppSearchResultCallback.Stub() {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> {
+ AppSearchResult<Void> result = resultParcel.getResult();
+ if (result.isSuccess()) {
+ callback.accept(
+ AppSearchResult.newSuccessfulResult(
+ AppSearchSession.this));
+ } else {
+ callback.accept(AppSearchResult.newFailedResult(result));
+ }
});
}
});
@@ -191,15 +201,16 @@
mDatabaseName,
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchResult<Bundle> result = resultParcel.getResult();
if (result.isSuccess()) {
- Bundle responseBundle = (Bundle) result.getResultValue();
GetSchemaResponse response =
- new GetSchemaResponse(responseBundle);
+ new GetSchemaResponse(result.getResultValue());
callback.accept(AppSearchResult.newSuccessfulResult(response));
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(result));
}
});
}
@@ -227,15 +238,17 @@
mDatabaseName,
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchResult<List<String>> result = resultParcel.getResult();
if (result.isSuccess()) {
Set<String> namespaces =
- new ArraySet<>((List<String>) result.getResultValue());
+ new ArraySet<>(result.getResultValue());
callback.accept(
AppSearchResult.newSuccessfulResult(namespaces));
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(result));
}
});
}
@@ -280,13 +293,14 @@
/*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchBatchResultCallback.Stub() {
@Override
- public void onResult(AppSearchBatchResult result) {
- executor.execute(() -> callback.onResult(result));
+ public void onResult(AppSearchBatchResultParcel resultParcel) {
+ executor.execute(() -> callback.onResult(resultParcel.getResult()));
}
@Override
- public void onSystemError(AppSearchResult result) {
- executor.execute(() -> sendSystemErrorToCallback(result, callback));
+ public void onSystemError(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> sendSystemErrorToCallback(
+ resultParcel.getResult(), callback));
}
});
mIsMutated = true;
@@ -339,17 +353,20 @@
new ArrayList<>(request.getIds()),
request.getProjectionsInternal(),
mUserId,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchBatchResultCallback.Stub() {
@Override
- public void onResult(AppSearchBatchResult result) {
+ public void onResult(AppSearchBatchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchBatchResult<String, Bundle> result =
+ resultParcel.getResult();
AppSearchBatchResult.Builder<String, GenericDocument>
documentResultBuilder =
new AppSearchBatchResult.Builder<>();
// Translate successful results
for (Map.Entry<String, Bundle> bundleEntry :
- ((Map<String, Bundle>) result.getSuccesses()).entrySet()) {
+ result.getSuccesses().entrySet()) {
GenericDocument document;
try {
document = new GenericDocument(bundleEntry.getValue());
@@ -380,8 +397,9 @@
}
@Override
- public void onSystemError(AppSearchResult result) {
- executor.execute(() -> sendSystemErrorToCallback(result, callback));
+ public void onSystemError(AppSearchResultParcel result) {
+ executor.execute(
+ () -> sendSystemErrorToCallback(result.getResult(), callback));
}
});
} catch (RemoteException e) {
@@ -492,8 +510,9 @@
/*systemUsage=*/ false,
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- executor.execute(() -> callback.accept(result));
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> callback.accept(resultParcel.getResult()));
}
});
mIsMutated = true;
@@ -548,15 +567,17 @@
try {
mService.removeByDocumentId(mPackageName, mDatabaseName, request.getNamespace(),
new ArrayList<>(request.getIds()), mUserId,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchBatchResultCallback.Stub() {
@Override
- public void onResult(AppSearchBatchResult result) {
- executor.execute(() -> callback.onResult(result));
+ public void onResult(AppSearchBatchResultParcel resultParcel) {
+ executor.execute(() -> callback.onResult(resultParcel.getResult()));
}
@Override
- public void onSystemError(AppSearchResult result) {
- executor.execute(() -> sendSystemErrorToCallback(result, callback));
+ public void onSystemError(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> sendSystemErrorToCallback(
+ resultParcel.getResult(), callback));
}
});
mIsMutated = true;
@@ -597,9 +618,11 @@
try {
mService.removeByQuery(mPackageName, mDatabaseName, queryExpression,
searchSpec.getBundle(), mUserId,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- executor.execute(() -> callback.accept(result));
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> callback.accept(resultParcel.getResult()));
}
});
mIsMutated = true;
@@ -629,15 +652,15 @@
mDatabaseName,
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchResult<Bundle> result = resultParcel.getResult();
if (result.isSuccess()) {
- Bundle responseBundle = (Bundle) result.getResultValue();
- StorageInfo response =
- new StorageInfo(responseBundle);
+ StorageInfo response = new StorageInfo(result.getResultValue());
callback.accept(AppSearchResult.newSuccessfulResult(response));
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(result));
}
});
}
@@ -655,7 +678,8 @@
public void close() {
if (mIsMutated && !mIsClosed) {
try {
- mService.persistToDisk(mUserId);
+ mService.persistToDisk(mUserId,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime());
mIsClosed = true;
} catch (RemoteException e) {
Log.e(TAG, "Unable to close the AppSearchSession", e);
@@ -685,14 +709,16 @@
request.isForceOverride(),
request.getVersion(),
mUserId,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchResult<Bundle> result = resultParcel.getResult();
if (result.isSuccess()) {
try {
SetSchemaResponse setSchemaResponse =
- new SetSchemaResponse(
- (Bundle) result.getResultValue());
+ new SetSchemaResponse(result.getResultValue());
if (!request.isForceOverride()) {
// Throw exception if there is any deleted types or
// incompatible types. That's the only case we swallowed
@@ -707,7 +733,7 @@
callback.accept(AppSearchResult.throwableToFailedResult(t));
}
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(result));
}
});
}
@@ -771,9 +797,11 @@
/*forceOverride=*/ false,
request.getVersion(),
mUserId,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- setSchemaFuture.complete(result);
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ setSchemaFuture.complete(resultParcel.getResult());
}
});
AppSearchResult<Bundle> setSchemaResult = setSchemaFuture.get();
@@ -821,10 +849,11 @@
/*forceOverride=*/ true,
request.getVersion(),
mUserId,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchResultCallback.Stub() {
@Override
- public void onResult(AppSearchResult result) {
- setSchema2Future.complete(result);
+ public void onResult(AppSearchResultParcel resultParcel) {
+ setSchema2Future.complete(resultParcel.getResult());
}
});
AppSearchResult<Bundle> setSchema2Result = setSchema2Future.get();
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index d49f472..bb8d565 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -16,11 +16,14 @@
package android.app.appsearch;
-
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -70,15 +73,20 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
try {
- mService.initialize(mUserId, new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- executor.execute(() -> {
- if (result.isSuccess()) {
- callback.accept(
- AppSearchResult.newSuccessfulResult(GlobalSearchSession.this));
- } else {
- callback.accept(result);
- }
+ mService.initialize(mUserId,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
+ new IAppSearchResultCallback.Stub() {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> {
+ AppSearchResult<Void> result = resultParcel.getResult();
+ if (result.isSuccess()) {
+ callback.accept(
+ AppSearchResult.newSuccessfulResult(
+ GlobalSearchSession.this));
+ } else {
+ callback.accept(AppSearchResult.newFailedResult(result));
+ }
});
}
});
@@ -159,8 +167,9 @@
/*systemUsage=*/ true,
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- executor.execute(() -> callback.accept(result));
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> callback.accept(resultParcel.getResult()));
}
});
mIsMutated = true;
@@ -177,7 +186,8 @@
public void close() {
if (mIsMutated && !mIsClosed) {
try {
- mService.persistToDisk(mUserId);
+ mService.persistToDisk(mUserId,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime());
mIsClosed = true;
} catch (RemoteException e) {
Log.e(TAG, "Unable to close the GlobalSearchSession", e);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index 531c984..0106b0f 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -20,8 +20,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -106,14 +110,19 @@
try {
if (mIsFirstLoad) {
mIsFirstLoad = false;
+ long binderCallStartTimeMillis = SystemClock.elapsedRealtime();
if (mDatabaseName == null) {
// Global query, there's no one package-database combination to check.
mService.globalQuery(mPackageName, mQueryExpression,
- mSearchSpec.getBundle(), mUserId, wrapCallback(executor, callback));
+ mSearchSpec.getBundle(), mUserId,
+ binderCallStartTimeMillis,
+ wrapCallback(executor, callback));
} else {
// Normal local query, pass in specified database.
mService.query(mPackageName, mDatabaseName, mQueryExpression,
- mSearchSpec.getBundle(), mUserId, wrapCallback(executor, callback));
+ mSearchSpec.getBundle(), mUserId,
+ binderCallStartTimeMillis,
+ wrapCallback(executor, callback));
}
} else {
mService.getNextPage(mNextPageToken, mUserId, wrapCallback(executor, callback));
@@ -139,18 +148,20 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
return new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- executor.execute(() -> invokeCallback(result, callback));
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> invokeCallback(resultParcel.getResult(), callback));
}
};
}
- private void invokeCallback(AppSearchResult result,
+ private void invokeCallback(
+ @NonNull AppSearchResult<Bundle> searchResultPageResult,
@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
- if (result.isSuccess()) {
+ if (searchResultPageResult.isSuccess()) {
try {
SearchResultPage searchResultPage =
- new SearchResultPage((Bundle) result.getResultValue());
+ new SearchResultPage(searchResultPageResult.getResultValue());
mNextPageToken = searchResultPage.getNextPageToken();
callback.accept(AppSearchResult.newSuccessfulResult(
searchResultPage.getResults()));
@@ -158,7 +169,7 @@
callback.accept(AppSearchResult.throwableToFailedResult(t));
}
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(searchResultPageResult));
}
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.aidl
similarity index 80%
copy from apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
copy to apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.aidl
index 4686de8..89908cd 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2020, The Android Open Source Project
+ * Copyright 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.app.appsearch.aidl;
/** {@hide} */
-parcelable AppSearchBatchResult;
\ No newline at end of file
+parcelable AppSearchBatchResultParcel<ValueType>;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.java b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.java
new file mode 100644
index 0000000..b0cc10c
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.aidl;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchResult;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Parcelable wrapper around {@link AppSearchBatchResult}.
+ *
+ * <p>{@link AppSearchBatchResult} can contain any type of key and value, including non-parcelable
+ * values. For the specific case of sending {@link AppSearchBatchResult} across Binder, this class
+ * wraps an {@link AppSearchBatchResult} that has String keys and Parcelable values. It provides
+ * parcelability of the whole structure.
+ *
+ * @param <ValueType> The type of result object for successful calls. Must be a parcelable type.
+ * @hide
+ */
+public final class AppSearchBatchResultParcel<ValueType> implements Parcelable {
+ private final AppSearchBatchResult<String, ValueType> mResult;
+
+ /** Creates a new {@link AppSearchBatchResultParcel} from the given result. */
+ public AppSearchBatchResultParcel(@NonNull AppSearchBatchResult<String, ValueType> result) {
+ mResult = Objects.requireNonNull(result);
+ }
+
+ private AppSearchBatchResultParcel(@NonNull Parcel in) {
+ Bundle bundle = in.readBundle();
+ AppSearchBatchResult.Builder<String, ValueType> builder =
+ new AppSearchBatchResult.Builder<>();
+ for (String key : bundle.keySet()) {
+ AppSearchResultParcel<ValueType> resultParcel = bundle.getParcelable(key);
+ builder.setResult(key, resultParcel.getResult());
+ }
+ mResult = builder.build();
+ }
+
+ @NonNull
+ public AppSearchBatchResult<String, ValueType> getResult() {
+ return mResult;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ Bundle bundle = new Bundle();
+ for (Map.Entry<String, AppSearchResult<ValueType>> entry
+ : mResult.getAll().entrySet()) {
+ bundle.putParcelable(entry.getKey(), new AppSearchResultParcel<>(entry.getValue()));
+ }
+ dest.writeBundle(bundle);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @NonNull
+ public static final Creator<AppSearchBatchResultParcel<?>> CREATOR =
+ new Creator<AppSearchBatchResultParcel<?>>() {
+ @NonNull
+ @Override
+ public AppSearchBatchResultParcel<?> createFromParcel(@NonNull Parcel in) {
+ return new AppSearchBatchResultParcel<>(in);
+ }
+
+ @NonNull
+ @Override
+ public AppSearchBatchResultParcel<?>[] newArray(int size) {
+ return new AppSearchBatchResultParcel<?>[size];
+ }
+ };
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.aidl
similarity index 81%
rename from apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
rename to apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.aidl
index 4686de8..4e35bd5 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2020, The Android Open Source Project
+ * Copyright 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.app.appsearch.aidl;
/** {@hide} */
-parcelable AppSearchBatchResult;
\ No newline at end of file
+parcelable AppSearchResultParcel<ValueType>;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.java b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.java
new file mode 100644
index 0000000..8b137d3
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.aidl;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Parcelable wrapper around {@link AppSearchResult}.
+ *
+ * <p>{@link AppSearchResult} can contain any value, including non-parcelable values. For the
+ * specific case of sending {@link AppSearchResult} across Binder, this class wraps an
+ * {@link AppSearchResult} that contains a parcelable type and provides parcelability of the whole
+ * structure.
+ *
+ * @param <ValueType> The type of result object for successful calls. Must be a parcelable type.
+ * @hide
+ */
+public final class AppSearchResultParcel<ValueType> implements Parcelable {
+ private final AppSearchResult<ValueType> mResult;
+
+ /** Creates a new {@link AppSearchResultParcel} from the given result. */
+ public AppSearchResultParcel(@NonNull AppSearchResult<ValueType> result) {
+ mResult = Objects.requireNonNull(result);
+ }
+
+ private AppSearchResultParcel(@NonNull Parcel in) {
+ int resultCode = in.readInt();
+ ValueType resultValue = (ValueType) in.readValue(/*loader=*/ null);
+ String errorMessage = in.readString();
+ if (resultCode == AppSearchResult.RESULT_OK) {
+ mResult = AppSearchResult.newSuccessfulResult(resultValue);
+ } else {
+ mResult = AppSearchResult.newFailedResult(resultCode, errorMessage);
+ }
+ }
+
+ @NonNull
+ public AppSearchResult<ValueType> getResult() {
+ return mResult;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mResult.getResultCode());
+ if (mResult.isSuccess()) {
+ dest.writeValue(mResult.getResultValue());
+ } else {
+ dest.writeValue(null);
+ }
+ dest.writeString(mResult.getErrorMessage());
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @NonNull
+ public static final Creator<AppSearchResultParcel<?>> CREATOR =
+ new Creator<AppSearchResultParcel<?>>() {
+ @NonNull
+ @Override
+ public AppSearchResultParcel<?> createFromParcel(@NonNull Parcel in) {
+ return new AppSearchResultParcel<>(in);
+ }
+
+ @NonNull
+ @Override
+ public AppSearchResultParcel<?>[] newArray(int size) {
+ return new AppSearchResultParcel<?>[size];
+ }
+ };
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchBatchResultCallback.aidl
similarity index 70%
rename from apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl
rename to apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchBatchResultCallback.aidl
index 64b331e..1fe19cc 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchBatchResultCallback.aidl
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.app.appsearch.aidl;
-import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.aidl.AppSearchBatchResultParcel;
+import android.app.appsearch.aidl.AppSearchResultParcel;
/** {@hide} */
oneway interface IAppSearchBatchResultCallback {
- void onResult(in AppSearchBatchResult result);
- void onSystemError(in AppSearchResult result);
+ void onResult(in AppSearchBatchResultParcel resultParcel);
+ void onSystemError(in AppSearchResultParcel resultParcel);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
similarity index 91%
rename from apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
rename to apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
index 507bd68..18ebddc 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.app.appsearch.aidl;
import android.os.Bundle;
-import android.app.appsearch.IAppSearchBatchResultCallback;
-import android.app.appsearch.IAppSearchResultCallback;
+import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.os.ParcelFileDescriptor;
/** {@hide} */
@@ -37,6 +37,7 @@
* incompatible documents will be deleted.
* @param schemaVersion The overall schema version number of the request.
* @param userId Id of the calling user
+ * @param binderCallStartTimeMillis start timestamp of binder call in Millis
* @param callback {@link IAppSearchResultCallback#onResult} will be called with an
* {@link AppSearchResult}<{@link Bundle}>, where the value are
* {@link SetSchemaResponse} bundle.
@@ -50,6 +51,7 @@
boolean forceOverride,
in int schemaVersion,
in int userId,
+ in long binderCallStartTimeMillis,
in IAppSearchResultCallback callback);
/**
@@ -115,6 +117,7 @@
* @param typePropertyPaths A map of schema type to a list of property paths to return in the
* result.
* @param userId Id of the calling user
+ * @param binderCallStartTimeMillis start timestamp of binder call in Millis
* @param callback
* If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError}
* will be called with the cause throwable. Otherwise,
@@ -129,6 +132,7 @@
in List<String> ids,
in Map<String, List<String>> typePropertyPaths,
in int userId,
+ in long binderCallStartTimeMillis,
in IAppSearchBatchResultCallback callback);
/**
@@ -139,6 +143,7 @@
* @param queryExpression String to search for
* @param searchSpecBundle SearchSpec bundle
* @param userId Id of the calling user
+ * @param binderCallStartTimeMillis start timestamp of binder call in Millis
* @param callback {@link AppSearchResult}<{@link Bundle}> of performing this
* operation.
*/
@@ -148,6 +153,7 @@
in String queryExpression,
in Bundle searchSpecBundle,
in int userId,
+ in long binderCallStartTimeMillis,
in IAppSearchResultCallback callback);
/**
@@ -158,6 +164,7 @@
* @param queryExpression String to search for
* @param searchSpecBundle SearchSpec bundle
* @param userId Id of the calling user
+ * @param binderCallStartTimeMillis start timestamp of binder call in Millis
* @param callback {@link AppSearchResult}<{@link Bundle}> of performing this
* operation.
*/
@@ -166,6 +173,7 @@
in String queryExpression,
in Bundle searchSpecBundle,
in int userId,
+ in long binderCallStartTimeMillis,
in IAppSearchResultCallback callback);
/**
@@ -200,7 +208,7 @@
* @param searchSpecBundle SearchSpec bundle.
* @param userId Id of the calling user.
* @param callback {@link IAppSearchResultCallback#onResult} will be called with an
- * {@link AppSearchResult}<{@code null}>.
+ * {@link AppSearchResult}<{@code Void}>.
*/
void writeQueryResultsToFile(
in String packageName,
@@ -269,6 +277,7 @@
* @param namespace Namespace of the document to remove.
* @param ids The IDs of the documents to delete
* @param userId Id of the calling user
+ * @param binderCallStartTimeMillis start timestamp of binder call in Millis
* @param callback
* If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError}
* will be called with the cause throwable. Otherwise,
@@ -283,6 +292,7 @@
in String namespace,
in List<String> ids,
in int userId,
+ in long binderCallStartTimeMillis,
in IAppSearchBatchResultCallback callback);
/**
@@ -293,6 +303,7 @@
* @param queryExpression String to search for
* @param searchSpecBundle SearchSpec bundle
* @param userId Id of the calling user
+ * @param binderCallStartTimeMillis start timestamp of binder call in Millis
* @param callback {@link IAppSearchResultCallback#onResult} will be called with an
* {@link AppSearchResult}<{@link Void}>.
*/
@@ -302,6 +313,7 @@
in String queryExpression,
in Bundle searchSpecBundle,
in int userId,
+ in long binderCallStartTimeMillis,
in IAppSearchResultCallback callback);
/**
@@ -324,15 +336,20 @@
* Persists all update/delete requests to the disk.
*
* @param userId Id of the calling user
+ * @param binderCallStartTimeMillis start timestamp of binder call in Millis
*/
- void persistToDisk(in int userId);
+ void persistToDisk(in int userId, in long binderCallStartTimeMillis);
/**
* Creates and initializes AppSearchImpl for the calling app.
*
* @param userId Id of the calling user
+ * @param binderCallStartTimeMillis start timestamp of binder call in Millis
* @param callback {@link IAppSearchResultCallback#onResult} will be called with an
* {@link AppSearchResult}<{@link Void}>.
*/
- void initialize(in int userId, in IAppSearchResultCallback callback);
+ void initialize(
+ in int userId,
+ in long binderCallStartTimeMillis,
+ in IAppSearchResultCallback callback);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchResultCallback.aidl
similarity index 81%
rename from apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl
rename to apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchResultCallback.aidl
index 299c9957..097f0d1 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchResultCallback.aidl
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.app.appsearch.aidl;
-import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.aidl.AppSearchResultParcel;
/** {@hide} */
oneway interface IAppSearchResultCallback {
- void onResult(in AppSearchResult result);
+ void onResult(in AppSearchResultParcel resultParcel);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
similarity index 62%
rename from apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
rename to apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
index 9ae0d62..272e12d 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
@@ -13,17 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package android.app.appsearch;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.ArrayMap;
-import com.android.internal.util.Preconditions;
-
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
@@ -31,19 +26,21 @@
/**
* Provides results for AppSearch batch operations which encompass multiple documents.
*
- * <p>Individual results of a batch operation are separated into two maps: one for successes and
- * one for failures. For successes, {@link #getSuccesses()} will return a map of keys to
- * instances of the value type. For failures, {@link #getFailures()} will return a map of keys to
- * {@link AppSearchResult} objects.
+ * <p>Individual results of a batch operation are separated into two maps: one for successes and one
+ * for failures. For successes, {@link #getSuccesses()} will return a map of keys to instances of
+ * the value type. For failures, {@link #getFailures()} will return a map of keys to {@link
+ * AppSearchResult} objects.
*
* <p>Alternatively, {@link #getAll()} returns a map of keys to {@link AppSearchResult} objects for
* both successes and failures.
*
+ * @param <KeyType> The type of the keys for which the results will be reported.
+ * @param <ValueType> The type of the result objects for successful results.
* @see AppSearchSession#put
* @see AppSearchSession#getByDocumentId
* @see AppSearchSession#remove
*/
-public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
+public final class AppSearchBatchResult<KeyType, ValueType> {
@NonNull private final Map<KeyType, ValueType> mSuccesses;
@NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures;
@NonNull private final Map<KeyType, AppSearchResult<ValueType>> mAll;
@@ -52,30 +49,9 @@
@NonNull Map<KeyType, ValueType> successes,
@NonNull Map<KeyType, AppSearchResult<ValueType>> failures,
@NonNull Map<KeyType, AppSearchResult<ValueType>> all) {
- mSuccesses = successes;
- mFailures = failures;
- mAll = all;
- }
-
- private AppSearchBatchResult(@NonNull Parcel in) {
- mAll = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null));
- Map<KeyType, ValueType> successes = new ArrayMap<>();
- Map<KeyType, AppSearchResult<ValueType>> failures = new ArrayMap<>();
- for (Map.Entry<KeyType, AppSearchResult<ValueType>> entry : mAll.entrySet()) {
- if (entry.getValue().isSuccess()) {
- successes.put(entry.getKey(), entry.getValue().getResultValue());
- } else {
- failures.put(entry.getKey(), entry.getValue());
- }
- }
- mSuccesses = Collections.unmodifiableMap(successes);
- mFailures = Collections.unmodifiableMap(failures);
- }
-
- /** @hide */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeMap(mAll);
+ mSuccesses = Objects.requireNonNull(successes);
+ mFailures = Objects.requireNonNull(failures);
+ mAll = Objects.requireNonNull(all);
}
/** Returns {@code true} if this {@link AppSearchBatchResult} has no failures. */
@@ -95,18 +71,18 @@
*/
@NonNull
public Map<KeyType, ValueType> getSuccesses() {
- return mSuccesses;
+ return Collections.unmodifiableMap(mSuccesses);
}
/**
- * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all
- * failed individual results.
+ * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all failed
+ * individual results.
*
* <p>The values of the {@link Map} will not be {@code null}.
*/
@NonNull
public Map<KeyType, AppSearchResult<ValueType>> getFailures() {
- return mFailures;
+ return Collections.unmodifiableMap(mFailures);
}
/**
@@ -117,11 +93,12 @@
*/
@NonNull
public Map<KeyType, AppSearchResult<ValueType>> getAll() {
- return mAll;
+ return Collections.unmodifiableMap(mAll);
}
/**
* Asserts that this {@link AppSearchBatchResult} has no failures.
+ *
* @hide
*/
public void checkSuccess() {
@@ -136,38 +113,16 @@
return "{\n successes: " + mSuccesses + "\n failures: " + mFailures + "\n}";
}
- /** @hide */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** @hide */
- @NonNull
- public static final Creator<AppSearchBatchResult> CREATOR =
- new Creator<AppSearchBatchResult>() {
- @NonNull
- @Override
- public AppSearchBatchResult createFromParcel(@NonNull Parcel in) {
- return new AppSearchBatchResult(in);
- }
-
- @NonNull
- @Override
- public AppSearchBatchResult[] newArray(int size) {
- return new AppSearchBatchResult[size];
- }
- };
-
/**
* Builder for {@link AppSearchBatchResult} objects.
*
- * <p>Once {@link #build} is called, the instance can no longer be used.
+ * @param <KeyType> The type of the keys for which the results will be reported.
+ * @param <ValueType> The type of the result objects for successful results.
*/
public static final class Builder<KeyType, ValueType> {
- private final Map<KeyType, ValueType> mSuccesses = new ArrayMap<>();
- private final Map<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
- private final Map<KeyType, AppSearchResult<ValueType>> mAll = new ArrayMap<>();
+ private ArrayMap<KeyType, ValueType> mSuccesses = new ArrayMap<>();
+ private ArrayMap<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
+ private ArrayMap<KeyType, AppSearchResult<ValueType>> mAll = new ArrayMap<>();
private boolean mBuilt = false;
/**
@@ -175,15 +130,21 @@
*
* <p>Any previous mapping for a key, whether success or failure, is deleted.
*
- * @throws IllegalStateException if the builder has already been used.
+ * <p>This is a convenience function which is equivalent to {@code setResult(key,
+ * AppSearchResult.newSuccessfulResult(value))}.
+ *
+ * @param key The key to associate the result with; usually corresponds to some identifier
+ * from the input like an ID or name.
+ * @param value An optional value to associate with the successful result of the operation
+ * being performed.
*/
- @SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
+ @SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
@NonNull
public Builder<KeyType, ValueType> setSuccess(
- @NonNull KeyType key, @Nullable ValueType result) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
+ @NonNull KeyType key, @Nullable ValueType value) {
Objects.requireNonNull(key);
- return setResult(key, AppSearchResult.newSuccessfulResult(result));
+ resetIfBuilt();
+ return setResult(key, AppSearchResult.newSuccessfulResult(value));
}
/**
@@ -191,16 +152,23 @@
*
* <p>Any previous mapping for a key, whether success or failure, is deleted.
*
- * @throws IllegalStateException if the builder has already been used.
+ * <p>This is a convenience function which is equivalent to {@code setResult(key,
+ * AppSearchResult.newFailedResult(resultCode, errorMessage))}.
+ *
+ * @param key The key to associate the result with; usually corresponds to some identifier
+ * from the input like an ID or name.
+ * @param resultCode One of the constants documented in {@link
+ * AppSearchResult#getResultCode}.
+ * @param errorMessage An optional string describing the reason or nature of the failure.
*/
- @SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
+ @SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
@NonNull
public Builder<KeyType, ValueType> setFailure(
@NonNull KeyType key,
@AppSearchResult.ResultCode int resultCode,
@Nullable String errorMessage) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(key);
+ resetIfBuilt();
return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage));
}
@@ -209,15 +177,17 @@
*
* <p>Any previous mapping for a key, whether success or failure, is deleted.
*
- * @throws IllegalStateException if the builder has already been used.
+ * @param key The key to associate the result with; usually corresponds to some identifier
+ * from the input like an ID or name.
+ * @param result The result to associate with the key.
*/
- @SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
+ @SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
@NonNull
public Builder<KeyType, ValueType> setResult(
@NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(key);
Objects.requireNonNull(result);
+ resetIfBuilt();
if (result.isSuccess()) {
mSuccesses.put(key, result.getResultValue());
mFailures.remove(key);
@@ -231,14 +201,20 @@
/**
* Builds an {@link AppSearchBatchResult} object from the contents of this {@link Builder}.
- *
- * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public AppSearchBatchResult<KeyType, ValueType> build() {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
mBuilt = true;
return new AppSearchBatchResult<>(mSuccesses, mFailures, mAll);
}
+
+ private void resetIfBuilt() {
+ if (mBuilt) {
+ mSuccesses = new ArrayMap<>(mSuccesses);
+ mFailures = new ArrayMap<>(mFailures);
+ mAll = new ArrayMap<>(mAll);
+ mBuilt = false;
+ }
+ }
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
similarity index 83%
rename from apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
rename to apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
index b06e215..c57cf2e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
@@ -13,15 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package android.app.appsearch;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.appsearch.exceptions.AppSearchException;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -36,24 +33,26 @@
*
* @param <ValueType> The type of result object for successful calls.
*/
-public final class AppSearchResult<ValueType> implements Parcelable {
+public final class AppSearchResult<ValueType> {
private static final String TAG = "AppSearchResult";
/**
* Result codes from {@link AppSearchSession} methods.
+ *
* @hide
*/
- @IntDef(value = {
- RESULT_OK,
- RESULT_UNKNOWN_ERROR,
- RESULT_INTERNAL_ERROR,
- RESULT_INVALID_ARGUMENT,
- RESULT_IO_ERROR,
- RESULT_OUT_OF_SPACE,
- RESULT_NOT_FOUND,
- RESULT_INVALID_SCHEMA,
- RESULT_SECURITY_ERROR,
- })
+ @IntDef(
+ value = {
+ RESULT_OK,
+ RESULT_UNKNOWN_ERROR,
+ RESULT_INTERNAL_ERROR,
+ RESULT_INVALID_ARGUMENT,
+ RESULT_IO_ERROR,
+ RESULT_OUT_OF_SPACE,
+ RESULT_NOT_FOUND,
+ RESULT_INVALID_SCHEMA,
+ RESULT_SECURITY_ERROR,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface ResultCode {}
@@ -109,20 +108,6 @@
mErrorMessage = errorMessage;
}
- private AppSearchResult(@NonNull Parcel in) {
- mResultCode = in.readInt();
- mResultValue = (ValueType) in.readValue(/*loader=*/ null);
- mErrorMessage = in.readString();
- }
-
- /** @hide */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mResultCode);
- dest.writeValue(mResultValue);
- dest.writeString(mErrorMessage);
- }
-
/** Returns {@code true} if {@link #getResultCode} equals {@link AppSearchResult#RESULT_OK}. */
public boolean isSuccess() {
return getResultCode() == RESULT_OK;
@@ -154,8 +139,8 @@
*
* <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error
* message may be {@code null} even if {@link #isSuccess} is {@code false}. See the
- * documentation of the particular {@link AppSearchSession} call producing this
- * {@link AppSearchResult} for what is returned by {@link #getErrorMessage}.
+ * documentation of the particular {@link AppSearchSession} call producing this {@link
+ * AppSearchResult} for what is returned by {@link #getErrorMessage}.
*/
@Nullable
public String getErrorMessage() {
@@ -190,30 +175,11 @@
return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage;
}
- /** @hide */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** @hide */
- @NonNull
- public static final Creator<AppSearchResult> CREATOR = new Creator<AppSearchResult>() {
- @NonNull
- @Override
- public AppSearchResult createFromParcel(@NonNull Parcel in) {
- return new AppSearchResult(in);
- }
-
- @NonNull
- @Override
- public AppSearchResult[] newArray(int size) {
- return new AppSearchResult[size];
- }
- };
-
/**
* Creates a new successful {@link AppSearchResult}.
+ *
+ * @param value An optional value to associate with the successful result of the operation being
+ * performed.
*/
@NonNull
public static <ValueType> AppSearchResult<ValueType> newSuccessfulResult(
@@ -223,6 +189,9 @@
/**
* Creates a new failed {@link AppSearchResult}.
+ *
+ * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+ * @param errorMessage An optional string describing the reason or nature of the failure.
*/
@NonNull
public static <ValueType> AppSearchResult<ValueType> newFailedResult(
@@ -238,7 +207,8 @@
@NonNull
public static <ValueType> AppSearchResult<ValueType> newFailedResult(
@NonNull AppSearchResult<?> otherFailedResult) {
- Preconditions.checkState(!otherFailedResult.isSuccess(),
+ Preconditions.checkState(
+ !otherFailedResult.isSuccess(),
"Cannot convert a success result to a failed result");
return AppSearchResult.newFailedResult(
otherFailedResult.getResultCode(), otherFailedResult.getErrorMessage());
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index c1fcd6c..3619a56 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -182,7 +182,7 @@
@IntDef(
value = {
DATA_TYPE_STRING,
- DATA_TYPE_INT64,
+ DATA_TYPE_LONG,
DATA_TYPE_DOUBLE,
DATA_TYPE_BOOLEAN,
DATA_TYPE_BYTES,
@@ -195,7 +195,7 @@
public static final int DATA_TYPE_STRING = 1;
/** @hide */
- public static final int DATA_TYPE_INT64 = 2;
+ public static final int DATA_TYPE_LONG = 2;
/** @hide */
public static final int DATA_TYPE_DOUBLE = 3;
@@ -315,8 +315,8 @@
switch (propertyBundle.getInt(PropertyConfig.DATA_TYPE_FIELD)) {
case PropertyConfig.DATA_TYPE_STRING:
return new StringPropertyConfig(propertyBundle);
- case PropertyConfig.DATA_TYPE_INT64:
- return new Int64PropertyConfig(propertyBundle);
+ case PropertyConfig.DATA_TYPE_LONG:
+ return new LongPropertyConfig(propertyBundle);
case PropertyConfig.DATA_TYPE_DOUBLE:
return new DoublePropertyConfig(propertyBundle);
case PropertyConfig.DATA_TYPE_BOOLEAN:
@@ -485,8 +485,13 @@
}
}
- /** Configuration for a property containing a 64-bit integer. */
- public static final class Int64PropertyConfig extends PropertyConfig {
+ /**
+ * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage(implicitMember = "")
+ public static class Int64PropertyConfig extends PropertyConfig {
Int64PropertyConfig(@NonNull Bundle bundle) {
super(bundle);
}
@@ -497,6 +502,7 @@
private @Cardinality int mCardinality = CARDINALITY_OPTIONAL;
/** Creates a new {@link Int64PropertyConfig.Builder}. */
+ @UnsupportedAppUsage
public Builder(@NonNull String propertyName) {
mPropertyName = Objects.requireNonNull(propertyName);
}
@@ -509,6 +515,7 @@
*/
@SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
@NonNull
+ @UnsupportedAppUsage
public Int64PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
Preconditions.checkArgumentInRange(
cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
@@ -518,16 +525,61 @@
/** Constructs a new {@link Int64PropertyConfig} from the contents of this builder. */
@NonNull
+ @UnsupportedAppUsage
public Int64PropertyConfig build() {
Bundle bundle = new Bundle();
bundle.putString(NAME_FIELD, mPropertyName);
- bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_INT64);
+ bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_LONG);
bundle.putInt(CARDINALITY_FIELD, mCardinality);
return new Int64PropertyConfig(bundle);
}
}
}
+ /** Configuration for a property containing a 64-bit integer. */
+ // TODO(b/181887768): This should extend directly from PropertyConfig
+ public static final class LongPropertyConfig extends Int64PropertyConfig {
+ LongPropertyConfig(@NonNull Bundle bundle) {
+ super(bundle);
+ }
+
+ /** Builder for {@link LongPropertyConfig}. */
+ public static final class Builder {
+ private final String mPropertyName;
+ private @Cardinality int mCardinality = CARDINALITY_OPTIONAL;
+
+ /** Creates a new {@link LongPropertyConfig.Builder}. */
+ public Builder(@NonNull String propertyName) {
+ mPropertyName = Objects.requireNonNull(propertyName);
+ }
+
+ /**
+ * The cardinality of the property (whether it is optional, required or repeated).
+ *
+ * <p>If this method is not called, the default cardinality is {@link
+ * PropertyConfig#CARDINALITY_OPTIONAL}.
+ */
+ @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+ @NonNull
+ public LongPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+ Preconditions.checkArgumentInRange(
+ cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+ mCardinality = cardinality;
+ return this;
+ }
+
+ /** Constructs a new {@link LongPropertyConfig} from the contents of this builder. */
+ @NonNull
+ public LongPropertyConfig build() {
+ Bundle bundle = new Bundle();
+ bundle.putString(NAME_FIELD, mPropertyName);
+ bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_LONG);
+ bundle.putInt(CARDINALITY_FIELD, mCardinality);
+ return new LongPropertyConfig(bundle);
+ }
+ }
+ }
+
/** Configuration for a property containing a double-precision decimal number. */
public static final class DoublePropertyConfig extends PropertyConfig {
DoublePropertyConfig(@NonNull Bundle bundle) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 736deab..cc48ccb 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -892,129 +892,121 @@
@Override
@NonNull
public String toString() {
- return formatGenericDocumentString(this, /*indentLevel=*/ 0);
+ StringBuilder stringBuilder = new StringBuilder();
+ appendGenericDocumentString(this, /*indentLevel=*/ 0, stringBuilder);
+ return stringBuilder.toString();
}
- @NonNull
- private static String formatGenericDocumentString(
- @NonNull GenericDocument document, int indentLevel) {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append(getIndent(indentLevel)).append("{\n");
+ private static void appendGenericDocumentString(
+ @NonNull GenericDocument document, int indentLevel, @NonNull StringBuilder builder) {
+ Objects.requireNonNull(document);
+ Objects.requireNonNull(builder);
- String indentLevelOneString = getIndent(indentLevel + 1);
+ builder.append(getIndent(indentLevel)).append("{\n");
- stringBuilder
- .append(indentLevelOneString)
+ String indent1 = getIndent(indentLevel + 1);
+
+ builder.append(indent1)
.append("namespace: \"")
.append(document.getNamespace())
.append("\",\n");
- stringBuilder
- .append(indentLevelOneString)
- .append("id: \"")
- .append(document.getId())
- .append("\",\n");
+ builder.append(indent1).append("id: \"").append(document.getId()).append("\",\n");
- stringBuilder
- .append(indentLevelOneString)
- .append("score: " + document.getScore())
- .append(",\n");
+ builder.append(indent1).append("score: ").append(document.getScore()).append(",\n");
- stringBuilder
- .append(indentLevelOneString)
+ builder.append(indent1)
.append("schemaType: \"")
.append(document.getSchemaType())
.append("\",\n");
- stringBuilder
- .append(indentLevelOneString)
- .append("creationTimestampMillis: " + document.getCreationTimestampMillis())
+ builder.append(indent1)
+ .append("creationTimestampMillis: ")
+ .append(document.getCreationTimestampMillis())
.append(",\n");
- stringBuilder
- .append(indentLevelOneString)
- .append("timeToLiveMillis: " + document.getTtlMillis())
+ builder.append(indent1)
+ .append("timeToLiveMillis: ")
+ .append(document.getTtlMillis())
.append(",\n");
- stringBuilder.append(indentLevelOneString).append("properties: {\n");
+ builder.append(indent1).append("properties: {\n");
- int idx = 0;
- for (String propertyName : document.getPropertyNames()) {
- Object property = document.getProperty(propertyName);
- stringBuilder
- .append(getIndent(indentLevel + 2))
+ String[] sortedProperties = document.getPropertyNames().toArray(new String[0]);
+ Arrays.sort(sortedProperties);
+
+ for (int i = 0; i < sortedProperties.length; i++) {
+ Object property = document.getProperty(sortedProperties[i]);
+ builder.append(getIndent(indentLevel + 2))
.append("\"")
- .append(propertyName)
+ .append(sortedProperties[i])
.append("\"")
.append(": ");
- stringBuilder.append(getPropertyString(property, indentLevel + 2));
- if (idx != document.getPropertyNames().size() - 1) {
- stringBuilder.append(",\n");
+ appendPropertyString(property, indentLevel + 2, builder);
+ if (i != sortedProperties.length - 1) {
+ builder.append(",\n");
}
- ++idx;
}
- stringBuilder.append("\n");
- stringBuilder.append(indentLevelOneString).append("}");
+ builder.append("\n");
+ builder.append(indent1).append("}");
- stringBuilder.append("\n");
- stringBuilder.append(getIndent(indentLevel)).append("}");
-
- return stringBuilder.toString();
+ builder.append("\n");
+ builder.append(getIndent(indentLevel)).append("}");
}
/**
- * Creates string for property.
+ * Appends a string for the given property to the given builder.
*
* @param property property object to create string for.
* @param indentLevel base indent level for property.
+ * @param builder the builder to append to.
*/
- @NonNull
- private static String getPropertyString(@NonNull Object property, int indentLevel) {
+ private static void appendPropertyString(
+ @NonNull Object property, int indentLevel, @NonNull StringBuilder builder) {
Objects.requireNonNull(property);
+ Objects.requireNonNull(builder);
- StringBuilder str = new StringBuilder("[");
-
+ builder.append("[");
if (property instanceof GenericDocument[]) {
GenericDocument[] documentValues = (GenericDocument[]) property;
for (int i = 0; i < documentValues.length; ++i) {
- str.append("\n");
- str.append(formatGenericDocumentString(documentValues[i], indentLevel + 1));
+ builder.append("\n");
+ appendGenericDocumentString(documentValues[i], indentLevel + 1, builder);
if (i != documentValues.length - 1) {
- str.append(", ");
+ builder.append(", ");
}
- str.append("\n");
+ builder.append("\n");
}
- str.append(getIndent(indentLevel));
+ builder.append(getIndent(indentLevel));
} else {
int propertyArrLength = Array.getLength(property);
for (int i = 0; i < propertyArrLength; i++) {
Object propertyElement = Array.get(property, i);
if (propertyElement instanceof String) {
- str.append("\"").append(propertyElement).append("\"");
+ builder.append("\"").append(propertyElement).append("\"");
} else if (propertyElement instanceof byte[]) {
- str.append(Arrays.toString((byte[]) propertyElement));
+ builder.append(Arrays.toString((byte[]) propertyElement));
} else {
- str.append(propertyElement);
+ builder.append(propertyElement);
}
if (i != propertyArrLength - 1) {
- str.append(", ");
+ builder.append(", ");
}
}
}
- str.append("]");
- return str.toString();
+ builder.append("]");
}
- /** Creates string for given indent level. */
+ /** Appends a string for given indent level to the given builder. */
@NonNull
private static String getIndent(int indentLevel) {
- StringBuilder indentedString = new StringBuilder();
+ StringBuilder builder = new StringBuilder();
for (int i = 0; i < indentLevel; ++i) {
- indentedString.append(" ");
+ builder.append(" ");
}
- return indentedString.toString();
+ return builder.toString();
}
/**
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java
index e324b9f..558899e 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java
@@ -164,8 +164,7 @@
@NonNull
public GetByDocumentIdRequest build() {
mBuilt = true;
- return new GetByDocumentIdRequest(
- mNamespace, new ArraySet<>(mIds), new ArrayMap<>(mProjectionTypePropertyPaths));
+ return new GetByDocumentIdRequest(mNamespace, mIds, mProjectionTypePropertyPaths);
}
private void resetIfBuilt() {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java
index b6575c2..26bdf03 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java
@@ -93,14 +93,25 @@
private final String mDocumentId;
private Long mUsageTimestampMillis;
- /** Creates a {@link ReportSystemUsageRequest.Builder} instance. */
+ /**
+ * Creates a {@link ReportSystemUsageRequest.Builder} instance.
+ *
+ * @param packageName The package name of the app which owns the document that was used
+ * (e.g. from {@link SearchResult#getPackageName}).
+ * @param databaseName The database in which the document that was used resides (e.g. from
+ * {@link SearchResult#getDatabaseName}).
+ * @param namespace The namespace of the document that was used (e.g. from {@link
+ * GenericDocument#getNamespace}.
+ * @param documentId The ID of document that was used (e.g. from {@link
+ * GenericDocument#getId}.
+ */
public Builder(
@NonNull String packageName,
- @NonNull String database,
+ @NonNull String databaseName,
@NonNull String namespace,
@NonNull String documentId) {
mPackageName = Objects.requireNonNull(packageName);
- mDatabase = Objects.requireNonNull(database);
+ mDatabase = Objects.requireNonNull(databaseName);
mNamespace = Objects.requireNonNull(namespace);
mDocumentId = Objects.requireNonNull(documentId);
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
index 59fa6e0..c388bde 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
@@ -71,7 +71,14 @@
private String mDocumentId;
private Long mUsageTimestampMillis;
- /** Creates a {@link ReportUsageRequest.Builder} instance. */
+ /**
+ * Creates a new {@link ReportUsageRequest.Builder} instance.
+ *
+ * @param namespace The namespace of the document that was used (e.g. from {@link
+ * GenericDocument#getNamespace}.
+ * @param documentId The ID of document that was used (e.g. from {@link
+ * GenericDocument#getId}.
+ */
public Builder(@NonNull String namespace, @NonNull String documentId) {
mNamespace = Objects.requireNonNull(namespace);
mDocumentId = Objects.requireNonNull(documentId);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
index 0d186f2..4beb667 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import com.android.internal.util.Preconditions;
@@ -82,8 +83,12 @@
return mDocument;
}
- /** @deprecated This method exists only for dogfooder transition and must be removed. */
+ /**
+ * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
+ * @hide
+ */
@Deprecated
+ @UnsupportedAppUsage
@NonNull
public List<MatchInfo> getMatches() {
return getMatchInfos();
@@ -191,8 +196,12 @@
return this;
}
- /** @deprecated This method exists only for dogfooder transition and must be removed. */
+ /**
+ * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
+ * @hide
+ */
@Deprecated
+ @UnsupportedAppUsage
@NonNull
public Builder addMatch(@NonNull MatchInfo matchInfo) {
return addMatchInfo(matchInfo);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index 2c75b71..3e5a2ca 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -375,5 +375,19 @@
mBundle.getInt(RESULT_CODE_FIELD),
mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ ""));
}
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "MigrationFailure { schemaType: "
+ + getSchemaType()
+ + ", namespace: "
+ + getNamespace()
+ + ", documentId: "
+ + getDocumentId()
+ + ", appSearchResult: "
+ + getAppSearchResult().toString()
+ + "}";
+ }
}
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java
new file mode 100644
index 0000000..f2cc3b9
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Utilities for logging to logcat.
+ *
+ * @hide
+ */
+public final class LogUtil {
+ /**
+ * The {@link #piiTrace} logs are intended for sensitive data that can't be enabled in
+ * production, so they are build-gated by this constant.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>0: no tracing.
+ * <li>1: fast tracing (statuses/counts only)
+ * <li>2: full tracing (complete messages)
+ * </ul>
+ */
+ private static final int PII_TRACE_LEVEL = 0;
+
+ private final String mTag;
+
+ public LogUtil(@NonNull String tag) {
+ mTag = Objects.requireNonNull(tag);
+ }
+
+ /** Returns whether piiTrace() is enabled (PII_TRACE_LEVEL > 0). */
+ public boolean isPiiTraceEnabled() {
+ return PII_TRACE_LEVEL > 0;
+ }
+
+ /**
+ * If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided
+ * message to logcat.
+ *
+ * <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately.
+ */
+ public void piiTrace(@NonNull String message) {
+ piiTrace(message, /*fastTraceObj=*/ null, /*fullTraceObj=*/ null);
+ }
+
+ /**
+ * If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided
+ * message and object to logcat.
+ *
+ * <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately.
+ *
+ * <p>Otherwise, {@code traceObj} is logged if it is non-null.
+ */
+ public void piiTrace(@NonNull String message, @Nullable Object traceObj) {
+ piiTrace(message, /*fastTraceObj=*/ traceObj, /*fullTraceObj=*/ null);
+ }
+
+ /**
+ * If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided
+ * message and objects to logcat.
+ *
+ * <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately.
+ *
+ * <p>If {@link #PII_TRACE_LEVEL} is 1, {@code fastTraceObj} is logged if it is non-null.
+ *
+ * <p>If {@link #PII_TRACE_LEVEL} is 2, {@code fullTraceObj} is logged if it is non-null, else
+ * {@code fastTraceObj} is logged if it is non-null..
+ */
+ public void piiTrace(
+ @NonNull String message, @Nullable Object fastTraceObj, @Nullable Object fullTraceObj) {
+ if (PII_TRACE_LEVEL == 0) {
+ return;
+ }
+ StringBuilder builder = new StringBuilder("(trace) ").append(message);
+ if (PII_TRACE_LEVEL == 1 && fastTraceObj != null) {
+ builder.append(": ").append(fastTraceObj);
+ } else if (PII_TRACE_LEVEL == 2 && fullTraceObj != null) {
+ builder.append(": ").append(fullTraceObj);
+ } else if (PII_TRACE_LEVEL == 2 && fastTraceObj != null) {
+ builder.append(": ").append(fastTraceObj);
+ }
+ Log.i(mTag, builder.toString());
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index a4188a2..61ac63e 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -29,18 +29,21 @@
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetSchemaResponse;
-import android.app.appsearch.IAppSearchBatchResultCallback;
-import android.app.appsearch.IAppSearchManager;
-import android.app.appsearch.IAppSearchResultCallback;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.SetSchemaResponse;
import android.app.appsearch.StorageInfo;
+import android.app.appsearch.aidl.AppSearchBatchResultParcel;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageStats;
@@ -56,14 +59,15 @@
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.stats.LoggerInstanceManager;
import com.android.server.appsearch.stats.PlatformLogger;
-import com.android.server.usage.StorageStatsManagerInternal;
-import com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter;
+import com.android.server.usage.StorageStatsManagerLocal;
+import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter;
import com.google.android.icing.proto.PersistType;
@@ -120,7 +124,7 @@
mUserManager = mContext.getSystemService(UserManager.class);
mLoggerInstanceManager = LoggerInstanceManager.getInstance();
registerReceivers();
- LocalServices.getService(StorageStatsManagerInternal.class)
+ LocalManagerRegistry.getManager(StorageStatsManagerLocal.class)
.registerStorageStatsAugmenter(new AppSearchStorageStatsAugmenter(), TAG);
}
@@ -213,10 +217,13 @@
}
if (ImplInstanceManager.getAppSearchDir(userId).exists()) {
// Only clear the package's data if AppSearch exists for this user.
- AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext,
+ PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(mContext,
userId);
+ AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext,
+ userId, logger);
//TODO(b/145759910) clear visibility setting for package.
impl.clearPackageData(packageName);
+ logger.removeCachedUidForPackage(packageName);
}
} catch (Throwable t) {
Log.e(TAG, "Unable to remove data for package: " + packageName, t);
@@ -230,6 +237,18 @@
}
}
+ @Override
+ public void onUserStopping(@NonNull TargetUser user) {
+ synchronized (mUnlockedUserIdsLocked) {
+ mUnlockedUserIdsLocked.remove(user.getUserIdentifier());
+ try {
+ mImplInstanceManager.closeAndRemoveAppSearchImplForUser(user.getUserIdentifier());
+ } catch (Throwable t) {
+ Log.e(TAG, "Error handling user stopping.", t);
+ }
+ }
+ }
+
private void verifyUserUnlocked(int callingUserId) {
if (isUserLocked(callingUserId)) {
throw new IllegalStateException("User " + callingUserId + " is locked or not running.");
@@ -259,14 +278,20 @@
boolean forceOverride,
int schemaVersion,
@UserIdInt int userId,
+ @ElapsedRealtimeLong long binderCallStartTimeMillis,
@NonNull IAppSearchResultCallback callback) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(databaseName);
Objects.requireNonNull(schemaBundles);
Objects.requireNonNull(callback);
+ long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
+ @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
+ PlatformLogger logger = null;
+ int operationSuccessCount = 0;
+ int operationFailureCount = 0;
try {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
@@ -287,6 +312,7 @@
schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
}
AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId);
+ logger = mLoggerInstanceManager.getPlatformLogger(callingUserId);
SetSchemaResponse setSchemaResponse = impl.setSchema(
packageName,
databaseName,
@@ -295,10 +321,33 @@
schemasPackageAccessible,
forceOverride,
schemaVersion);
+ ++operationSuccessCount;
invokeCallbackOnResult(callback,
AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
} catch (Throwable t) {
+ ++operationFailureCount;
+ statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
+ } finally {
+ if (logger != null) {
+ int estimatedBinderLatencyMillis =
+ 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
+ int totalLatencyMillis =
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
+ CallStats.Builder cBuilder = new CallStats.Builder(packageName,
+ databaseName)
+ .setCallType(CallStats.CALL_TYPE_SET_SCHEMA)
+ // TODO(b/173532925) check the existing binder call latency chart
+ // is good enough for us:
+ // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
+ .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
+ .setNumOperationsSucceeded(operationSuccessCount)
+ .setNumOperationsFailed(operationFailureCount);
+ cBuilder.getGeneralStatsBuilder()
+ .setStatusCode(statusCode)
+ .setTotalLatencyMillis(totalLatencyMillis);
+ logger.logStats(cBuilder.build());
+ }
}
});
}
@@ -368,10 +417,10 @@
Objects.requireNonNull(databaseName);
Objects.requireNonNull(documentBundles);
Objects.requireNonNull(callback);
+ long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
- long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
PlatformLogger logger = null;
int operationSuccessCount = 0;
@@ -395,7 +444,8 @@
throwableToFailedResult(t));
AppSearchResult<Void> result = throwableToFailedResult(t);
resultBuilder.setResult(document.getId(), result);
- // for failures, we would just log the one for last failure
+ // Since we can only include one status code in the atom,
+ // for failures, we would just save the one for the last failure
statusCode = result.getResultCode();
++operationFailureCount;
}
@@ -404,25 +454,27 @@
impl.persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, resultBuilder.build());
} catch (Throwable t) {
+ ++operationFailureCount;
+ statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
} finally {
if (logger != null) {
+ int estimatedBinderLatencyMillis =
+ 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
+ int totalLatencyMillis =
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
CallStats.Builder cBuilder = new CallStats.Builder(packageName,
databaseName)
.setCallType(CallStats.CALL_TYPE_PUT_DOCUMENTS)
// TODO(b/173532925) check the existing binder call latency chart
// is good enough for us:
// http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
- .setEstimatedBinderLatencyMillis(
- 2 * (int) (totalLatencyStartTimeMillis
- - binderCallStartTimeMillis))
+ .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
.setNumOperationsSucceeded(operationSuccessCount)
.setNumOperationsFailed(operationFailureCount);
cBuilder.getGeneralStatsBuilder()
.setStatusCode(statusCode)
- .setTotalLatencyMillis(
- (int) (SystemClock.elapsedRealtime()
- - totalLatencyStartTimeMillis));
+ .setTotalLatencyMillis(totalLatencyMillis);
logger.logStats(cBuilder.build());
}
}
@@ -437,15 +489,21 @@
@NonNull List<String> ids,
@NonNull Map<String, List<String>> typePropertyPaths,
@UserIdInt int userId,
+ @ElapsedRealtimeLong long binderCallStartTimeMillis,
@NonNull IAppSearchBatchResultCallback callback) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(databaseName);
Objects.requireNonNull(namespace);
Objects.requireNonNull(ids);
Objects.requireNonNull(callback);
+ long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
+ @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
+ PlatformLogger logger = null;
+ int operationSuccessCount = 0;
+ int operationFailureCount = 0;
try {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
@@ -453,6 +511,7 @@
new AppSearchBatchResult.Builder<>();
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(callingUserId);
+ logger = mLoggerInstanceManager.getPlatformLogger(callingUserId);
for (int i = 0; i < ids.size(); i++) {
String id = ids.get(i);
try {
@@ -463,14 +522,42 @@
namespace,
id,
typePropertyPaths);
+ ++operationSuccessCount;
resultBuilder.setSuccess(id, document.getBundle());
} catch (Throwable t) {
- resultBuilder.setResult(id, throwableToFailedResult(t));
+ // Since we can only include one status code in the atom,
+ // for failures, we would just save the one for the last failure
+ AppSearchResult<Bundle> result = throwableToFailedResult(t);
+ resultBuilder.setResult(id, result);
+ statusCode = result.getResultCode();
+ ++operationFailureCount;
}
}
invokeCallbackOnResult(callback, resultBuilder.build());
} catch (Throwable t) {
+ ++operationFailureCount;
+ statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
+ } finally {
+ if (logger != null) {
+ int estimatedBinderLatencyMillis =
+ 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
+ int totalLatencyMillis =
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
+ CallStats.Builder cBuilder = new CallStats.Builder(packageName,
+ databaseName)
+ .setCallType(CallStats.CALL_TYPE_GET_DOCUMENTS)
+ // TODO(b/173532925) check the existing binder call latency chart
+ // is good enough for us:
+ // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
+ .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
+ .setNumOperationsSucceeded(operationSuccessCount)
+ .setNumOperationsFailed(operationFailureCount);
+ cBuilder.getGeneralStatsBuilder()
+ .setStatusCode(statusCode)
+ .setTotalLatencyMillis(totalLatencyMillis);
+ logger.logStats(cBuilder.build());
+ }
}
});
}
@@ -482,32 +569,62 @@
@NonNull String queryExpression,
@NonNull Bundle searchSpecBundle,
@UserIdInt int userId,
+ @ElapsedRealtimeLong long binderCallStartTimeMillis,
@NonNull IAppSearchResultCallback callback) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(databaseName);
Objects.requireNonNull(queryExpression);
Objects.requireNonNull(searchSpecBundle);
Objects.requireNonNull(callback);
+ long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
+ @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
+ PlatformLogger logger = null;
+ int operationSuccessCount = 0;
+ int operationFailureCount = 0;
try {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(callingUserId);
+ logger = mLoggerInstanceManager.getPlatformLogger(callingUserId);
SearchResultPage searchResultPage =
impl.query(
packageName,
databaseName,
queryExpression,
new SearchSpec(searchSpecBundle),
- /*logger=*/ null);
+ logger);
+ ++operationSuccessCount;
invokeCallbackOnResult(
callback,
AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
} catch (Throwable t) {
+ ++operationFailureCount;
+ statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
+ } finally {
+ if (logger != null) {
+ int estimatedBinderLatencyMillis =
+ 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
+ int totalLatencyMillis =
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
+ CallStats.Builder cBuilder = new CallStats.Builder(packageName,
+ databaseName)
+ .setCallType(CallStats.CALL_TYPE_SEARCH)
+ // TODO(b/173532925) check the existing binder call latency chart
+ // is good enough for us:
+ // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
+ .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
+ .setNumOperationsSucceeded(operationSuccessCount)
+ .setNumOperationsFailed(operationFailureCount);
+ cBuilder.getGeneralStatsBuilder()
+ .setStatusCode(statusCode)
+ .setTotalLatencyMillis(totalLatencyMillis);
+ logger.logStats(cBuilder.build());
+ }
}
});
}
@@ -518,17 +635,24 @@
@NonNull String queryExpression,
@NonNull Bundle searchSpecBundle,
@UserIdInt int userId,
+ @ElapsedRealtimeLong long binderCallStartTimeMillis,
@NonNull IAppSearchResultCallback callback) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(queryExpression);
Objects.requireNonNull(searchSpecBundle);
Objects.requireNonNull(callback);
+ long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
+ @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
+ PlatformLogger logger = null;
+ int operationSuccessCount = 0;
+ int operationFailureCount = 0;
try {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
+ logger = mLoggerInstanceManager.getPlatformLogger(callingUserId);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(callingUserId);
SearchResultPage searchResultPage =
@@ -537,12 +661,36 @@
new SearchSpec(searchSpecBundle),
packageName,
callingUid,
- /*logger=*/ null);
+ logger);
+ ++operationSuccessCount;
invokeCallbackOnResult(
callback,
AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
- } catch (Throwable t) {
+ } catch (Throwable t) {
+ ++operationFailureCount;
+ statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
+ } finally {
+ if (logger != null) {
+ int estimatedBinderLatencyMillis =
+ 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
+ int totalLatencyMillis =
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
+ // TODO(b/173532925) database would be nulluable once we remove generalStats
+ CallStats.Builder cBuilder = new CallStats.Builder(packageName,
+ /*database=*/ "")
+ .setCallType(CallStats.CALL_TYPE_GLOBAL_SEARCH)
+ // TODO(b/173532925) check the existing binder call latency chart
+ // is good enough for us:
+ // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
+ .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
+ .setNumOperationsSucceeded(operationSuccessCount)
+ .setNumOperationsFailed(operationFailureCount);
+ cBuilder.getGeneralStatsBuilder()
+ .setStatusCode(statusCode)
+ .setTotalLatencyMillis(totalLatencyMillis);
+ logger.logStats(cBuilder.build());
+ }
}
});
}
@@ -723,14 +871,20 @@
@NonNull String namespace,
@NonNull List<String> ids,
@UserIdInt int userId,
+ @ElapsedRealtimeLong long binderCallStartTimeMillis,
@NonNull IAppSearchBatchResultCallback callback) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(databaseName);
Objects.requireNonNull(ids);
Objects.requireNonNull(callback);
+ long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
+ @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
+ PlatformLogger logger = null;
+ int operationSuccessCount = 0;
+ int operationFailureCount = 0;
try {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
@@ -738,20 +892,49 @@
new AppSearchBatchResult.Builder<>();
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(callingUserId);
+ logger = mLoggerInstanceManager.getPlatformLogger(callingUserId);
for (int i = 0; i < ids.size(); i++) {
String id = ids.get(i);
try {
impl.remove(packageName, databaseName, namespace, id);
+ ++operationSuccessCount;
resultBuilder.setSuccess(id, /*result= */ null);
} catch (Throwable t) {
- resultBuilder.setResult(id, throwableToFailedResult(t));
+ AppSearchResult<Void> result = throwableToFailedResult(t);
+ resultBuilder.setResult(id, result);
+ // Since we can only include one status code in the atom,
+ // for failures, we would just save the one for the last failure
+ statusCode = result.getResultCode();
+ ++operationFailureCount;
}
}
// Now that the batch has been written. Persist the newly written data.
impl.persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, resultBuilder.build());
} catch (Throwable t) {
+ ++operationFailureCount;
+ statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
+ } finally {
+ if (logger != null) {
+ int estimatedBinderLatencyMillis =
+ 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
+ int totalLatencyMillis =
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
+ CallStats.Builder cBuilder = new CallStats.Builder(packageName,
+ databaseName)
+ .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID)
+ // TODO(b/173532925) check the existing binder call latency chart
+ // is good enough for us:
+ // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
+ .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
+ .setNumOperationsSucceeded(operationSuccessCount)
+ .setNumOperationsFailed(operationFailureCount);
+ cBuilder.getGeneralStatsBuilder()
+ .setStatusCode(statusCode)
+ .setTotalLatencyMillis(totalLatencyMillis);
+ logger.logStats(cBuilder.build());
+ }
}
});
}
@@ -763,20 +946,28 @@
@NonNull String queryExpression,
@NonNull Bundle searchSpecBundle,
@UserIdInt int userId,
+ @ElapsedRealtimeLong long binderCallStartTimeMillis,
@NonNull IAppSearchResultCallback callback) {
+ // TODO(b/173532925) log CallStats once we have CALL_TYPE_REMOVE_BY_QUERY added
Objects.requireNonNull(packageName);
Objects.requireNonNull(databaseName);
Objects.requireNonNull(queryExpression);
Objects.requireNonNull(searchSpecBundle);
Objects.requireNonNull(callback);
+ long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
+ @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
+ PlatformLogger logger = null;
+ int operationSuccessCount = 0;
+ int operationFailureCount = 0;
try {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(callingUserId);
+ logger = mLoggerInstanceManager.getPlatformLogger(callingUserId);
impl.removeByQuery(
packageName,
databaseName,
@@ -784,9 +975,32 @@
new SearchSpec(searchSpecBundle));
// Now that the batch has been written. Persist the newly written data.
impl.persistToDisk(PersistType.Code.LITE);
+ ++operationSuccessCount;
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) {
+ ++operationFailureCount;
+ statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
+ } finally {
+ if (logger != null) {
+ int estimatedBinderLatencyMillis =
+ 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
+ int totalLatencyMillis =
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
+ CallStats.Builder cBuilder = new CallStats.Builder(packageName,
+ databaseName)
+ .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH)
+ // TODO(b/173532925) check the existing binder call latency chart
+ // is good enough for us:
+ // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
+ .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
+ .setNumOperationsSucceeded(operationSuccessCount)
+ .setNumOperationsFailed(operationFailureCount);
+ cBuilder.getGeneralStatsBuilder()
+ .setStatusCode(statusCode)
+ .setTotalLatencyMillis(totalLatencyMillis);
+ logger.logStats(cBuilder.build());
+ }
}
});
}
@@ -820,34 +1034,97 @@
}
@Override
- public void persistToDisk(@UserIdInt int userId) {
+ public void persistToDisk(@UserIdInt int userId,
+ @ElapsedRealtimeLong long binderCallStartTimeMillis) {
+ long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
+ @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
+ PlatformLogger logger = null;
+ int operationSuccessCount = 0;
+ int operationFailureCount = 0;
try {
verifyUserUnlocked(callingUserId);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(callingUserId);
+ logger = mLoggerInstanceManager.getPlatformLogger(callingUserId);
impl.persistToDisk(PersistType.Code.FULL);
+ ++operationSuccessCount;
} catch (Throwable t) {
+ ++operationFailureCount;
+ statusCode = throwableToFailedResult(t).getResultCode();
Log.e(TAG, "Unable to persist the data to disk", t);
+ } finally {
+ if (logger != null) {
+ int estimatedBinderLatencyMillis =
+ 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
+ int totalLatencyMillis =
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
+ CallStats.Builder cBuilder = new CallStats.Builder(/*packageName=*/ "",
+ /*databaseName=*/ "")
+ .setCallType(CallStats.CALL_TYPE_FLUSH)
+ // TODO(b/173532925) check the existing binder call latency chart
+ // is good enough for us:
+ // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
+ .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
+ .setNumOperationsSucceeded(operationSuccessCount)
+ .setNumOperationsFailed(operationFailureCount);
+ cBuilder.getGeneralStatsBuilder()
+ .setStatusCode(statusCode)
+ .setTotalLatencyMillis(totalLatencyMillis);
+ logger.logStats(cBuilder.build());
+ }
}
});
}
@Override
- public void initialize(@UserIdInt int userId, @NonNull IAppSearchResultCallback callback) {
+ public void initialize(@UserIdInt int userId,
+ @ElapsedRealtimeLong long binderCallStartTimeMillis,
+ @NonNull IAppSearchResultCallback callback) {
Objects.requireNonNull(callback);
+ long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
+ @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
+ PlatformLogger logger = null;
+ int operationSuccessCount = 0;
+ int operationFailureCount = 0;
try {
verifyUserUnlocked(callingUserId);
- mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUserId);
- mLoggerInstanceManager.getOrCreatePlatformLogger(getContext(), callingUserId);
+ logger = mLoggerInstanceManager.getOrCreatePlatformLogger(mContext,
+ callingUserId);
+ mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUserId, logger);
+ ++operationSuccessCount;
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) {
+ ++operationFailureCount;
+ statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
+ } finally {
+ if (logger != null) {
+ int estimatedBinderLatencyMillis =
+ 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
+ int totalLatencyMillis =
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
+ // TODO(b/173532925) make packageName and database nullable after
+ // removing generalStats
+ CallStats.Builder cBuilder = new CallStats.Builder(/*packageName=*/"",
+ /*database=*/ "")
+ .setCallType(CallStats.CALL_TYPE_INITIALIZE)
+ // TODO(b/173532925) check the existing binder call latency chart
+ // is good enough for us:
+ // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
+ .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
+ .setNumOperationsSucceeded(operationSuccessCount)
+ .setNumOperationsFailed(operationFailureCount);
+ cBuilder.getGeneralStatsBuilder()
+ .setStatusCode(statusCode)
+ .setTotalLatencyMillis(totalLatencyMillis);
+ logger.logStats(cBuilder.build());
+ }
}
});
}
@@ -869,7 +1146,7 @@
private void invokeCallbackOnResult(
IAppSearchResultCallback callback, AppSearchResult<?> result) {
try {
- callback.onResult(result);
+ callback.onResult(new AppSearchResultParcel<>(result));
} catch (RemoteException e) {
Log.e(TAG, "Unable to send result to the callback", e);
}
@@ -877,9 +1154,9 @@
/** Invokes the {@link IAppSearchBatchResultCallback} with the result. */
private void invokeCallbackOnResult(
- IAppSearchBatchResultCallback callback, AppSearchBatchResult<?, ?> result) {
+ IAppSearchBatchResultCallback callback, AppSearchBatchResult<String, ?> result) {
try {
- callback.onResult(result);
+ callback.onResult(new AppSearchBatchResultParcel<>(result));
} catch (RemoteException e) {
Log.e(TAG, "Unable to send result to the callback", e);
}
@@ -891,8 +1168,9 @@
* <p>The throwable is convert to a {@link AppSearchResult};
*/
private void invokeCallbackOnError(IAppSearchResultCallback callback, Throwable throwable) {
+ AppSearchResult<?> result = throwableToFailedResult(throwable);
try {
- callback.onResult(throwableToFailedResult(throwable));
+ callback.onResult(new AppSearchResultParcel<>(result));
} catch (RemoteException e) {
Log.e(TAG, "Unable to send result to the callback", e);
}
@@ -905,8 +1183,9 @@
*/
private void invokeCallbackOnError(
@NonNull IAppSearchBatchResultCallback callback, @NonNull Throwable throwable) {
+ AppSearchResult<?> result = throwableToFailedResult(throwable);
try {
- callback.onSystemError(throwableToFailedResult(throwable));
+ callback.onSystemError(new AppSearchResultParcel<>(result));
} catch (RemoteException e) {
Log.e(TAG, "Unable to send error to the callback", e);
}
@@ -931,17 +1210,21 @@
// TODO(b/179160886): Cache the previous storage stats.
private class AppSearchStorageStatsAugmenter implements StorageStatsAugmenter {
@Override
- public void augmentStatsForPackage(
+ public void augmentStatsForPackageForUser(
@NonNull PackageStats stats,
@NonNull String packageName,
- @UserIdInt int userId,
- boolean callerHasStatsPermission) {
+ @NonNull UserHandle userHandle,
+ boolean canCallerAccessAllStats) {
Objects.requireNonNull(stats);
Objects.requireNonNull(packageName);
+ Objects.requireNonNull(userHandle);
+ int userId = userHandle.getIdentifier();
try {
verifyUserUnlocked(userId);
- AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext,
+ PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(mContext,
userId);
+ AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext,
+ userId, logger);
stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes();
} catch (Throwable t) {
Log.e(
@@ -956,7 +1239,7 @@
@Override
public void augmentStatsForUid(
- @NonNull PackageStats stats, int uid, boolean callerHasStatsPermission) {
+ @NonNull PackageStats stats, int uid, boolean canCallerAccessAllStats) {
Objects.requireNonNull(stats);
int userId = UserHandle.getUserId(uid);
try {
@@ -965,14 +1248,46 @@
if (packagesForUid == null) {
return;
}
- AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext,
+ PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(mContext,
userId);
- for (String packageName : packagesForUid) {
- stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes();
+ AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext,
+ userId, logger);
+ for (int i = 0; i < packagesForUid.length; i++) {
+ stats.dataSize +=
+ impl.getStorageInfoForPackage(packagesForUid[i]).getSizeBytes();
}
} catch (Throwable t) {
Log.e(TAG, "Unable to augment storage stats for uid " + uid, t);
}
}
+
+ @Override
+ public void augmentStatsForUser(
+ @NonNull PackageStats stats, @NonNull UserHandle userHandle) {
+ // TODO(b/179160886): this implementation could incur many jni calls and a lot of
+ // in-memory processing from getStorageInfoForPackage. Instead, we can just compute the
+ // size of the icing dir (or use the overall StorageInfo without interpolating it).
+ Objects.requireNonNull(stats);
+ Objects.requireNonNull(userHandle);
+ int userId = userHandle.getIdentifier();
+ try {
+ verifyUserUnlocked(userId);
+ List<PackageInfo> packagesForUser =
+ mPackageManager.getInstalledPackagesAsUser(/*flags=*/0, userId);
+ if (packagesForUser == null) {
+ return;
+ }
+ PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(mContext,
+ userId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getOrCreateAppSearchImpl(mContext, userId, logger);
+ for (int i = 0; i < packagesForUser.size(); i++) {
+ String packageName = packagesForUser.get(i).packageName;
+ stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes();
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "Unable to augment storage stats for user " + userId, t);
+ }
+ }
}
}
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 94ee830..f8bc473 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
@@ -30,6 +31,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
+import com.android.server.appsearch.external.localstorage.AppSearchLogger;
import java.io.File;
@@ -88,16 +90,17 @@
* one will be created.
*
* @param context The context
- * @param userId The multi-user userId of the device user calling AppSearch
+ * @param userId The multi-user userId of the device user calling AppSearch
* @return An initialized {@link AppSearchImpl} for this user
*/
@NonNull
public AppSearchImpl getOrCreateAppSearchImpl(
- @NonNull Context context, @UserIdInt int userId) throws AppSearchException {
+ @NonNull Context context, @UserIdInt int userId, @Nullable AppSearchLogger logger)
+ throws AppSearchException {
synchronized (mInstancesLocked) {
AppSearchImpl instance = mInstancesLocked.get(userId);
if (instance == null) {
- instance = createImpl(context, userId);
+ instance = createImpl(context, userId, logger);
mInstancesLocked.put(userId, instance);
}
return instance;
@@ -117,11 +120,29 @@
*/
public void removeAppSearchImplForUser(@UserIdInt int userId) {
synchronized (mInstancesLocked) {
+ // no need to close and persist data to disk since we are removing them now.
mInstancesLocked.remove(userId);
}
}
/**
+ * Close and remove an instance of {@link AppSearchImpl} for the given user.
+ *
+ * <p>All mutation apply to this {@link AppSearchImpl} will be persisted to disk.
+ *
+ * @param userId The multi-user userId of the user that need to be removed.
+ */
+ public void closeAndRemoveAppSearchImplForUser(@UserIdInt int userId) {
+ synchronized (mInstancesLocked) {
+ AppSearchImpl appSearchImpl = mInstancesLocked.get(userId);
+ if (appSearchImpl != null) {
+ appSearchImpl.close();
+ mInstancesLocked.remove(userId);
+ }
+ }
+ }
+
+ /**
* Gets an instance of AppSearchImpl for the given user.
*
* <p>This method should only be called by an initialized SearchSession, which has been already
@@ -146,11 +167,12 @@
}
}
- private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
+ private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId,
+ @Nullable AppSearchLogger logger)
throws AppSearchException {
File appSearchDir = getAppSearchDir(userId);
return AppSearchImpl.create(
- appSearchDir, context, userId, mGlobalQuerierPackage, /*logger=*/ null);
+ appSearchDir, context, userId, mGlobalQuerierPackage, logger);
}
/**
@@ -164,10 +186,10 @@
context.getString(R.string.config_globalAppSearchDataQuerierPackage);
try {
if (context.getPackageManager()
- .getPackageInfoAsUser(
- globalAppSearchDataQuerierPackage,
- MATCH_FACTORY_ONLY,
- UserHandle.USER_SYSTEM)
+ .getPackageInfoAsUser(
+ globalAppSearchDataQuerierPackage,
+ MATCH_FACTORY_ONLY,
+ UserHandle.USER_SYSTEM)
== null) {
return "";
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
index 0f643c5..b7e2159 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
@@ -36,6 +36,10 @@
import android.util.Log;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
+import com.android.server.appsearch.visibilitystore.NotPlatformSurfaceableMap;
+import com.android.server.appsearch.visibilitystore.PackageAccessibleDocument;
+import com.android.server.appsearch.visibilitystore.PackageAccessibleMap;
+import com.android.server.appsearch.visibilitystore.VisibilityDocument;
import com.google.android.icing.proto.PersistType;
@@ -74,74 +78,10 @@
/** No-op user id that won't have any visibility settings. */
public static final int NO_OP_USER_ID = -1;
- /** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */
- private static final String VISIBILITY_TYPE = "VisibilityType";
-
/** Version for the visibility schema */
private static final int SCHEMA_VERSION = 0;
/**
- * Property that holds the list of platform-hidden schemas, as part of the visibility settings.
- */
- private static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
-
- /** Property that holds nested documents of package accessible schemas. */
- private static final String PACKAGE_ACCESSIBLE_PROPERTY = "packageAccessible";
-
- /** Schema type for nested documents that hold package accessible information. */
- private static final String PACKAGE_ACCESSIBLE_TYPE = "PackageAccessibleType";
-
- /** Property that holds the package name that can access a schema. */
- private static final String PACKAGE_NAME_PROPERTY = "packageName";
-
- /** Property that holds the SHA 256 certificate of the app that can access a schema. */
- private static final String SHA_256_CERT_PROPERTY = "sha256Cert";
-
- /** Property that holds the prefixed schema type that is accessible by some package. */
- private static final String ACCESSIBLE_SCHEMA_PROPERTY = "accessibleSchema";
-
- /** Schema for the VisibilityStore's documents. */
- private static final AppSearchSchema VISIBILITY_SCHEMA =
- new AppSearchSchema.Builder(VISIBILITY_TYPE)
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder(
- NOT_PLATFORM_SURFACEABLE_PROPERTY)
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .build())
- .addProperty(
- new AppSearchSchema.DocumentPropertyConfig.Builder(
- PACKAGE_ACCESSIBLE_PROPERTY, PACKAGE_ACCESSIBLE_TYPE)
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .build())
- .build();
-
- /**
- * Schema for package accessible documents, these will be nested in a top-level visibility
- * document.
- */
- private static final AppSearchSchema PACKAGE_ACCESSIBLE_SCHEMA =
- new AppSearchSchema.Builder(PACKAGE_ACCESSIBLE_TYPE)
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY)
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .build())
- .addProperty(
- new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY)
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .build())
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder(
- ACCESSIBLE_SCHEMA_PROPERTY)
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .build())
- .build();
-
- /**
* These cannot have any of the special characters used by AppSearchImpl (e.g. {@code
* AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}.
*/
@@ -177,24 +117,12 @@
// platform-surfaceable content.
private int mGlobalQuerierUid;
- /**
- * Maps prefixes to the set of schemas that are platform-hidden within that prefix. All schemas
- * in the map are prefixed.
- *
- * <p>Although the prefix key isn't used for lookup, it's helpful in ensuring that all previous
- * visibility settings for a prefix are completely overridden by new visibility settings.
- */
- private final Map<String, Set<String>> mNotPlatformSurfaceableMap = new ArrayMap<>();
+ /** Stores the schemas that are platform-hidden. All values are prefixed. */
+ private final NotPlatformSurfaceableMap mNotPlatformSurfaceableMap =
+ new NotPlatformSurfaceableMap();
- /**
- * Maps prefixes to a an internal map. The internal map maps prefixed schemas to the set of
- * PackageIdentifiers that have access to that schema.
- *
- * <p>Although the prefix key isn't used for lookup, it's helpful in ensuring that all previous
- * visibility settings for a prefix are completely overridden by new visibility settings.
- */
- private final Map<String, Map<String, Set<PackageIdentifier>>> mPackageAccessibleMap =
- new ArrayMap<>();
+ /** Stores the schemas that are package accessible. All values are prefixed. */
+ private final PackageAccessibleMap mPackageAccessibleMap = new PackageAccessibleMap();
/**
* Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()}
@@ -228,9 +156,9 @@
boolean hasVisibilityType = false;
boolean hasPackageAccessibleType = false;
for (AppSearchSchema schema : getSchemaResponse.getSchemas()) {
- if (schema.getSchemaType().equals(VISIBILITY_TYPE)) {
+ if (schema.getSchemaType().equals(VisibilityDocument.SCHEMA_TYPE)) {
hasVisibilityType = true;
- } else if (schema.getSchemaType().equals(PACKAGE_ACCESSIBLE_TYPE)) {
+ } else if (schema.getSchemaType().equals(PackageAccessibleDocument.SCHEMA_TYPE)) {
hasPackageAccessibleType = true;
}
@@ -244,7 +172,7 @@
mAppSearchImpl.setSchema(
PACKAGE_NAME,
DATABASE_NAME,
- Arrays.asList(VISIBILITY_SCHEMA, PACKAGE_ACCESSIBLE_SCHEMA),
+ Arrays.asList(VisibilityDocument.SCHEMA, PackageAccessibleDocument.SCHEMA),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
@@ -261,40 +189,34 @@
try {
// Note: We use the other clients' prefixed names as ids
- GenericDocument document =
+ VisibilityDocument visibilityDocument = new VisibilityDocument(
mAppSearchImpl.getDocument(
PACKAGE_NAME,
DATABASE_NAME,
NAMESPACE,
/*id=*/ addIdPrefix(prefix),
- /*typePropertyPaths=*/ Collections.emptyMap());
+ /*typePropertyPaths=*/ Collections.emptyMap()));
// Update platform visibility settings
String[] notPlatformSurfaceableSchemas =
- document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY);
+ visibilityDocument.getNotPlatformSurfaceableSchemas();
if (notPlatformSurfaceableSchemas != null) {
- mNotPlatformSurfaceableMap.put(
- prefix, new ArraySet<>(Arrays.asList(notPlatformSurfaceableSchemas)));
+ mNotPlatformSurfaceableMap.setNotPlatformSurfaceable(
+ prefix,
+ new ArraySet<>(notPlatformSurfaceableSchemas));
}
// Update 3p package visibility settings
Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
GenericDocument[] packageAccessibleDocuments =
- document.getPropertyDocumentArray(PACKAGE_ACCESSIBLE_PROPERTY);
+ visibilityDocument.getPackageAccessibleSchemas();
if (packageAccessibleDocuments != null) {
for (int i = 0; i < packageAccessibleDocuments.length; i++) {
- String packageName =
- packageAccessibleDocuments[i].getPropertyString(
- PACKAGE_NAME_PROPERTY);
- byte[] sha256Cert =
- packageAccessibleDocuments[i].getPropertyBytes(
- SHA_256_CERT_PROPERTY);
+ PackageAccessibleDocument packageAccessibleDocument =
+ new PackageAccessibleDocument(packageAccessibleDocuments[i]);
PackageIdentifier packageIdentifier =
- new PackageIdentifier(packageName, sha256Cert);
-
- String prefixedSchema =
- packageAccessibleDocuments[i].getPropertyString(
- ACCESSIBLE_SCHEMA_PROPERTY);
+ packageAccessibleDocument.getPackageIdentifier();
+ String prefixedSchema = packageAccessibleDocument.getAccessibleSchemaType();
Set<PackageIdentifier> packageIdentifiers =
schemaToPackageIdentifierMap.get(prefixedSchema);
if (packageIdentifiers == null) {
@@ -304,7 +226,7 @@
schemaToPackageIdentifierMap.put(prefixedSchema, packageIdentifiers);
}
}
- mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap);
+ mPackageAccessibleMap.setPackageAccessible(prefix, schemaToPackageIdentifierMap);
} catch (AppSearchException e) {
if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
// TODO(b/172068212): This indicates some desync error. We were expecting a
@@ -339,38 +261,30 @@
Objects.requireNonNull(schemasPackageAccessible);
// Persist the document
- GenericDocument.Builder<?> visibilityDocument =
- new GenericDocument.Builder<>(
- NAMESPACE, /*id=*/ addIdPrefix(prefix), VISIBILITY_TYPE);
+ VisibilityDocument.Builder visibilityDocument =
+ new VisibilityDocument.Builder(NAMESPACE, /*id=*/ addIdPrefix(prefix));
if (!schemasNotPlatformSurfaceable.isEmpty()) {
- visibilityDocument.setPropertyString(
- NOT_PLATFORM_SURFACEABLE_PROPERTY,
+ visibilityDocument.setSchemasNotPlatformSurfaceable(
schemasNotPlatformSurfaceable.toArray(new String[0]));
}
Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
- List<GenericDocument> packageAccessibleDocuments = new ArrayList<>();
+ List<PackageAccessibleDocument> packageAccessibleDocuments = new ArrayList<>();
for (Map.Entry<String, List<PackageIdentifier>> entry :
schemasPackageAccessible.entrySet()) {
for (int i = 0; i < entry.getValue().size(); i++) {
- GenericDocument packageAccessibleDocument = new GenericDocument.Builder<>(
- NAMESPACE, /*id=*/ "", PACKAGE_ACCESSIBLE_TYPE)
- .setPropertyString(
- PACKAGE_NAME_PROPERTY,
- entry.getValue().get(i).getPackageName())
- .setPropertyBytes(
- SHA_256_CERT_PROPERTY,
- entry.getValue().get(i).getSha256Certificate())
- .setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, entry.getKey())
- .build();
+ PackageAccessibleDocument packageAccessibleDocument =
+ new PackageAccessibleDocument.Builder(NAMESPACE, /*id=*/ "")
+ .setAccessibleSchemaType(entry.getKey())
+ .setPackageIdentifier(entry.getValue().get(i))
+ .build();
packageAccessibleDocuments.add(packageAccessibleDocument);
}
schemaToPackageIdentifierMap.put(entry.getKey(), new ArraySet<>(entry.getValue()));
}
if (!packageAccessibleDocuments.isEmpty()) {
- visibilityDocument.setPropertyDocument(
- PACKAGE_ACCESSIBLE_PROPERTY,
- packageAccessibleDocuments.toArray(new GenericDocument[0]));
+ visibilityDocument.setPackageAccessibleSchemas(
+ packageAccessibleDocuments.toArray(new PackageAccessibleDocument[0]));
}
mAppSearchImpl.putDocument(
@@ -379,8 +293,8 @@
mAppSearchImpl.persistToDisk(PersistType.Code.LITE);
// Update derived data structures.
- mNotPlatformSurfaceableMap.put(prefix, schemasNotPlatformSurfaceable);
- mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap);
+ mNotPlatformSurfaceableMap.setNotPlatformSurfaceable(prefix, schemasNotPlatformSurfaceable);
+ mPackageAccessibleMap.setPackageAccessible(prefix, schemaToPackageIdentifierMap);
}
/** Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}. */
@@ -389,10 +303,14 @@
Objects.requireNonNull(prefix);
Objects.requireNonNull(prefixedSchema);
+ if (prefix.equals(VISIBILITY_STORE_PREFIX)) {
+ return false; // VisibilityStore schemas are for internal bookkeeping.
+ }
+
// We compare appIds here rather than direct uids because the package's uid may change based
// on the user that's running.
if (UserHandle.isSameApp(mGlobalQuerierUid, callerUid)
- && isSchemaPlatformSurfaceable(prefix, prefixedSchema)) {
+ && mNotPlatformSurfaceableMap.isSchemaPlatformSurfaceable(prefix, prefixedSchema)) {
return true;
}
@@ -401,29 +319,6 @@
}
/**
- * Returns whether the caller has platform query privileges, and if so, that the schema is
- * surfaceable on the platform.
- */
- private boolean isSchemaPlatformSurfaceable(
- @NonNull String prefix, @NonNull String prefixedSchema) {
- if (prefix.equals(VISIBILITY_STORE_PREFIX)) {
- // VisibilityStore schemas are for internal bookkeeping.
- return false;
- }
-
- Set<String> notPlatformSurfaceableSchemas = mNotPlatformSurfaceableMap.get(prefix);
- if (notPlatformSurfaceableSchemas == null) {
- // No schemas were opted out of being platform-surfaced. So by default, it can be
- // surfaced.
- return true;
- }
-
- // Some schemas were opted out of being platform-surfaced. As long as this schema
- // isn't one of those opt-outs, it's surfaceable.
- return !notPlatformSurfaceableSchemas.contains(prefixedSchema);
- }
-
- /**
* Returns whether the schema is accessible by the {@code callerUid}. Checks that the callerUid
* has one of the allowed PackageIdentifier's package. And if so, that the package also has the
* matching certificate.
@@ -434,20 +329,8 @@
*/
private boolean isSchemaPackageAccessible(
@NonNull String prefix, @NonNull String prefixedSchema, int callerUid) {
- Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap =
- mPackageAccessibleMap.get(prefix);
- if (schemaToPackageIdentifierMap == null) {
- // No schemas under this prefix have granted package access, return early.
- return false;
- }
-
Set<PackageIdentifier> packageIdentifiers =
- schemaToPackageIdentifierMap.get(prefixedSchema);
- if (packageIdentifiers == null) {
- // No package identifiers were granted access for this schema, return early.
- return false;
- }
-
+ mPackageAccessibleMap.getAccessiblePackages(prefix, prefixedSchema);
for (PackageIdentifier packageIdentifier : packageIdentifiers) {
// Check that the caller uid matches this allowlisted PackageIdentifier.
// TODO(b/169883602): Consider caching the UIDs of packages. Looking this up in the
@@ -467,7 +350,6 @@
return true;
}
}
-
// If we can't verify the schema is package accessible, default to no access.
return false;
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index e5e20e7..1cb0cc5 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -28,6 +28,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
+import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetByDocumentIdRequest;
@@ -38,6 +39,7 @@
import android.app.appsearch.SetSchemaResponse;
import android.app.appsearch.StorageInfo;
import android.app.appsearch.exceptions.AppSearchException;
+import android.app.appsearch.util.LogUtil;
import android.content.Context;
import android.os.Bundle;
import android.os.SystemClock;
@@ -147,8 +149,11 @@
private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
+ private final LogUtil mLogUtil = new LogUtil(TAG);
+
@GuardedBy("mReadWriteLock")
- private final IcingSearchEngine mIcingSearchEngineLocked;
+ @VisibleForTesting
+ final IcingSearchEngine mIcingSearchEngineLocked;
@GuardedBy("mReadWriteLock")
private final VisibilityStore mVisibilityStoreLocked;
@@ -216,7 +221,7 @@
appSearchImpl.initializeVisibilityStore();
long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime();
- if (logger != null && initStatsBuilder != null) {
+ if (logger != null) {
initStatsBuilder
.setTotalLatencyMillis(
(int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
@@ -247,66 +252,91 @@
IcingSearchEngineOptions.newBuilder()
.setBaseDir(icingDir.getAbsolutePath())
.build();
+ mLogUtil.piiTrace("Constructing IcingSearchEngine, request", options);
mIcingSearchEngineLocked = new IcingSearchEngine(options);
+ mLogUtil.piiTrace(
+ "Constructing IcingSearchEngine, response",
+ Objects.hashCode(mIcingSearchEngineLocked));
+
mVisibilityStoreLocked =
new VisibilityStore(this, context, userId, globalQuerierPackage);
- InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
- if (initStatsBuilder != null) {
- initStatsBuilder
- .setStatusCode(
- statusProtoToAppSearchException(initializeResultProto.getStatus())
- .getResultCode())
- // TODO(b/173532925) how to get DeSyncs value
- .setHasDeSync(false);
- AppSearchLoggerHelper.copyNativeStats(
- initializeResultProto.getInitializeStats(), initStatsBuilder);
- }
-
- long prepareSchemaAndNamespacesLatencyStartMillis = SystemClock.elapsedRealtime();
- SchemaProto schemaProto;
- GetAllNamespacesResultProto getAllNamespacesResultProto = null;
+ // The core initialization procedure. If any part of this fails, we bail into
+ // resetLocked(), deleting all data (but hopefully allowing AppSearchImpl to come up).
try {
- checkSuccess(initializeResultProto.getStatus());
- schemaProto = getSchemaProtoLocked();
- getAllNamespacesResultProto = mIcingSearchEngineLocked.getAllNamespaces();
- checkSuccess(getAllNamespacesResultProto.getStatus());
- } catch (AppSearchException e) {
- Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e);
- if (initStatsBuilder != null && getAllNamespacesResultProto != null) {
+ mLogUtil.piiTrace("icingSearchEngine.initialize, request");
+ InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
+ mLogUtil.piiTrace(
+ "icingSearchEngine.initialize, response",
+ initializeResultProto.getStatus(),
+ initializeResultProto);
+
+ if (initStatsBuilder != null) {
initStatsBuilder
.setStatusCode(
- statusProtoToAppSearchException(
- getAllNamespacesResultProto.getStatus())
- .getResultCode())
+ statusProtoToResultCode(initializeResultProto.getStatus()))
+ // TODO(b/173532925) how to get DeSyncs value
+ .setHasDeSync(false);
+ AppSearchLoggerHelper.copyNativeStats(
+ initializeResultProto.getInitializeStats(), initStatsBuilder);
+ }
+
+ checkSuccess(initializeResultProto.getStatus());
+
+ long prepareSchemaAndNamespacesLatencyStartMillis = SystemClock.elapsedRealtime();
+ SchemaProto schemaProto = getSchemaProtoLocked();
+
+ mLogUtil.piiTrace("init:getAllNamespaces, request");
+ GetAllNamespacesResultProto getAllNamespacesResultProto =
+ mIcingSearchEngineLocked.getAllNamespaces();
+ mLogUtil.piiTrace(
+ "init:getAllNamespaces, response",
+ getAllNamespacesResultProto.getNamespacesCount(),
+ getAllNamespacesResultProto);
+
+ if (initStatsBuilder != null) {
+ initStatsBuilder
+ .setStatusCode(
+ statusProtoToResultCode(
+ getAllNamespacesResultProto.getStatus()))
.setPrepareSchemaAndNamespacesLatencyMillis(
(int)
(SystemClock.elapsedRealtime()
- prepareSchemaAndNamespacesLatencyStartMillis));
}
+
+ checkSuccess(getAllNamespacesResultProto.getStatus());
+
+ // Populate schema map
+ for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
+ String prefixedSchemaType = schema.getSchemaType();
+ addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), schema);
+ }
+
+ // Populate namespace map
+ for (String prefixedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
+ addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace), prefixedNamespace);
+ }
+
+ // logging prepare_schema_and_namespaces latency
+ if (initStatsBuilder != null) {
+ initStatsBuilder.setPrepareSchemaAndNamespacesLatencyMillis(
+ (int)
+ (SystemClock.elapsedRealtime()
+ - prepareSchemaAndNamespacesLatencyStartMillis));
+ }
+
+ mLogUtil.piiTrace("Init completed successfully");
+
+ } catch (AppSearchException e) {
// Some error. Reset and see if it fixes it.
- resetLocked();
- return;
+ Log.e(TAG, "Error initializing, resetting IcingSearchEngine.", e);
+ if (initStatsBuilder != null) {
+ initStatsBuilder.setStatusCode(e.getResultCode());
+ }
+ resetLocked(initStatsBuilder);
}
- // Populate schema map
- for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
- String prefixedSchemaType = schema.getSchemaType();
- addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), schema);
- }
-
- // Populate namespace map
- for (String prefixedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
- addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace), prefixedNamespace);
- }
-
- // logging prepare_schema_and_namespaces latency
- if (initStatsBuilder != null) {
- initStatsBuilder.setPrepareSchemaAndNamespacesLatencyMillis(
- (int)
- (SystemClock.elapsedRealtime()
- - prepareSchemaAndNamespacesLatencyStartMillis));
- }
} finally {
mReadWriteLock.writeLock().unlock();
}
@@ -321,8 +351,9 @@
mReadWriteLock.writeLock().lock();
try {
throwIfClosedLocked();
-
+ mLogUtil.piiTrace("Initializing VisibilityStore, request");
mVisibilityStoreLocked.initialize();
+ mLogUtil.piiTrace("Initializing VisibilityStore, response");
} finally {
mReadWriteLock.writeLock().unlock();
}
@@ -348,9 +379,10 @@
if (mClosedLocked) {
return;
}
-
persistToDisk(PersistType.Code.FULL);
+ mLogUtil.piiTrace("icingSearchEngine.close, request");
mIcingSearchEngineLocked.close();
+ mLogUtil.piiTrace("icingSearchEngine.close, response");
mClosedLocked = true;
} catch (AppSearchException e) {
Log.w(TAG, "Error when closing AppSearchImpl.", e);
@@ -410,9 +442,12 @@
rewriteSchema(prefix, existingSchemaBuilder, newSchemaBuilder.build());
// Apply schema
+ SchemaProto finalSchema = existingSchemaBuilder.build();
+ mLogUtil.piiTrace("setSchema, request", finalSchema.getTypesCount(), finalSchema);
SetSchemaResultProto setSchemaResultProto =
- mIcingSearchEngineLocked.setSchema(
- existingSchemaBuilder.build(), forceOverride);
+ mIcingSearchEngineLocked.setSchema(finalSchema, forceOverride);
+ mLogUtil.piiTrace(
+ "setSchema, response", setSchemaResultProto.getStatus(), setSchemaResultProto);
// Determine whether it succeeded.
try {
@@ -544,11 +579,16 @@
mReadWriteLock.readLock().lock();
try {
throwIfClosedLocked();
+ mLogUtil.piiTrace("getAllNamespaces, request");
// We can't just use mNamespaceMap here because we have no way to prune namespaces from
// mNamespaceMap when they have no more documents (e.g. after setting schema to empty or
// using deleteByQuery).
GetAllNamespacesResultProto getAllNamespacesResultProto =
mIcingSearchEngineLocked.getAllNamespaces();
+ mLogUtil.piiTrace(
+ "getAllNamespaces, response",
+ getAllNamespacesResultProto.getNamespacesCount(),
+ getAllNamespacesResultProto);
checkSuccess(getAllNamespacesResultProto.getStatus());
String prefix = createPrefix(packageName, databaseName);
List<String> results = new ArrayList<>();
@@ -601,17 +641,18 @@
String prefix = createPrefix(packageName, databaseName);
addPrefixToDocument(documentBuilder, prefix);
long rewriteDocumentTypeEndTimeMillis = SystemClock.elapsedRealtime();
+ DocumentProto finalDocument = documentBuilder.build();
+ mLogUtil.piiTrace("putDocument, request", finalDocument.getUri(), finalDocument);
PutResultProto putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build());
+ mLogUtil.piiTrace("putDocument, response", putResultProto.getStatus(), putResultProto);
addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace());
// Logging stats
- if (logger != null && pStatsBuilder != null) {
+ if (pStatsBuilder != null) {
pStatsBuilder
.getGeneralStatsBuilder()
- .setStatusCode(
- statusProtoToAppSearchException(putResultProto.getStatus())
- .getResultCode());
+ .setStatusCode(statusProtoToResultCode(putResultProto.getStatus()));
pStatsBuilder
.setGenerateDocumentProtoLatencyMillis(
(int)
@@ -629,7 +670,7 @@
} finally {
mReadWriteLock.writeLock().unlock();
- if (logger != null && pStatsBuilder != null) {
+ if (logger != null) {
long totalEndTimeMillis = SystemClock.elapsedRealtime();
pStatsBuilder
.getGeneralStatsBuilder()
@@ -685,8 +726,14 @@
.addAllTypePropertyMasks(prefixedPropertyMasks)
.build();
+ String finalNamespace = createPrefix(packageName, databaseName) + namespace;
+ if (mLogUtil.isPiiTraceEnabled()) {
+ mLogUtil.piiTrace(
+ "getDocument, request", finalNamespace + ", " + id + "," + getResultSpec);
+ }
GetResultProto getResultProto =
- mIcingSearchEngineLocked.get(prefix + namespace, id, getResultSpec);
+ mIcingSearchEngineLocked.get(finalNamespace, id, getResultSpec);
+ mLogUtil.piiTrace("getDocument, response", getResultProto.getStatus(), getResultProto);
checkSuccess(getResultProto.getStatus());
// The schema type map cannot be null at this point. It could only be null if no
@@ -754,7 +801,7 @@
sStatsBuilder);
} finally {
mReadWriteLock.readLock().unlock();
- if (logger != null && sStatsBuilder != null) {
+ if (logger != null) {
sStatsBuilder.setTotalLatencyMillis(
(int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
logger.logStats(sStatsBuilder.build());
@@ -832,9 +879,10 @@
}
} else {
// Client didn't specify certain schemas to search over, check all schemas
- Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix).keySet();
+ Map<String, SchemaTypeConfigProto> prefixedSchemas =
+ mSchemaMapLocked.get(prefix);
if (prefixedSchemas != null) {
- for (String prefixedSchema : prefixedSchemas) {
+ for (String prefixedSchema : prefixedSchemas.keySet()) {
if (packageName.equals(callerPackageName)
|| mVisibilityStoreLocked.isSchemaSearchableByCaller(
prefix, prefixedSchema, callerUid)) {
@@ -854,7 +902,7 @@
} finally {
mReadWriteLock.readLock().unlock();
- if (logger != null && sStatsBuilder != null) {
+ if (logger != null) {
sStatsBuilder.setTotalLatencyMillis(
(int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
logger.logStats(sStatsBuilder.build());
@@ -938,18 +986,25 @@
rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes, allowedPrefixedSchemas);
ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
+ SearchSpecProto finalSearchSpec = searchSpecBuilder.build();
+ ResultSpecProto finalResultSpec = resultSpecBuilder.build();
long rewriteSearchSpecLatencyEndMillis = SystemClock.elapsedRealtime();
+ if (mLogUtil.isPiiTraceEnabled()) {
+ mLogUtil.piiTrace(
+ "search, request",
+ finalSearchSpec.getQuery(),
+ finalSearchSpec + ", " + scoringSpec + ", " + finalResultSpec);
+ }
SearchResultProto searchResultProto =
- mIcingSearchEngineLocked.search(
- searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build());
+ mIcingSearchEngineLocked.search(finalSearchSpec, scoringSpec, finalResultSpec);
+ mLogUtil.piiTrace(
+ "search, response", searchResultProto.getResultsCount(), searchResultProto);
if (sStatsBuilder != null) {
sStatsBuilder
- .setStatusCode(
- statusProtoToAppSearchException(searchResultProto.getStatus())
- .getResultCode())
+ .setStatusCode(statusProtoToResultCode(searchResultProto.getStatus()))
.setRewriteSearchSpecLatencyMillis(
(int)
(rewriteSearchSpecLatencyEndMillis
@@ -985,8 +1040,13 @@
try {
throwIfClosedLocked();
+ mLogUtil.piiTrace("getNextPage, request", nextPageToken);
SearchResultProto searchResultProto =
mIcingSearchEngineLocked.getNextPage(nextPageToken);
+ mLogUtil.piiTrace(
+ "getNextPage, response",
+ searchResultProto.getResultsCount(),
+ searchResultProto);
checkSuccess(searchResultProto.getStatus());
return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
} finally {
@@ -1007,6 +1067,7 @@
try {
throwIfClosedLocked();
+ mLogUtil.piiTrace("invalidateNextPageToken, request", nextPageToken);
mIcingSearchEngineLocked.invalidateNextPageToken(nextPageToken);
} finally {
mReadWriteLock.readLock().unlock();
@@ -1039,7 +1100,9 @@
.setUsageType(usageType)
.build();
+ mLogUtil.piiTrace("reportUsage, request", report.getDocumentUri(), report);
ReportUsageResultProto result = mIcingSearchEngineLocked.reportUsage(report);
+ mLogUtil.piiTrace("reportUsage, response", result.getStatus(), result);
checkSuccess(result.getStatus());
} finally {
mReadWriteLock.writeLock().unlock();
@@ -1068,9 +1131,13 @@
throwIfClosedLocked();
String prefixedNamespace = createPrefix(packageName, databaseName) + namespace;
+ if (mLogUtil.isPiiTraceEnabled()) {
+ mLogUtil.piiTrace("removeById, request", prefixedNamespace + ", " + id);
+ }
DeleteResultProto deleteResultProto =
mIcingSearchEngineLocked.delete(prefixedNamespace, id);
-
+ mLogUtil.piiTrace(
+ "removeById, response", deleteResultProto.getStatus(), deleteResultProto);
checkSuccess(deleteResultProto.getStatus());
} finally {
mReadWriteLock.writeLock().unlock();
@@ -1121,8 +1188,12 @@
searchSpecBuilder, Collections.singleton(prefix), allowedPrefixedSchemas)) {
return;
}
+ SearchSpecProto finalSearchSpec = searchSpecBuilder.build();
+ mLogUtil.piiTrace("removeByQuery, request", finalSearchSpec);
DeleteByQueryResultProto deleteResultProto =
- mIcingSearchEngineLocked.deleteByQuery(searchSpecBuilder.build());
+ mIcingSearchEngineLocked.deleteByQuery(finalSearchSpec);
+ mLogUtil.piiTrace(
+ "removeByQuery, response", deleteResultProto.getStatus(), deleteResultProto);
// It seems that the caller wants to get success if the data matching the query is
// not in the DB because it was not there or was successfully deleted.
@@ -1202,7 +1273,10 @@
@NonNull
private StorageInfo getStorageInfoForNamespacesLocked(@NonNull Set<String> prefixedNamespaces)
throws AppSearchException {
+ mLogUtil.piiTrace("getStorageInfo, request");
StorageInfoResultProto storageInfoResult = mIcingSearchEngineLocked.getStorageInfo();
+ mLogUtil.piiTrace(
+ "getStorageInfo, response", storageInfoResult.getStatus(), storageInfoResult);
checkSuccess(storageInfoResult.getStatus());
if (!storageInfoResult.hasStorageInfo()
|| !storageInfoResult.getStorageInfo().hasDocumentStorageInfo()) {
@@ -1277,8 +1351,13 @@
try {
throwIfClosedLocked();
+ mLogUtil.piiTrace("persistToDisk, request", persistType);
PersistToDiskResultProto persistToDiskResultProto =
mIcingSearchEngineLocked.persistToDisk(persistType);
+ mLogUtil.piiTrace(
+ "persistToDisk, response",
+ persistToDiskResultProto.getStatus(),
+ persistToDiskResultProto);
checkSuccess(persistToDiskResultProto.getStatus());
} finally {
mReadWriteLock.writeLock().unlock();
@@ -1305,12 +1384,21 @@
newSchemaBuilder.addTypes(existingSchema.getTypes(i));
}
}
+ SchemaProto finalSchema = newSchemaBuilder.build();
// Apply schema, set force override to true to remove all schemas and documents under
// that package.
+ mLogUtil.piiTrace(
+ "clearPackageData.setSchema, request",
+ finalSchema.getTypesCount(),
+ finalSchema);
SetSchemaResultProto setSchemaResultProto =
mIcingSearchEngineLocked.setSchema(
- newSchemaBuilder.build(), /*ignoreErrorsAndDeleteDocuments=*/ true);
+ finalSchema, /*ignoreErrorsAndDeleteDocuments=*/ true);
+ mLogUtil.piiTrace(
+ "clearPackageData.setSchema, response",
+ setSchemaResultProto.getStatus(),
+ setSchemaResultProto);
// Determine whether it succeeded.
checkSuccess(setSchemaResultProto.getStatus());
@@ -1330,12 +1418,24 @@
* @throws AppSearchException on IcingSearchEngine error.
*/
@GuardedBy("mReadWriteLock")
- private void resetLocked() throws AppSearchException {
+ private void resetLocked(@Nullable InitializeStats.Builder initStatsBuilder)
+ throws AppSearchException {
+ mLogUtil.piiTrace("icingSearchEngine.reset, request");
ResetResultProto resetResultProto = mIcingSearchEngineLocked.reset();
+ mLogUtil.piiTrace(
+ "icingSearchEngine.reset, response",
+ resetResultProto.getStatus(),
+ resetResultProto);
mOptimizeIntervalCountLocked = 0;
mSchemaMapLocked.clear();
mNamespaceMapLocked.clear();
+ if (initStatsBuilder != null) {
+ initStatsBuilder
+ .setHasReset(true)
+ .setResetStatusCode(statusProtoToResultCode(resetResultProto.getStatus()));
+ }
+
// Must be called after everything else since VisibilityStore may repopulate
// IcingSearchEngine with an initial schema.
mVisibilityStoreLocked.handleReset();
@@ -1472,15 +1572,17 @@
// Empty namespaces on the search spec means to query over all namespaces.
Set<String> existingNamespaces = mNamespaceMapLocked.get(prefix);
- if (namespaceFilters.isEmpty()) {
- // Include all namespaces
- searchSpecBuilder.addAllNamespaceFilters(existingNamespaces);
- } else {
- // Prefix the given namespaces.
- for (int i = 0; i < namespaceFilters.size(); i++) {
- String prefixedNamespace = prefix + namespaceFilters.get(i);
- if (existingNamespaces.contains(prefixedNamespace)) {
- searchSpecBuilder.addNamespaceFilters(prefixedNamespace);
+ if (existingNamespaces != null) {
+ if (namespaceFilters.isEmpty()) {
+ // Include all namespaces
+ searchSpecBuilder.addAllNamespaceFilters(existingNamespaces);
+ } else {
+ // Prefix the given namespaces.
+ for (int i = 0; i < namespaceFilters.size(); i++) {
+ String prefixedNamespace = prefix + namespaceFilters.get(i);
+ if (existingNamespaces.contains(prefixedNamespace)) {
+ searchSpecBuilder.addNamespaceFilters(prefixedNamespace);
+ }
}
}
}
@@ -1581,6 +1683,9 @@
Map<String, List<String>> packageAndNamespaceToNamespaces = new ArrayMap<>();
for (String prefix : existingPrefixes) {
Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix);
+ if (prefixedNamespaces == null) {
+ continue;
+ }
String packageName = getPackageName(prefix);
// Create a new prefix without the database name. This will allow us to group namespaces
// that have the same name and package but a different database name together.
@@ -1636,6 +1741,9 @@
Map<String, List<String>> packageToNamespacesMap = new ArrayMap<>();
for (String prefix : existingPrefixes) {
Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix);
+ if (prefixedNamespaces == null) {
+ continue;
+ }
String packageName = getPackageName(prefix);
List<String> packageNamespaceList = packageToNamespacesMap.get(packageName);
if (packageNamespaceList == null) {
@@ -1677,6 +1785,9 @@
Map<String, List<String>> namespaceToPrefixedNamespaces = new ArrayMap<>();
for (String prefix : existingPrefixes) {
Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix);
+ if (prefixedNamespaces == null) {
+ continue;
+ }
for (String prefixedNamespace : prefixedNamespaces) {
String namespace;
try {
@@ -1707,7 +1818,9 @@
@VisibleForTesting
@GuardedBy("mReadWriteLock")
SchemaProto getSchemaProtoLocked() throws AppSearchException {
+ mLogUtil.piiTrace("getSchema, request");
GetSchemaResultProto schemaProto = mIcingSearchEngineLocked.getSchema();
+ mLogUtil.piiTrace("getSchema, response", schemaProto.getStatus(), schemaProto);
// TODO(b/161935693) check GetSchemaResultProto is success or not. Call reset() if it's not.
// TODO(b/161935693) only allow GetSchemaResultProto NOT_FOUND on first run
checkCodeOneOf(schemaProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
@@ -1781,7 +1894,9 @@
return;
}
- throw statusProtoToAppSearchException(statusProto);
+ throw new AppSearchException(
+ ResultCodeToProtoConverter.toResultCode(statusProto.getCode()),
+ statusProto.getMessage());
}
/**
@@ -1849,7 +1964,10 @@
public void optimize() throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
+ mLogUtil.piiTrace("optimize, request");
OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize();
+ mLogUtil.piiTrace(
+ "optimize, response", optimizeResultProto.getStatus(), optimizeResultProto);
checkSuccess(optimizeResultProto.getStatus());
} finally {
mReadWriteLock.writeLock().unlock();
@@ -1887,7 +2005,10 @@
@GuardedBy("mReadWriteLock")
@VisibleForTesting
GetOptimizeInfoResultProto getOptimizeInfoResultLocked() {
- return mIcingSearchEngineLocked.getOptimizeInfo();
+ mLogUtil.piiTrace("getOptimizeInfo, request");
+ GetOptimizeInfoResultProto result = mIcingSearchEngineLocked.getOptimizeInfo();
+ mLogUtil.piiTrace("getOptimizeInfo, response", result.getStatus(), result);
+ return result;
}
@GuardedBy("mReadWriteLock")
@@ -1898,16 +2019,16 @@
}
/**
- * Converts an erroneous status code to an AppSearchException. Callers should ensure that the
- * status code is not OK or WARNING_DATA_LOSS.
+ * Converts an erroneous status code from the Icing status enums to the AppSearchResult enums.
*
- * @param statusProto StatusProto with error code and message to translate into
- * AppSearchException.
- * @return AppSearchException with the parallel error code.
+ * <p>Callers should ensure that the status code is not OK or WARNING_DATA_LOSS.
+ *
+ * @param statusProto StatusProto with error code to translate into an {@link AppSearchResult}
+ * code.
+ * @return {@link AppSearchResult} error code
*/
- private static AppSearchException statusProtoToAppSearchException(StatusProto statusProto) {
- return new AppSearchException(
- ResultCodeToProtoConverter.toResultCode(statusProto.getCode()),
- statusProto.getMessage());
+ private static @AppSearchResult.ResultCode int statusProtoToResultCode(
+ @NonNull StatusProto statusProto) {
+ return ResultCodeToProtoConverter.toResultCode(statusProto.getCode());
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
index 4a5ecf1..4b92ce6 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
@@ -103,8 +103,7 @@
toStatsBuilder
.setNativeLatencyMillis(fromNativeStats.getLatencyMs())
.setTermCount(fromNativeStats.getNumTerms())
- // TODO(b/173532925) query length missing in native
- // .setNativeQueryLength(0)
+ .setQueryLength(fromNativeStats.getQueryLength())
.setFilteredNamespaceCount(fromNativeStats.getNumNamespacesFiltered())
.setFilteredSchemaTypeCount(fromNativeStats.getNumSchemaTypesFiltered())
.setRequestedPageSize(fromNativeStats.getRequestedPageSize())
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
index 0cdad37..9ce916f 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
@@ -199,7 +199,7 @@
case AppSearchSchema.PropertyConfig.DATA_TYPE_STRING:
documentBuilder.setPropertyString(propertyName, EMPTY_STRING_ARRAY);
break;
- case AppSearchSchema.PropertyConfig.DATA_TYPE_INT64:
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_LONG:
documentBuilder.setPropertyLong(propertyName, EMPTY_LONG_ARRAY);
break;
case AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE:
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
index 80f7007..3e4e7d2 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
@@ -133,7 +133,7 @@
case STRING:
return toStringPropertyConfig(proto);
case INT64:
- return new AppSearchSchema.Int64PropertyConfig.Builder(proto.getPropertyName())
+ return new AppSearchSchema.LongPropertyConfig.Builder(proto.getPropertyName())
.setCardinality(proto.getCardinality().getNumber())
.build();
case DOUBLE:
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
index ecc774c..6b443b3 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
@@ -128,14 +128,14 @@
return new SearchResult.MatchInfo.Builder(propertyPath)
.setExactMatchRange(
new SearchResult.MatchRange(
- snippetMatchProto.getExactMatchPosition(),
- snippetMatchProto.getExactMatchPosition()
- + snippetMatchProto.getExactMatchBytes()))
+ snippetMatchProto.getExactMatchUtf16Position(),
+ snippetMatchProto.getExactMatchUtf16Position()
+ + snippetMatchProto.getExactMatchUtf16Length()))
.setSnippetRange(
new SearchResult.MatchRange(
- snippetMatchProto.getWindowPosition(),
- snippetMatchProto.getWindowPosition()
- + snippetMatchProto.getWindowBytes()))
+ snippetMatchProto.getWindowUtf16Position(),
+ snippetMatchProto.getWindowUtf16Position()
+ + snippetMatchProto.getWindowUtf16Length()))
.build();
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
index cf640c1..ea5263a 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
@@ -43,13 +43,16 @@
CALL_TYPE_SET_SCHEMA,
CALL_TYPE_PUT_DOCUMENTS,
CALL_TYPE_GET_DOCUMENTS,
- CALL_TYPE_REMOVE_DOCUMENTS,
+ CALL_TYPE_REMOVE_DOCUMENTS_BY_ID,
CALL_TYPE_PUT_DOCUMENT,
CALL_TYPE_GET_DOCUMENT,
- CALL_TYPE_REMOVE_DOCUMENT,
- CALL_TYPE_QUERY,
+ CALL_TYPE_REMOVE_DOCUMENT_BY_ID,
+ CALL_TYPE_SEARCH,
CALL_TYPE_OPTIMIZE,
CALL_TYPE_FLUSH,
+ CALL_TYPE_GLOBAL_SEARCH,
+ CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH,
+ CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CallType {}
@@ -59,13 +62,16 @@
public static final int CALL_TYPE_SET_SCHEMA = 2;
public static final int CALL_TYPE_PUT_DOCUMENTS = 3;
public static final int CALL_TYPE_GET_DOCUMENTS = 4;
- public static final int CALL_TYPE_REMOVE_DOCUMENTS = 5;
+ public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_ID = 5;
public static final int CALL_TYPE_PUT_DOCUMENT = 6;
public static final int CALL_TYPE_GET_DOCUMENT = 7;
- public static final int CALL_TYPE_REMOVE_DOCUMENT = 8;
- public static final int CALL_TYPE_QUERY = 9;
+ public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_ID = 8;
+ public static final int CALL_TYPE_SEARCH = 9;
public static final int CALL_TYPE_OPTIMIZE = 10;
public static final int CALL_TYPE_FLUSH = 11;
+ public static final int CALL_TYPE_GLOBAL_SEARCH = 12;
+ public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH = 13;
+ public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH = 14;
@NonNull private final GeneralStats mGeneralStats;
@CallType private final int mCallType;
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java
index 5364a0c..72befa7 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java
@@ -106,6 +106,10 @@
private final int mNativeNumDocuments;
/** Returns number of schema types currently in the schema store. */
private final int mNativeNumSchemaTypes;
+ /** Whether we had to reset the index, losing all data, during initialization. */
+ private final boolean mHasReset;
+ /** If we had to reset, contains the status code of the reset operation. */
+ @AppSearchResult.ResultCode private final int mResetStatusCode;
/** Returns the status of the initialization. */
@AppSearchResult.ResultCode
@@ -214,6 +218,21 @@
return mNativeNumSchemaTypes;
}
+ /** Returns whether we had to reset the index, losing all data, as part of initialization. */
+ public boolean hasReset() {
+ return mHasReset;
+ }
+
+ /**
+ * Returns the status of the reset, if one was performed according to {@link #hasReset}.
+ *
+ * <p>If no value has been set, the default value is {@link AppSearchResult#RESULT_OK}.
+ */
+ @AppSearchResult.ResultCode
+ public int getResetStatusCode() {
+ return mResetStatusCode;
+ }
+
InitializeStats(@NonNull Builder builder) {
Objects.requireNonNull(builder);
mStatusCode = builder.mStatusCode;
@@ -232,11 +251,14 @@
mNativeDocumentStoreDataStatus = builder.mNativeDocumentStoreDataStatus;
mNativeNumDocuments = builder.mNativeNumDocuments;
mNativeNumSchemaTypes = builder.mNativeNumSchemaTypes;
+ mHasReset = builder.mHasReset;
+ mResetStatusCode = builder.mResetStatusCode;
}
/** Builder for {@link InitializeStats}. */
public static class Builder {
@AppSearchResult.ResultCode int mStatusCode;
+
int mTotalLatencyMillis;
boolean mHasDeSync;
int mPrepareSchemaAndNamespacesLatencyMillis;
@@ -251,6 +273,8 @@
@DocumentStoreDataStatus int mNativeDocumentStoreDataStatus;
int mNativeNumDocuments;
int mNativeNumSchemaTypes;
+ boolean mHasReset;
+ @AppSearchResult.ResultCode int mResetStatusCode;
/** Sets the status of the initialization. */
@NonNull
@@ -392,6 +416,20 @@
return this;
}
+ /** Sets whether we had to reset the index, losing all data, as part of initialization. */
+ @NonNull
+ public Builder setHasReset(boolean hasReset) {
+ mHasReset = hasReset;
+ return this;
+ }
+
+ /** Sets the status of the reset, if one was performed according to {@link #setHasReset}. */
+ @NonNull
+ public Builder setResetStatusCode(@AppSearchResult.ResultCode int resetStatusCode) {
+ mResetStatusCode = resetStatusCode;
+ return this;
+ }
+
/**
* Constructs a new {@link InitializeStats} from the contents of this {@link
* InitializeStats.Builder}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/RemoveStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/RemoveStats.java
new file mode 100644
index 0000000..b900c49
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/RemoveStats.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.external.localstorage.stats;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.RemoveByDocumentIdRequest;
+import android.app.appsearch.SearchSpec;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class holds detailed stats for {@link
+ * android.app.appsearch.AppSearchSession#remove(RemoveByDocumentIdRequest)} and {@link
+ * android.app.appsearch.AppSearchSession#remove(String, SearchSpec)}
+ *
+ * @hide
+ */
+public final class RemoveStats {
+ @IntDef(
+ value = {
+ // It needs to be sync with DeleteType.Code in
+ // external/icing/proto/icing/proto/logging.proto#DeleteStatsProto
+ UNKNOWN,
+ SINGLE,
+ QUERY,
+ NAMESPACE,
+ SCHEMA_TYPE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeleteType {}
+
+ /** Default. Should never be used. */
+ public static final int UNKNOWN = 0;
+ /** Delete by namespace + id. */
+ public static final int SINGLE = 1;
+ /** Delete by query. */
+ public static final int QUERY = 2;
+ /** Delete by namespace. */
+ public static final int NAMESPACE = 3;
+ /** Delete by schema type. */
+ public static final int SCHEMA_TYPE = 4;
+
+ @NonNull private final String mPackageName;
+ @NonNull private final String mDatabase;
+ /**
+ * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal
+ * state.
+ */
+ @AppSearchResult.ResultCode private final int mStatusCode;
+
+ private final int mTotalLatencyMillis;
+ private final int mNativeLatencyMillis;
+ @DeleteType private final int mNativeDeleteType;
+ private final int mNativeNumDocumentsDeleted;
+
+ RemoveStats(@NonNull Builder builder) {
+ Objects.requireNonNull(builder);
+ mPackageName = builder.mPackageName;
+ mDatabase = builder.mDatabase;
+ mStatusCode = builder.mStatusCode;
+ mTotalLatencyMillis = builder.mTotalLatencyMillis;
+ mNativeLatencyMillis = builder.mNativeLatencyMillis;
+ mNativeDeleteType = builder.mNativeDeleteType;
+ mNativeNumDocumentsDeleted = builder.mNativeNumDocumentsDeleted;
+ }
+
+ /** Returns calling package name. */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /** Returns calling database name. */
+ @NonNull
+ public String getDatabase() {
+ return mDatabase;
+ }
+
+ /** Returns status code for this remove. */
+ @AppSearchResult.ResultCode
+ public int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /** Returns total latency of this remove in millis. */
+ public int getTotalLatencyMillis() {
+ return mTotalLatencyMillis;
+ }
+
+ /** Returns how much time in millis spent in the native code. */
+ public int getNativeLatencyMillis() {
+ return mNativeLatencyMillis;
+ }
+
+ /** Returns what type of delete for this remove call. */
+ @DeleteType
+ public int getDeleteType() {
+ return mNativeDeleteType;
+ }
+
+ /** Returns how many documents get deleted in this call. */
+ public int getDeletedDocumentCount() {
+ return mNativeNumDocumentsDeleted;
+ }
+
+ /** Builder for {@link RemoveStats}. */
+ public static class Builder {
+ @NonNull final String mPackageName;
+ @NonNull final String mDatabase;
+ @AppSearchResult.ResultCode int mStatusCode;
+ int mTotalLatencyMillis;
+ int mNativeLatencyMillis;
+ @DeleteType int mNativeDeleteType;
+ int mNativeNumDocumentsDeleted;
+
+ /** Constructor for the {@link Builder}. */
+ public Builder(@NonNull String packageName, @NonNull String database) {
+ mPackageName = Objects.requireNonNull(packageName);
+ mDatabase = Objects.requireNonNull(database);
+ }
+
+ /** Sets the status code. */
+ @NonNull
+ public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
+ mStatusCode = statusCode;
+ return this;
+ }
+
+ /** Sets total latency in millis. */
+ @NonNull
+ public Builder setTotalLatencyMillis(int totalLatencyMillis) {
+ mTotalLatencyMillis = totalLatencyMillis;
+ return this;
+ }
+
+ /** Sets native latency in millis. */
+ @NonNull
+ public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
+ mNativeLatencyMillis = nativeLatencyMillis;
+ return this;
+ }
+
+ /** Sets delete type for this call. */
+ @NonNull
+ public Builder setDeleteType(@DeleteType int nativeDeleteType) {
+ mNativeDeleteType = nativeDeleteType;
+ return this;
+ }
+
+ /** Sets how many documents get deleted for this call. */
+ @NonNull
+ public Builder setDeletedDocumentCount(int nativeNumDocumentsDeleted) {
+ mNativeNumDocumentsDeleted = nativeNumDocumentsDeleted;
+ return this;
+ }
+
+ /** Creates a {@link RemoveStats}. */
+ @NonNull
+ public RemoveStats build() {
+ return new RemoveStats(/* builder= */ this);
+ }
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java
index 9ae9f18..80cfe89 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java
@@ -112,8 +112,11 @@
return prefixedString.substring(delimiterIndex + 1);
}
throw new AppSearchException(
- AppSearchResult.RESULT_UNKNOWN_ERROR,
- "The prefixed value doesn't contains a valid database name.");
+ AppSearchResult.RESULT_INTERNAL_ERROR,
+ "The prefixed value \""
+ + prefixedString
+ + "\" doesn't contain a valid "
+ + "database name");
}
/**
@@ -128,8 +131,11 @@
int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER);
if (databaseDelimiterIndex == -1) {
throw new AppSearchException(
- AppSearchResult.RESULT_UNKNOWN_ERROR,
- "The databaseName prefixed value doesn't contain a valid database name.");
+ AppSearchResult.RESULT_INTERNAL_ERROR,
+ "The prefixed value \""
+ + prefixedString
+ + "\" doesn't contain a valid "
+ + "database name");
}
// Add 1 to include the char size of the DATABASE_DELIMITER
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
index 88f238e..df59306 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -17,6 +17,7 @@
package com.android.server.appsearch.stats;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -193,12 +194,22 @@
@Override
public void logStats(@NonNull InitializeStats stats) throws AppSearchException {
- // TODO(b/173532925): Implement
+ Objects.requireNonNull(stats);
+ synchronized (mLock) {
+ if (shouldLogForTypeLocked(CallStats.CALL_TYPE_INITIALIZE)) {
+ logStatsImplLocked(stats);
+ }
+ }
}
@Override
public void logStats(@NonNull SearchStats stats) throws AppSearchException {
- // TODO(b/173532925): Implement
+ Objects.requireNonNull(stats);
+ synchronized (mLock) {
+ if (shouldLogForTypeLocked(CallStats.CALL_TYPE_SEARCH)) {
+ logStatsImplLocked(stats);
+ }
+ }
}
/**
@@ -242,7 +253,9 @@
//
// Something is wrong while calculating the hash code for database
// this shouldn't happen since we always use "MD5" and "UTF-8"
- Log.e(TAG, "Error calculating hash code for database " + database, e);
+ if (database != null) {
+ Log.e(TAG, "Error calculating hash code for database " + database, e);
+ }
}
}
@@ -277,21 +290,102 @@
//
// Something is wrong while calculating the hash code for database
// this shouldn't happen since we always use "MD5" and "UTF-8"
- Log.e(TAG, "Error calculating hash code for database " + database, e);
+ if (database != null) {
+ Log.e(TAG, "Error calculating hash code for database " + database, e);
+ }
}
}
+ @GuardedBy("mLock")
+ private void logStatsImplLocked(@NonNull SearchStats stats) {
+ mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
+ ExtraStats extraStats = createExtraStatsLocked(stats.getPackageName(),
+ CallStats.CALL_TYPE_SEARCH);
+ String database = stats.getDatabase();
+ try {
+ int hashCodeForDatabase = calculateHashCodeMd5(database);
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_SEARCH_QUERY_STATS_REPORTED,
+ extraStats.mSamplingRatio,
+ extraStats.mSkippedSampleCount,
+ extraStats.mPackageUid,
+ hashCodeForDatabase,
+ stats.getStatusCode(),
+ stats.getTotalLatencyMillis(),
+ stats.getRewriteSearchSpecLatencyMillis(),
+ stats.getRewriteSearchResultLatencyMillis(),
+ stats.getVisibilityScope(),
+ stats.getNativeLatencyMillis(),
+ stats.getTermCount(),
+ stats.getQueryLength(),
+ stats.getFilteredNamespaceCount(),
+ stats.getFilteredSchemaTypeCount(),
+ stats.getRequestedPageSize(),
+ stats.getCurrentPageReturnedResultCount(),
+ stats.isFirstPage(),
+ stats.getParseQueryLatencyMillis(),
+ stats.getRankingStrategy(),
+ stats.getScoredDocumentCount(),
+ stats.getScoringLatencyMillis(),
+ stats.getRankingLatencyMillis(),
+ stats.getDocumentRetrievingLatencyMillis(),
+ stats.getResultWithSnippetsCount());
+ } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
+ // TODO(b/184204720) report hashing error to Westworld
+ // We need to set a special value(e.g. 0xFFFFFFFF) for the hashing of the database,
+ // so in the dashboard we know there is some error for hashing.
+ //
+ // Something is wrong while calculating the hash code for database
+ // this shouldn't happen since we always use "MD5" and "UTF-8"
+ if (database != null) {
+ Log.e(TAG, "Error calculating hash code for database " + database, e);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void logStatsImplLocked(@NonNull InitializeStats stats) {
+ mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
+ ExtraStats extraStats = createExtraStatsLocked(/*packageName=*/ null,
+ CallStats.CALL_TYPE_INITIALIZE);
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_SEARCH_INITIALIZE_STATS_REPORTED,
+ extraStats.mSamplingRatio,
+ extraStats.mSkippedSampleCount,
+ extraStats.mPackageUid,
+ stats.getStatusCode(),
+ stats.getTotalLatencyMillis(),
+ stats.hasDeSync(),
+ stats.getPrepareSchemaAndNamespacesLatencyMillis(),
+ stats.getPrepareVisibilityStoreLatencyMillis(),
+ stats.getNativeLatencyMillis(),
+ stats.getDocumentStoreRecoveryCause(),
+ stats.getIndexRestorationCause(),
+ stats.getSchemaStoreRecoveryCause(),
+ stats.getDocumentStoreRecoveryLatencyMillis(),
+ stats.getIndexRestorationLatencyMillis(),
+ stats.getSchemaStoreRecoveryLatencyMillis(),
+ stats.getDocumentStoreDataStatus(),
+ stats.getDocumentCount(),
+ stats.getSchemaTypeCount());
+ }
+
/**
* Calculate the hash code as an integer by returning the last four bytes of its MD5.
*
* @param str a string
- * @return hash code as an integer
+ * @return hash code as an integer. returns -1 if str is null.
* @throws AppSearchException if either algorithm or encoding does not exist.
*/
@VisibleForTesting
@NonNull
- static int calculateHashCodeMd5(@NonNull String str) throws
+ static int calculateHashCodeMd5(@Nullable String str) throws
NoSuchAlgorithmException, UnsupportedEncodingException {
+ if (str == null) {
+ // Just return -1 if caller doesn't have database name
+ // For some stats like globalQuery, databaseName can be null.
+ // Since in atom it is an integer, we have to return something here.
+ return -1;
+ }
+
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes(/*charsetName=*/ "UTF-8"));
byte[] digest = md.digest();
@@ -314,12 +408,19 @@
* <p>This method is called by most of logToWestworldLocked functions to reduce code
* duplication.
*/
+ // TODO(b/173532925) Once we add CTS test for logging atoms and can inspect the result, we can
+ // remove this @VisibleForTesting and directly use PlatformLogger.logStats to test sampling and
+ // rate limiting.
@VisibleForTesting
@GuardedBy("mLock")
@NonNull
- ExtraStats createExtraStatsLocked(@NonNull String packageName,
+ ExtraStats createExtraStatsLocked(@Nullable String packageName,
@CallStats.CallType int callType) {
- int packageUid = getPackageUidAsUserLocked(packageName);
+ int packageUid = Process.INVALID_UID;
+ if (packageName != null) {
+ packageUid = getPackageUidAsUserLocked(packageName);
+ }
+
int samplingRatio = mConfig.mSamplingRatios.get(callType,
mConfig.mDefaultSamplingRatio);
@@ -337,6 +438,9 @@
* stats.
*/
@GuardedBy("mLock")
+ // TODO(b/173532925) Once we add CTS test for logging atoms and can inspect the result, we can
+ // remove this @VisibleForTesting and directly use PlatformLogger.logStats to test sampling and
+ // rate limiting.
@VisibleForTesting
boolean shouldLogForTypeLocked(@CallStats.CallType int callType) {
int samplingRatio = mConfig.mSamplingRatios.get(callType,
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java
new file mode 100644
index 0000000..5afdda2
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appsearch.visibilitystore;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Stores information about what types are hidden from platform surfaces through the
+ * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} API.
+ *
+ * This object is not thread safe.
+ * @hide
+ */
+public class NotPlatformSurfaceableMap {
+ /**
+ * Maps prefixes to the set of prefixed schemas that are platform-hidden within that prefix.
+ */
+ private final Map<String, Set<String>> mMap = new ArrayMap<>();
+
+ /**
+ * Sets the prefixed schemas that are opted out of platform surfacing for the prefix.
+ *
+ * <p>Any existing mappings for this prefix are overwritten.
+ */
+ public void setNotPlatformSurfaceable(@NonNull String prefix, @NonNull Set<String> schemas) {
+ mMap.put(prefix, schemas);
+ }
+
+ /**
+ * Returns whether the given prefixed schema is platform surfaceable (has not opted out) in the
+ * given prefix.
+ */
+ public boolean isSchemaPlatformSurfaceable(@NonNull String prefix, @NonNull String schemaType) {
+ Set<String> schemaTypes = mMap.get(prefix);
+ if (schemaTypes == null) {
+ // No opt-outs for this prefix
+ return true;
+ }
+ // Some schemas were opted out of being platform-surfaced. As long as this schema
+ // isn't one of those opt-outs, it's surfaceable.
+ return !schemaTypes.contains(schemaType);
+ }
+
+ /** Discards all data in the map. */
+ public void clear() {
+ mMap.clear();
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java
new file mode 100644
index 0000000..5601ef9
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appsearch.visibilitystore;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.PackageIdentifier;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Holds configuration about a package+cert that can access a schema.
+ *
+ * @see android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage
+ * @hide
+ */
+public class PackageAccessibleDocument extends GenericDocument {
+ /** Schema type for nested documents that hold package accessible information. */
+ public static final String SCHEMA_TYPE = "PackageAccessibleType";
+
+ /** Property that holds the package name that can access a schema. */
+ private static final String PACKAGE_NAME_PROPERTY = "packageName";
+
+ /** Property that holds the SHA 256 certificate of the app that can access a schema. */
+ private static final String SHA_256_CERT_PROPERTY = "sha256Cert";
+
+ /** Property that holds the prefixed schema type that is accessible by some package. */
+ private static final String ACCESSIBLE_SCHEMA_PROPERTY = "accessibleSchema";
+
+ /**
+ * Schema for package accessible documents, these will be nested in a top-level
+ * {@link VisibilityDocument}.
+ *
+ * <p>NOTE: If you update this, also update
+ * {@link com.android.server.appsearch.external.localstorage.VisibilityStore#SCHEMA_VERSION}
+ */
+ public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
+ .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(
+ ACCESSIBLE_SCHEMA_PROPERTY)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .build();
+
+ public PackageAccessibleDocument(@NonNull GenericDocument genericDocument) {
+ super(genericDocument);
+ }
+
+ @Nullable
+ public String getAccessibleSchemaType() {
+ return getPropertyString(ACCESSIBLE_SCHEMA_PROPERTY);
+ }
+
+ /** Gets which package is able to access {@link #getAccessibleSchemaType} */
+ @NonNull
+ public PackageIdentifier getPackageIdentifier() {
+ String packageName = getPropertyString(PACKAGE_NAME_PROPERTY);
+ byte[] sha256Cert = getPropertyBytes(SHA_256_CERT_PROPERTY);
+ return new PackageIdentifier(packageName, sha256Cert);
+ }
+
+ /** Builder for {@link PackageAccessibleDocument} instances. */
+ public static class Builder extends GenericDocument.Builder<PackageAccessibleDocument.Builder> {
+ public Builder(@NonNull String namespace, @NonNull String id) {
+ super(namespace, id, SCHEMA_TYPE);
+ }
+
+ /** Sets which prefixed schema type is accessible by the package */
+ @NonNull
+ public Builder setAccessibleSchemaType(@NonNull String schemaType) {
+ return setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, schemaType);
+ }
+
+ /** Sets which package is able to access the {@link #setAccessibleSchemaType}. */
+ @NonNull
+ public Builder setPackageIdentifier(@NonNull PackageIdentifier packageIdentifier) {
+ return setPropertyString(PACKAGE_NAME_PROPERTY, packageIdentifier.getPackageName())
+ .setPropertyBytes(SHA_256_CERT_PROPERTY,
+ packageIdentifier.getSha256Certificate());
+ }
+
+ @Override
+ @NonNull
+ public PackageAccessibleDocument build() {
+ return new PackageAccessibleDocument(super.build());
+ }
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java
new file mode 100644
index 0000000..e90e8bf
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appsearch.visibilitystore;
+
+import android.annotation.NonNull;
+import android.app.appsearch.PackageIdentifier;
+import android.util.ArrayMap;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Stores information about what types are accessible to which packages through the
+ * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} API.
+ *
+ * This object is not thread safe.
+ * @hide
+ */
+public class PackageAccessibleMap {
+ /**
+ * Maps prefixes to prefixed schema types to PackageIdentifiers that have access to that schema.
+ */
+ private final Map<String, Map<String, Set<PackageIdentifier>>> mMap = new ArrayMap<>();
+
+ /**
+ * Sets the prefixed schemas that have package visibility in the given prefix.
+ *
+ * <p>Any existing mappings for this prefix are overwritten.
+ */
+ public void setPackageAccessible(
+ @NonNull String prefix,
+ @NonNull Map<String, Set<PackageIdentifier>> schemaToPackageIdentifier) {
+ mMap.put(prefix, schemaToPackageIdentifier);
+ }
+
+ /**
+ * Returns the set of all {@link android.app.appsearch.PackageIdentifier}s which can access the
+ * given schema type.
+ *
+ * <p>If no such settings exist, returns the empty set.
+ */
+ @NonNull
+ public Set<PackageIdentifier> getAccessiblePackages(
+ @NonNull String prefix, @NonNull String schemaType) {
+ Map<String, Set<PackageIdentifier>> schemaTypeToVisibility = mMap.get(prefix);
+ if (schemaTypeToVisibility == null) {
+ return Collections.emptySet();
+ }
+ Set<PackageIdentifier> accessiblePackages = schemaTypeToVisibility.get(schemaType);
+ if (accessiblePackages == null) {
+ return Collections.emptySet();
+ }
+ return accessiblePackages;
+ }
+
+ /** Discards all data in the map. */
+ public void clear() {
+ mMap.clear();
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java
new file mode 100644
index 0000000..327ce85
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appsearch.visibilitystore;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Holds the visibility settings that apply to a package's databases.
+ * @hide
+ */
+public class VisibilityDocument extends GenericDocument {
+ /** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */
+ public static final String SCHEMA_TYPE = "VisibilityType";
+
+ /**
+ * Property that holds the list of platform-hidden schemas, as part of the visibility settings.
+ */
+ private static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
+
+ /** Property that holds nested documents of package accessible schemas. */
+ private static final String PACKAGE_ACCESSIBLE_PROPERTY = "packageAccessible";
+
+ /**
+ * Schema for the VisibilityStore's documents.
+ *
+ * <p>NOTE: If you update this, also update
+ * {@link com.android.server.appsearch.external.localstorage.VisibilityStore#SCHEMA_VERSION}
+ */
+ public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
+ .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(
+ NOT_PLATFORM_SURFACEABLE_PROPERTY)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+ .build())
+ .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
+ PACKAGE_ACCESSIBLE_PROPERTY, PackageAccessibleDocument.SCHEMA_TYPE)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+ .build())
+ .build();
+
+ public VisibilityDocument(@NonNull GenericDocument genericDocument) {
+ super(genericDocument);
+ }
+
+ @Nullable
+ public String[] getNotPlatformSurfaceableSchemas() {
+ return getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY);
+ }
+
+ @Nullable
+ public GenericDocument[] getPackageAccessibleSchemas() {
+ return getPropertyDocumentArray(PACKAGE_ACCESSIBLE_PROPERTY);
+ }
+
+ /** Builder for {@link VisibilityDocument}. */
+ public static class Builder extends GenericDocument.Builder<VisibilityDocument.Builder> {
+ public Builder(@NonNull String namespace, @NonNull String id) {
+ super(namespace, id, SCHEMA_TYPE);
+ }
+
+ /** Sets which prefixed schemas have opted out of platform surfacing. */
+ @NonNull
+ public Builder setSchemasNotPlatformSurfaceable(
+ @NonNull String[] notPlatformSurfaceableSchemas) {
+ return setPropertyString(
+ NOT_PLATFORM_SURFACEABLE_PROPERTY, notPlatformSurfaceableSchemas);
+ }
+
+ /** Sets which prefixed schemas have configured package access. */
+ @NonNull
+ public Builder setPackageAccessibleSchemas(
+ @NonNull PackageAccessibleDocument[] packageAccessibleSchemas) {
+ return setPropertyDocument(PACKAGE_ACCESSIBLE_PROPERTY, packageAccessibleSchemas);
+ }
+ }
+}
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index d07a3b9..9bfe071 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ibbf4260deb720ce724be81ee4394ea96181ee0f7
+c6630eba424d98dd54ece674e769d9b0b883e410
diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp
index ec64941..5407cb4 100644
--- a/apex/appsearch/testing/Android.bp
+++ b/apex/appsearch/testing/Android.bp
@@ -33,7 +33,7 @@
visibility: [
"//frameworks/base/core/tests/coretests",
"//cts/hostsidetests/appsearch",
- "//cts/tests/appsearch",
+ "//cts/tests:__subpackages__",
"//vendor:__subpackages__",
],
}
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
index 941cea9..71b4f36 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
@@ -17,6 +17,7 @@
package com.android.server.appsearch.testing;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
@@ -37,6 +38,7 @@
import android.app.appsearch.StorageInfo;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
+import android.os.UserHandle;
import androidx.test.core.app.ApplicationProvider;
@@ -58,18 +60,29 @@
private final AppSearchSession mAppSearchSession;
private final ExecutorService mExecutor;
+ /** Creates the SearchSessionShim with given SearchContext. */
@NonNull
public static ListenableFuture<AppSearchSessionShim> createSearchSession(
@NonNull AppSearchManager.SearchContext searchContext) {
- return createSearchSession(searchContext, Executors.newCachedThreadPool());
+ Context context = ApplicationProvider.getApplicationContext();
+ return createSearchSession(context, searchContext, Executors.newCachedThreadPool());
}
- /** Creates the SearchSession with given ExecutorService. */
+ /** Creates the SearchSessionShim with given SearchContext for the given user. */
@NonNull
public static ListenableFuture<AppSearchSessionShim> createSearchSession(
+ @NonNull AppSearchManager.SearchContext searchContext, @UserIdInt int userId) {
+ Context context = ApplicationProvider.getApplicationContext()
+ .createContextAsUser(new UserHandle(userId), /*flags=*/ 0);
+ return createSearchSession(context, searchContext, Executors.newCachedThreadPool());
+ }
+
+ /** Creates the SearchSession with given Context and ExecutorService. */
+ @NonNull
+ public static ListenableFuture<AppSearchSessionShim> createSearchSession(
+ @NonNull Context context,
@NonNull AppSearchManager.SearchContext searchContext,
@NonNull ExecutorService executor) {
- Context context = ApplicationProvider.getApplicationContext();
AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
SettableFuture<AppSearchResult<AppSearchSession>> future = SettableFuture.create();
appSearchManager.createSearchSession(searchContext, executor, future::set);
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
index a70d9b5..d6ce3eb 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
@@ -30,6 +30,9 @@
* <p>An {@link AppSearchSessionShim} instance provides access to database operations such as
* setting a schema, adding documents, and searching.
*
+ * <p>Instances of this interface are usually obtained from a storage implementation, e.g. {@code
+ * AppSearchManager.createSearchSession()} or {@code PlatformStorage.createSearchSession()}.
+ *
* <p>All implementations of this interface must be thread safe.
*
* @see GlobalSearchSessionShim
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 22ee501..38500af 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -258,8 +258,7 @@
public @NonNull ParcelFileDescriptor openBlob(@NonNull BlobHandle blobHandle)
throws IOException {
try {
- return mService.openBlob(blobHandle, mContext.getOpPackageName(),
- mContext.getAttributionTag());
+ return mService.openBlob(blobHandle, mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
throw new RuntimeException(e);
@@ -316,7 +315,7 @@
@CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
try {
mService.acquireLease(blobHandle, descriptionResId, null, leaseExpiryTimeMillis,
- mContext.getOpPackageName(), mContext.getAttributionTag());
+ mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
e.maybeRethrow(LimitExceededException.class);
@@ -379,7 +378,7 @@
@CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
try {
mService.acquireLease(blobHandle, INVALID_RES_ID, description, leaseExpiryTimeMillis,
- mContext.getOpPackageName(), mContext.getAttributionTag());
+ mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
e.maybeRethrow(LimitExceededException.class);
@@ -498,8 +497,7 @@
*/
public void releaseLease(@NonNull BlobHandle blobHandle) throws IOException {
try {
- mService.releaseLease(blobHandle, mContext.getOpPackageName(),
- mContext.getAttributionTag());
+ mService.releaseLease(blobHandle, mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
throw new RuntimeException(e);
@@ -604,8 +602,7 @@
@Nullable
public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle) throws IOException {
try {
- return mService.getLeaseInfo(blobHandle, mContext.getOpPackageName(),
- mContext.getAttributionTag());
+ return mService.getLeaseInfo(blobHandle, mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
throw new RuntimeException(e);
@@ -900,64 +897,6 @@
}
/**
- * Allow apps with location permission to access this blob data once it is committed using
- * a {@link BlobHandle} representing the blob.
- *
- * <p> This needs to be called before committing the blob using
- * {@link #commit(Executor, Consumer)}.
- *
- * Note that if a caller allows access to the blob using this API in addition to other APIs
- * like {@link #allowPackageAccess(String, byte[])}, then apps satisfying any one of these
- * access conditions will be allowed to access the blob.
- *
- * @param permissionName the name of the location permission that needs to be granted
- * for the app. This can be either one of
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
- *
- * @throws IOException when there is an I/O error while changing the access.
- * @throws SecurityException when the caller is not the owner of the session.
- * @throws IllegalStateException when the caller tries to change access for a blob which is
- * already committed.
- */
- public void allowPackagesWithLocationPermission(@NonNull String permissionName)
- throws IOException {
- try {
- mSession.allowPackagesWithLocationPermission(permissionName);
- } catch (ParcelableException e) {
- e.maybeRethrow(IOException.class);
- throw new RuntimeException(e);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Returns {@code true} if access has been allowed for apps with location permission by
- * using {@link #allowPackagesWithLocationPermission(String)}.
- *
- * @param permissionName the name of the location permission that needs to be granted
- * for the app. This can be either one of
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
- *
- * @throws IOException when there is an I/O error while getting the access type.
- * @throws IllegalStateException when the caller tries to get access type from a session
- * which is closed or abandoned.
- */
- public boolean arePackagesWithLocationPermissionAllowed(@NonNull String permissionName)
- throws IOException {
- try {
- return mSession.arePackagesWithLocationPermissionAllowed(permissionName);
- } catch (ParcelableException e) {
- e.maybeRethrow(IOException.class);
- throw new RuntimeException(e);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Commit the file that was written so far to this session to the blob store maintained by
* the system.
*
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index db6cb5c9..39a9fb4 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -25,13 +25,12 @@
interface IBlobStoreManager {
long createSession(in BlobHandle handle, in String packageName);
IBlobStoreSession openSession(long sessionId, in String packageName);
- ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName,
- in String attributionTag);
+ ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName);
void abandonSession(long sessionId, in String packageName);
void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description,
- long leaseTimeoutMillis, in String packageName, in String attributionTag);
- void releaseLease(in BlobHandle handle, in String packageName, in String attributionTag);
+ long leaseTimeoutMillis, in String packageName);
+ void releaseLease(in BlobHandle handle, in String packageName);
long getRemainingLeaseQuotaBytes(String packageName);
void waitForIdle(in RemoteCallback callback);
@@ -40,6 +39,5 @@
void deleteBlob(long blobId);
List<BlobHandle> getLeasedBlobs(in String packageName);
- LeaseInfo getLeaseInfo(in BlobHandle blobHandle, in String packageName,
- in String attributionTag);
+ LeaseInfo getLeaseInfo(in BlobHandle blobHandle, in String packageName);
}
\ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
index e3ccfb8..4035b96 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
@@ -26,12 +26,10 @@
void allowPackageAccess(in String packageName, in byte[] certificate);
void allowSameSignatureAccess();
void allowPublicAccess();
- void allowPackagesWithLocationPermission(in String permissionName);
boolean isPackageAccessAllowed(in String packageName, in byte[] certificate);
boolean isSameSignatureAccessAllowed();
boolean isPublicAccessAllowed();
- boolean arePackagesWithLocationPermissionAllowed(in String permissionName);
long getSize();
void close();
diff --git a/apex/blobstore/framework/java/android/app/blob/XmlTags.java b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
index 6e4b2f7..bfc5826 100644
--- a/apex/blobstore/framework/java/android/app/blob/XmlTags.java
+++ b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
@@ -38,7 +38,6 @@
public static final String ATTR_TYPE = "t";
public static final String TAG_ALLOWED_PACKAGE = "wl";
public static final String ATTR_CERTIFICATE = "ct";
- public static final String TAG_ALLOWED_PERMISSION = "ap";
// For BlobHandle
public static final String TAG_BLOB_HANDLE = "bh";
@@ -56,7 +55,4 @@
public static final String TAG_LEASEE = "l";
public static final String ATTR_DESCRIPTION_RES_NAME = "rn";
public static final String ATTR_DESCRIPTION = "d";
-
- // Generic
- public static final String ATTR_VALUE = "val";
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
index 09260b7..0d17bbc 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
@@ -15,30 +15,19 @@
*/
package com.android.server.blob;
-import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
-import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.app.blob.XmlTags.ATTR_CERTIFICATE;
import static android.app.blob.XmlTags.ATTR_PACKAGE;
import static android.app.blob.XmlTags.ATTR_TYPE;
-import static android.app.blob.XmlTags.ATTR_VALUE;
import static android.app.blob.XmlTags.TAG_ALLOWED_PACKAGE;
-import static android.app.blob.XmlTags.TAG_ALLOWED_PERMISSION;
-
-import static com.android.server.blob.BlobStoreConfig.TAG;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.permission.PermissionManager;
import android.util.ArraySet;
import android.util.Base64;
import android.util.DebugUtils;
import android.util.IndentingPrintWriter;
-import android.util.Slog;
import com.android.internal.util.XmlUtils;
@@ -64,27 +53,21 @@
ACCESS_TYPE_PUBLIC,
ACCESS_TYPE_SAME_SIGNATURE,
ACCESS_TYPE_ALLOWLIST,
- ACCESS_TYPE_LOCATION_PERMISSION,
})
@interface AccessType {}
public static final int ACCESS_TYPE_PRIVATE = 1 << 0;
public static final int ACCESS_TYPE_PUBLIC = 1 << 1;
public static final int ACCESS_TYPE_SAME_SIGNATURE = 1 << 2;
public static final int ACCESS_TYPE_ALLOWLIST = 1 << 3;
- public static final int ACCESS_TYPE_LOCATION_PERMISSION = 1 << 4;
private int mAccessType = ACCESS_TYPE_PRIVATE;
private final ArraySet<PackageIdentifier> mAllowedPackages = new ArraySet<>();
- private final ArraySet<String> mAllowedPermissions = new ArraySet<>();
void allow(BlobAccessMode other) {
if ((other.mAccessType & ACCESS_TYPE_ALLOWLIST) != 0) {
mAllowedPackages.addAll(other.mAllowedPackages);
}
- if ((other.mAccessType & ACCESS_TYPE_LOCATION_PERMISSION) != 0) {
- mAllowedPermissions.addAll(other.mAllowedPermissions);
- }
mAccessType |= other.mAccessType;
}
@@ -101,11 +84,6 @@
mAllowedPackages.add(PackageIdentifier.create(packageName, certificate));
}
- void allowPackagesWithLocationPermission(@NonNull String permissionName) {
- mAccessType |= ACCESS_TYPE_LOCATION_PERMISSION;
- mAllowedPermissions.add(permissionName);
- }
-
boolean isPublicAccessAllowed() {
return (mAccessType & ACCESS_TYPE_PUBLIC) != 0;
}
@@ -121,15 +99,8 @@
return mAllowedPackages.contains(PackageIdentifier.create(packageName, certificate));
}
- boolean arePackagesWithLocationPermissionAllowed(@NonNull String permissionName) {
- if ((mAccessType & ACCESS_TYPE_LOCATION_PERMISSION) == 0) {
- return false;
- }
- return mAllowedPermissions.contains(permissionName);
- }
-
- boolean isAccessAllowedForCaller(Context context, @NonNull String callingPackage,
- @NonNull String committerPackage, int callingUid, @Nullable String attributionTag) {
+ boolean isAccessAllowedForCaller(Context context,
+ @NonNull String callingPackage, @NonNull String committerPackage) {
if ((mAccessType & ACCESS_TYPE_PUBLIC) != 0) {
return true;
}
@@ -153,37 +124,9 @@
}
}
- if ((mAccessType & ACCESS_TYPE_LOCATION_PERMISSION) != 0) {
- final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- for (int i = 0; i < mAllowedPermissions.size(); ++i) {
- final String permission = mAllowedPermissions.valueAt(i);
- if (PermissionManager.checkPackageNamePermission(permission, callingPackage,
- UserHandle.getUserId(callingUid)) != PackageManager.PERMISSION_GRANTED) {
- continue;
- }
- // TODO: Add appropriate message
- if (appOpsManager.noteOpNoThrow(getAppOp(permission), callingUid, callingPackage,
- attributionTag, null /* message */) == AppOpsManager.MODE_ALLOWED) {
- return true;
- }
- }
- }
-
return false;
}
- private static String getAppOp(String permission) {
- switch (permission) {
- case ACCESS_FINE_LOCATION:
- return AppOpsManager.OPSTR_FINE_LOCATION;
- case ACCESS_COARSE_LOCATION:
- return AppOpsManager.OPSTR_COARSE_LOCATION;
- default:
- Slog.w(TAG, "Unknown permission found: " + permission);
- return null;
- }
- }
-
int getAccessType() {
return mAccessType;
}
@@ -205,16 +148,6 @@
}
fout.decreaseIndent();
}
- fout.print("Allowed permissions:");
- if (mAllowedPermissions.isEmpty()) {
- fout.println(" (Empty)");
- } else {
- fout.increaseIndent();
- for (int i = 0, count = mAllowedPermissions.size(); i < count; ++i) {
- fout.println(mAllowedPermissions.valueAt(i).toString());
- }
- fout.decreaseIndent();
- }
}
void writeToXml(@NonNull XmlSerializer out) throws IOException {
@@ -226,12 +159,6 @@
XmlUtils.writeByteArrayAttribute(out, ATTR_CERTIFICATE, packageIdentifier.certificate);
out.endTag(null, TAG_ALLOWED_PACKAGE);
}
- for (int i = 0, count = mAllowedPermissions.size(); i < count; ++i) {
- out.startTag(null, TAG_ALLOWED_PERMISSION);
- final String permission = mAllowedPermissions.valueAt(i);
- XmlUtils.writeStringAttribute(out, ATTR_VALUE, permission);
- out.endTag(null, TAG_ALLOWED_PERMISSION);
- }
}
@NonNull
@@ -249,10 +176,6 @@
final byte[] certificate = XmlUtils.readByteArrayAttribute(in, ATTR_CERTIFICATE);
blobAccessMode.allowPackageAccess(packageName, certificate);
}
- if (TAG_ALLOWED_PERMISSION.equals(in.getName())) {
- final String permission = XmlUtils.readStringAttribute(in, ATTR_VALUE);
- blobAccessMode.allowPackagesWithLocationPermission(permission);
- }
}
return blobAccessMode;
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index e477156..e116c81 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -263,8 +263,7 @@
return getBlobFile().length();
}
- boolean isAccessAllowedForCaller(@NonNull String callingPackage, int callingUid,
- @Nullable String attributionTag) {
+ boolean isAccessAllowedForCaller(@NonNull String callingPackage, int callingUid) {
// Don't allow the blob to be accessed after it's expiry time has passed.
if (getBlobHandle().isExpired()) {
return false;
@@ -293,7 +292,7 @@
// Check if the caller is allowed access as per the access mode specified
// by the committer.
if (committer.blobAccessMode.isAccessAllowedForCaller(mContext,
- callingPackage, committer.packageName, callingUid, attributionTag)) {
+ callingPackage, committer.packageName)) {
return true;
}
}
@@ -316,7 +315,7 @@
// Check if the caller is allowed access as per the access mode specified
// by the committer.
if (committer.blobAccessMode.isAccessAllowedForCaller(mContext,
- callingPackage, committer.packageName, callingUid, attributionTag)) {
+ callingPackage, committer.packageName)) {
return true;
}
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index cc5e31a..beeffd6 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -101,14 +101,15 @@
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import com.android.server.blob.BlobMetadata.Committer;
import com.android.server.pm.UserManagerInternal;
-import com.android.server.usage.StorageStatsManagerInternal;
-import com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter;
+import com.android.server.usage.StorageStatsManagerLocal;
+import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -208,7 +209,7 @@
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mStatsManager = getContext().getSystemService(StatsManager.class);
registerReceivers();
- LocalServices.getService(StorageStatsManagerInternal.class)
+ LocalManagerRegistry.getManager(StorageStatsManagerLocal.class)
.registerStorageStatsAugmenter(new BlobStorageStatsAugmenter(), TAG);
}
@@ -396,11 +397,11 @@
}
private ParcelFileDescriptor openBlobInternal(BlobHandle blobHandle, int callingUid,
- String callingPackage, String attributionTag) throws IOException {
+ String callingPackage) throws IOException {
synchronized (mBlobsLock) {
final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
- callingPackage, callingUid, attributionTag)) {
+ callingPackage, callingUid)) {
if (blobMetadata == null) {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
INVALID_BLOB_ID, INVALID_BLOB_SIZE,
@@ -448,7 +449,7 @@
private void acquireLeaseInternal(BlobHandle blobHandle, int descriptionResId,
CharSequence description, long leaseExpiryTimeMillis,
- int callingUid, String callingPackage, String attributionTag) {
+ int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
final int leasesCount = getLeasedBlobsCountLocked(callingUid, callingPackage);
if (leasesCount >= getMaxLeasedBlobs()) {
@@ -469,7 +470,7 @@
final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
- callingPackage, callingUid, attributionTag)) {
+ callingPackage, callingUid)) {
if (blobMetadata == null) {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
INVALID_BLOB_ID, INVALID_BLOB_SIZE,
@@ -520,11 +521,11 @@
}
private void releaseLeaseInternal(BlobHandle blobHandle, int callingUid,
- String callingPackage, String attributionTag) {
+ String callingPackage) {
synchronized (mBlobsLock) {
final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
- callingPackage, callingUid, attributionTag)) {
+ callingPackage, callingUid)) {
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
@@ -631,11 +632,11 @@
}
private LeaseInfo getLeaseInfoInternal(BlobHandle blobHandle,
- int callingUid, @NonNull String callingPackage, String attributionTag) {
+ int callingUid, @NonNull String callingPackage) {
synchronized (mBlobsLock) {
final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
- callingPackage, callingUid, attributionTag)) {
+ callingPackage, callingUid)) {
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
@@ -1281,17 +1282,20 @@
private class BlobStorageStatsAugmenter implements StorageStatsAugmenter {
@Override
- public void augmentStatsForPackage(@NonNull PackageStats stats, @NonNull String packageName,
- @UserIdInt int userId, boolean callerHasStatsPermission) {
+ public void augmentStatsForPackageForUser(
+ @NonNull PackageStats stats,
+ @NonNull String packageName,
+ @NonNull UserHandle userHandle,
+ boolean callerHasStatsPermission) {
final AtomicLong blobsDataSize = new AtomicLong(0);
forEachSessionInUser(session -> {
if (session.getOwnerPackageName().equals(packageName)) {
blobsDataSize.getAndAdd(session.getSize());
}
- }, userId);
+ }, userHandle.getIdentifier());
forEachBlob(blobMetadata -> {
- if (blobMetadata.shouldAttributeToLeasee(packageName, userId,
+ if (blobMetadata.shouldAttributeToLeasee(packageName, userHandle.getIdentifier(),
callerHasStatsPermission)) {
blobsDataSize.getAndAdd(blobMetadata.getSize());
}
@@ -1320,6 +1324,22 @@
stats.dataSize += blobsDataSize.get();
}
+
+ @Override
+ public void augmentStatsForUser(
+ @NonNull PackageStats stats, @NonNull UserHandle userHandle) {
+ final AtomicLong blobsDataSize = new AtomicLong(0);
+ forEachSessionInUser(session -> {
+ blobsDataSize.getAndAdd(session.getSize());
+ }, userHandle.getIdentifier());
+
+ // TODO(http://b/187460239): Update this to only include blobs available to userId.
+ forEachBlob(blobMetadata -> {
+ blobsDataSize.getAndAdd(blobMetadata.getSize());
+ });
+
+ stats.dataSize += blobsDataSize.get();
+ }
}
private void forEachSessionInUser(Consumer<BlobStoreSession> consumer, int userId) {
@@ -1458,7 +1478,7 @@
@Override
public ParcelFileDescriptor openBlob(@NonNull BlobHandle blobHandle,
- @NonNull String packageName, @Nullable String attributionTag) {
+ @NonNull String packageName) {
Objects.requireNonNull(blobHandle, "blobHandle must not be null");
blobHandle.assertIsValid();
Objects.requireNonNull(packageName, "packageName must not be null");
@@ -1473,7 +1493,7 @@
}
try {
- return openBlobInternal(blobHandle, callingUid, packageName, attributionTag);
+ return openBlobInternal(blobHandle, callingUid, packageName);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
@@ -1482,8 +1502,7 @@
@Override
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
@Nullable CharSequence description,
- @CurrentTimeSecondsLong long leaseExpiryTimeMillis, @NonNull String packageName,
- @Nullable String attributionTag) {
+ @CurrentTimeSecondsLong long leaseExpiryTimeMillis, @NonNull String packageName) {
Objects.requireNonNull(blobHandle, "blobHandle must not be null");
blobHandle.assertIsValid();
Preconditions.checkArgument(
@@ -1507,7 +1526,7 @@
try {
acquireLeaseInternal(blobHandle, descriptionResId, description,
- leaseExpiryTimeMillis, callingUid, packageName, attributionTag);
+ leaseExpiryTimeMillis, callingUid, packageName);
} catch (Resources.NotFoundException e) {
throw new IllegalArgumentException(e);
} catch (LimitExceededException e) {
@@ -1516,8 +1535,7 @@
}
@Override
- public void releaseLease(@NonNull BlobHandle blobHandle, @NonNull String packageName,
- @Nullable String attributionTag) {
+ public void releaseLease(@NonNull BlobHandle blobHandle, @NonNull String packageName) {
Objects.requireNonNull(blobHandle, "blobHandle must not be null");
blobHandle.assertIsValid();
Objects.requireNonNull(packageName, "packageName must not be null");
@@ -1531,7 +1549,7 @@
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
- releaseLeaseInternal(blobHandle, callingUid, packageName, attributionTag);
+ releaseLeaseInternal(blobHandle, callingUid, packageName);
}
@Override
@@ -1601,8 +1619,7 @@
@Override
@Nullable
- public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle, @NonNull String packageName,
- @Nullable String attributionTag) {
+ public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle, @NonNull String packageName) {
Objects.requireNonNull(blobHandle, "blobHandle must not be null");
blobHandle.assertIsValid();
Objects.requireNonNull(packageName, "packageName must not be null");
@@ -1616,7 +1633,7 @@
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
- return getLeaseInfoInternal(blobHandle, callingUid, packageName, attributionTag);
+ return getLeaseInfoInternal(blobHandle, callingUid, packageName);
}
@Override
@@ -1900,4 +1917,4 @@
return BackgroundThread.getHandler();
}
}
-}
\ No newline at end of file
+}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 3f0032f..8eef8ce 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -15,8 +15,6 @@
*/
package com.android.server.blob;
-import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
-import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.app.blob.BlobStoreManager.COMMIT_RESULT_ERROR;
import static android.app.blob.XmlTags.ATTR_CREATION_TIME_MS;
import static android.app.blob.XmlTags.ATTR_ID;
@@ -368,21 +366,6 @@
}
@Override
- public void allowPackagesWithLocationPermission(@NonNull String permissionName) {
- assertCallerIsOwner();
- Preconditions.checkArgument(ACCESS_FINE_LOCATION.equals(permissionName)
- || ACCESS_COARSE_LOCATION.equals(permissionName),
- "permissionName is unknown: " + permissionName);
- synchronized (mSessionLock) {
- if (mState != STATE_OPENED) {
- throw new IllegalStateException("Not allowed to change access type in state: "
- + stateToString(mState));
- }
- mBlobAccessMode.allowPackagesWithLocationPermission(permissionName);
- }
- }
-
- @Override
public boolean isPackageAccessAllowed(@NonNull String packageName,
@NonNull byte[] certificate) {
assertCallerIsOwner();
@@ -423,21 +406,6 @@
}
@Override
- public boolean arePackagesWithLocationPermissionAllowed(@NonNull String permissionName) {
- assertCallerIsOwner();
- Preconditions.checkArgument(ACCESS_FINE_LOCATION.equals(permissionName)
- || ACCESS_COARSE_LOCATION.equals(permissionName),
- "permissionName is unknown: " + permissionName);
- synchronized (mSessionLock) {
- if (mState != STATE_OPENED) {
- throw new IllegalStateException("Not allowed to change access type in state: "
- + stateToString(mState));
- }
- return mBlobAccessMode.arePackagesWithLocationPermissionAllowed(permissionName);
- }
- }
-
- @Override
public void close() {
closeSession(STATE_CLOSED, false /* sendCallback */);
}
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index b096537..9fa7810 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -152,6 +152,8 @@
* this broadcast will be sent. Applications can reschedule all the necessary alarms when
* receiving it.
*
+ * <p>This broadcast will <em>not</em> be sent when the user revokes the permission.
+ *
* <p><em>Note:</em>
* Applications are still required to check {@link #canScheduleExactAlarms()}
* before using the above APIs after receiving this broadcast,
@@ -530,9 +532,10 @@
* modest timeliness requirements for its alarms.
*
* <p>
- * Note: Starting with API {@link Build.VERSION_CODES#S}, the system will ensure that the window
- * specified is at least a few minutes, as smaller windows are considered practically exact
- * and should use the other APIs provided for exact alarms.
+ * Note: Starting with API {@link Build.VERSION_CODES#S}, apps should not pass in a window of
+ * less than 10 minutes. The system will try its best to accommodate smaller windows if the
+ * alarm is supposed to fire in the near future, but there are no guarantees and the app should
+ * expect any window smaller than 10 minutes to get elongated to 10 minutes.
*
* <p>
* This method can also be used to achieve strict ordering guarantees among
@@ -586,9 +589,10 @@
* if {@code null} is passed as the {@code targetHandler} parameter.
*
* <p>
- * Note: Starting with API {@link Build.VERSION_CODES#S}, the system will ensure that the window
- * specified is at least a few minutes, as smaller windows are considered practically exact
- * and should use the other APIs provided for exact alarms.
+ * Note: Starting with API {@link Build.VERSION_CODES#S}, apps should not pass in a window of
+ * less than 10 minutes. The system will try its best to accommodate smaller windows if the
+ * alarm is supposed to fire in the near future, but there are no guarantees and the app should
+ * expect any window smaller than 10 minutes to get elongated to 10 minutes.
*
* @see #setWindow(int, long, long, PendingIntent)
*/
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 9dd1296..42e953b 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -344,6 +344,9 @@
*/
public static final int REASON_MEDIA_SESSION_CALLBACK = 317;
+ /** @hide The app requests out-out. */
+ public static final int REASON_OPT_OUT_REQUESTED = 1000;
+
/**
* The list of BG-FGS-Launch and temp-allow-list reason code.
* @hide
@@ -411,6 +414,7 @@
REASON_EVENT_MMS,
REASON_SHELL,
REASON_MEDIA_SESSION_CALLBACK,
+ REASON_OPT_OUT_REQUESTED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ReasonCode {}
@@ -709,6 +713,8 @@
return "SHELL";
case REASON_MEDIA_SESSION_CALLBACK:
return "MEDIA_SESSION_CALLBACK";
+ case REASON_OPT_OUT_REQUESTED:
+ return "REASON_OPT_OUT_REQUESTED";
default:
return "(unknown:" + reasonCode + ")";
}
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 0eb2609..5365218 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -530,8 +530,7 @@
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
private static final long DEFAULT_MAX_INTERVAL = 365 * INTERVAL_DAY;
- // TODO (b/185199076): Tune based on breakage reports.
- private static final long DEFAULT_MIN_WINDOW = 30 * 60 * 1000;
+ private static final long DEFAULT_MIN_WINDOW = 10 * 60 * 1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000;
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
@@ -1147,6 +1146,18 @@
return when;
}
+ /**
+ * This is the minimum window that can be requested for the given alarm. Windows smaller than
+ * this value will be elongated to match it.
+ * Current heuristic is similar to {@link #maxTriggerTime(long, long, long)}, the minimum
+ * allowed window is either {@link Constants#MIN_WINDOW} or 75% of the alarm's futurity,
+ * whichever is smaller.
+ */
+ long getMinimumAllowedWindow(long nowElapsed, long triggerElapsed) {
+ final long futurity = triggerElapsed - nowElapsed;
+ return Math.min((long) (futurity * 0.75), mConstants.MIN_WINDOW);
+ }
+
// Apply a heuristic to { recurrence interval, futurity of the trigger time } to
// calculate the end of our nominal delivery window for the alarm.
static long maxTriggerTime(long now, long triggerAtTime, long interval) {
@@ -1833,25 +1844,6 @@
}
}
- // Snap the window to reasonable limits.
- if (windowLength > INTERVAL_DAY) {
- Slog.w(TAG, "Window length " + windowLength
- + "ms suspiciously long; limiting to 1 day");
- windowLength = INTERVAL_DAY;
- } else if (windowLength > 0 && windowLength < mConstants.MIN_WINDOW
- && (flags & FLAG_PRIORITIZE) == 0) {
- if (CompatChanges.isChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS,
- callingPackage, UserHandle.getUserHandleForUid(callingUid))) {
- Slog.w(TAG, "Window length " + windowLength + "ms too short; expanding to "
- + mConstants.MIN_WINDOW + "ms.");
- windowLength = mConstants.MIN_WINDOW;
- } else {
- // TODO (b/185199076): Remove log once we have some data about what apps will break
- Slog.wtf(TAG, "Short window " + windowLength + "ms specified by "
- + callingPackage);
- }
- }
-
// Sanity check the recurrence interval. This will catch people who supply
// seconds when the API expects milliseconds, or apps trying shenanigans
// around intentional period overflow, etc.
@@ -1883,7 +1875,7 @@
// Try to prevent spamming by making sure apps aren't firing alarms in the immediate future
final long minTrigger = nowElapsed
+ (UserHandle.isCore(callingUid) ? 0L : mConstants.MIN_FUTURITY);
- final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger;
+ final long triggerElapsed = Math.max(minTrigger, nominalTrigger);
final long maxElapsed;
if (windowLength == 0) {
@@ -1893,6 +1885,25 @@
// Fix this window in place, so that as time approaches we don't collapse it.
windowLength = maxElapsed - triggerElapsed;
} else {
+ // The window was explicitly requested. Snap it to allowable limits.
+ final long minAllowedWindow = getMinimumAllowedWindow(nowElapsed, triggerElapsed);
+ if (windowLength > INTERVAL_DAY) {
+ Slog.w(TAG, "Window length " + windowLength + "ms too long; limiting to 1 day");
+ windowLength = INTERVAL_DAY;
+ } else if ((flags & FLAG_PRIORITIZE) == 0 && windowLength < minAllowedWindow) {
+ // Prioritized alarms are exempt from minimum window limits.
+ if (CompatChanges.isChangeEnabled(
+ AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, callingPackage,
+ UserHandle.getUserHandleForUid(callingUid))) {
+ Slog.w(TAG, "Window length " + windowLength + "ms too short; expanding to "
+ + minAllowedWindow + "ms.");
+ windowLength = minAllowedWindow;
+ } else {
+ // TODO (b/185199076): Remove temporary log to catch breaking apps.
+ Slog.wtf(TAG, "Short window " + windowLength + "ms specified by "
+ + callingPackage);
+ }
+ }
maxElapsed = triggerElapsed + windowLength;
}
synchronized (mLock) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 452be30..96cbed7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2629,8 +2629,7 @@
}
} catch (NameNotFoundException e) {
throw new IllegalArgumentException(
- "Tried to schedule job for non-existent package: "
- + service.getPackageName());
+ "Tried to schedule job for non-existent component: " + service);
}
}
diff --git a/api/Android.bp b/api/Android.bp
index 5b73388..b85dc46 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,12 +24,17 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+metalava_cmd = "$(location metalava)"
+// Silence reflection warnings. See b/168689341
+metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
+metalava_cmd += " --no-banner --format=v2 "
+
genrule {
name: "current-api-xml",
tools: ["metalava"],
srcs: [":frameworks-base-api-current.txt"],
out: ["current.api"],
- cmd: "$(location metalava) --no-banner -convert2xmlnostrip $(in) $(out)",
+ cmd: metalava_cmd + "-convert2xmlnostrip $(in) $(out)",
visibility: ["//visibility:public"],
}
@@ -56,7 +61,7 @@
],
out: ["current.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ cmd: metalava_cmd + "$(in) --api $(out)",
dists: [
{
targets: ["droidcore"],
@@ -81,7 +86,7 @@
],
out: ["stdout.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 " +
+ cmd: metalava_cmd +
"--check-compatibility:api:released $(location :android.api.public.latest) " +
"--baseline:compatibility:released $(location :android-incompatibilities.api.public.latest) " +
"$(location :frameworks-base-api-current.txt) " +
@@ -138,7 +143,7 @@
],
out: ["removed.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ cmd: metalava_cmd + "$(in) --api $(out)",
dists: [
{
targets: ["droidcore"],
@@ -173,7 +178,7 @@
],
out: ["system-current.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ cmd: metalava_cmd + "$(in) --api $(out)",
dists: [
{
targets: ["droidcore"],
@@ -199,7 +204,7 @@
],
out: ["stdout.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 " +
+ cmd: metalava_cmd +
"--check-compatibility:api:released $(location :android.api.system.latest) " +
"--check-compatibility:base $(location :frameworks-base-api-current.txt) " +
"--baseline:compatibility:released $(location :android-incompatibilities.api.system.latest) " +
@@ -227,7 +232,7 @@
],
out: ["system-removed.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ cmd: metalava_cmd + "$(in) --api $(out)",
dists: [
{
targets: ["droidcore"],
@@ -263,7 +268,7 @@
],
out: ["module-lib-current.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ cmd: metalava_cmd + "$(in) --api $(out)",
dists: [
{
targets: ["droidcore"],
@@ -288,7 +293,7 @@
],
out: ["stdout.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 " +
+ cmd: metalava_cmd +
"--check-compatibility:api:released $(location :android.api.module-lib.latest) " +
// Note: having "public" be the base of module-lib is not perfect -- it should
// ideally be a merged public+system), but this will help when migrating from
@@ -319,7 +324,7 @@
],
out: ["module-lib-removed.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ cmd: metalava_cmd + "$(in) --api $(out)",
dists: [
{
targets: ["droidcore"],
@@ -360,7 +365,7 @@
],
out: ["system-server-current.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ cmd: metalava_cmd + "$(in) --api $(out)",
dists: [
{
targets: ["droidcore"],
@@ -384,7 +389,7 @@
],
out: ["system-server-removed.txt"],
tools: ["metalava"],
- cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ cmd: metalava_cmd + "$(in) --api $(out)",
dists: [
{
targets: ["droidcore"],
diff --git a/build/boot/boot-image-profile.txt b/boot/boot-image-profile.txt
similarity index 100%
rename from build/boot/boot-image-profile.txt
rename to boot/boot-image-profile.txt
diff --git a/build/boot/preloaded-classes b/boot/preloaded-classes
similarity index 100%
rename from build/boot/preloaded-classes
rename to boot/preloaded-classes
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index f5bee6c..260c8a4 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -258,7 +258,7 @@
public void runDisableAppDataIsolation() throws RemoteException {
if (!SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
throw new IllegalStateException("Storage app data isolation is not enabled.");
}
final String pkgName = nextArg();
diff --git a/core/api/current.txt b/core/api/current.txt
index 324ad33..f1241df 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -245,6 +245,7 @@
public static final class R.attr {
ctor public R.attr();
+ field public static final int __removed3;
field public static final int absListViewStyle = 16842858; // 0x101006a
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -3172,6 +3173,7 @@
field public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 128; // 0x80
field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+ field public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 8192; // 0x2000
field public static final int FLAG_REQUEST_ACCESSIBILITY_BUTTON = 256; // 0x100
field @Deprecated public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
@@ -7903,10 +7905,8 @@
public static class BlobStoreManager.Session implements java.io.Closeable {
method public void abandon() throws java.io.IOException;
method public void allowPackageAccess(@NonNull String, @NonNull byte[]) throws java.io.IOException;
- method public void allowPackagesWithLocationPermission(@NonNull String) throws java.io.IOException;
method public void allowPublicAccess() throws java.io.IOException;
method public void allowSameSignatureAccess() throws java.io.IOException;
- method public boolean arePackagesWithLocationPermissionAllowed(@NonNull String) throws java.io.IOException;
method public void close() throws java.io.IOException;
method public void commit(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws java.io.IOException;
method public long getSize() throws java.io.IOException;
@@ -11154,6 +11154,7 @@
field public static final String ACTION_VIEW = "android.intent.action.VIEW";
field public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE = "android.intent.action.VIEW_PERMISSION_USAGE";
+ field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD = "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
field public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
field @Deprecated public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
field public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
@@ -11206,6 +11207,7 @@
field public static final String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
field public static final String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
field public static final String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+ field public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
field public static final String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE = "android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
field public static final String EXTRA_BCC = "android.intent.extra.BCC";
field public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
@@ -11231,6 +11233,7 @@
field public static final String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
field public static final String EXTRA_DURATION_MILLIS = "android.intent.extra.DURATION_MILLIS";
field public static final String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+ field public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
field public static final String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
field public static final String EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE";
field public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
@@ -11245,6 +11248,7 @@
field public static final String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
field public static final String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
field public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+ field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
field public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
field public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
field public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -11267,6 +11271,7 @@
field @Deprecated public static final String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
field public static final String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
field public static final String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
+ field public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
field public static final String EXTRA_STREAM = "android.intent.extra.STREAM";
field public static final String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
field public static final String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS";
@@ -11875,7 +11880,6 @@
field public static final int SCREEN_ORIENTATION_USER_LANDSCAPE = 11; // 0xb
field public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12; // 0xc
field public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1; // 0x1
- field public String[] attributionTags;
field public int colorMode;
field public int configChanges;
field public int documentLaunchMode;
@@ -12067,6 +12071,7 @@
method public final int getLogoResource();
method public boolean isEnabled();
field public android.content.pm.ApplicationInfo applicationInfo;
+ field public String[] attributionTags;
field public int descriptionRes;
field public boolean directBootAware;
field public boolean enabled;
@@ -13079,7 +13084,6 @@
method public void reportShortcutUsed(String);
method @WorkerThread public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender);
method @WorkerThread public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
- method public void updateShortcutVisibility(@NonNull String, @Nullable byte[], boolean);
method @WorkerThread public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
field public static final int FLAG_MATCH_CACHED = 8; // 0x8
field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
@@ -39219,7 +39223,8 @@
method @MainThread public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context);
method @MainThread public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context, android.content.ComponentName);
method public void destroy();
- method public static boolean isRecognitionAvailable(android.content.Context);
+ method public static boolean isOnDeviceRecognitionAvailable(@NonNull android.content.Context);
+ method public static boolean isRecognitionAvailable(@NonNull android.content.Context);
method @MainThread public void setRecognitionListener(android.speech.RecognitionListener);
method @MainThread public void startListening(android.content.Intent);
method @MainThread public void stopListening();
@@ -46814,6 +46819,11 @@
method public void onActionProviderVisibilityChanged(boolean);
}
+ @UiThread public interface AttachedSurfaceControl {
+ method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction);
+ method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
+ }
+
public final class Choreographer {
method public static android.view.Choreographer getInstance();
method public void postFrameCallback(android.view.Choreographer.FrameCallback);
@@ -48540,6 +48550,7 @@
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
+ method public void dispatchCreateViewTranslationRequest(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @NonNull android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
method public void dispatchDisplayHint(int);
method public boolean dispatchDragEvent(android.view.DragEvent);
method protected void dispatchDraw(android.graphics.Canvas);
@@ -48561,7 +48572,6 @@
method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void dispatchProvideAutofillStructure(@NonNull android.view.ViewStructure, int);
method public void dispatchProvideStructure(android.view.ViewStructure);
- method public void dispatchRequestTranslation(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @NonNull android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
method public void dispatchScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>);
@@ -48714,6 +48724,7 @@
method public final int getRight();
method protected float getRightFadingEdgeStrength();
method protected int getRightPaddingOffset();
+ method @Nullable public android.view.AttachedSurfaceControl getRootSurfaceControl();
method public android.view.View getRootView();
method public android.view.WindowInsets getRootWindowInsets();
method public float getRotation();
@@ -48758,7 +48769,6 @@
method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarThumbDrawable();
method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarTrackDrawable();
method public int getVerticalScrollbarWidth();
- method @Nullable public android.view.ViewRoot getViewRoot();
method @Nullable public android.view.translation.ViewTranslationResponse getViewTranslationResponse();
method public android.view.ViewTreeObserver getViewTreeObserver();
method public int getVisibility();
@@ -49834,11 +49844,6 @@
method public android.view.ViewPropertyAnimator zBy(float);
}
- @UiThread public interface ViewRoot {
- method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction);
- method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
- }
-
public abstract class ViewStructure {
ctor public ViewStructure();
method public abstract int addChildCount(int);
@@ -50025,6 +50030,7 @@
method @ColorInt public int getNavigationBarDividerColor();
method public android.transition.Transition getReenterTransition();
method public android.transition.Transition getReturnTransition();
+ method @Nullable public android.view.AttachedSurfaceControl getRootSurfaceControl();
method public android.transition.Transition getSharedElementEnterTransition();
method public android.transition.Transition getSharedElementExitTransition();
method public android.transition.Transition getSharedElementReenterTransition();
@@ -50034,7 +50040,6 @@
method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects();
method public long getTransitionBackgroundFadeDuration();
method public android.transition.TransitionManager getTransitionManager();
- method @Nullable public android.view.ViewRoot getViewRoot();
method public abstract int getVolumeControlStream();
method public android.view.WindowManager getWindowManager();
method public final android.content.res.TypedArray getWindowStyle();
@@ -51978,6 +51983,7 @@
method public int getSubtypeCount();
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
+ method public boolean shouldShowInInputMethodPicker();
method public boolean suppressesSpellChecker();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
@@ -52841,7 +52847,7 @@
method public int getTranslationFlags();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationContext> CREATOR;
- field public static final int FLAG_DICTIONARY_DESCRIPTION = 4; // 0x4
+ field public static final int FLAG_DEFINITIONS = 4; // 0x4
field public static final int FLAG_LOW_LATENCY = 1; // 0x1
field public static final int FLAG_TRANSLITERATION = 2; // 0x2
}
@@ -52883,8 +52889,8 @@
public static final class TranslationRequest.Builder {
ctor public TranslationRequest.Builder();
- method @NonNull public android.view.translation.TranslationRequest.Builder addTranslationRequestValue(@NonNull android.view.translation.TranslationRequestValue);
- method @NonNull public android.view.translation.TranslationRequest.Builder addViewTranslationRequest(@NonNull android.view.translation.ViewTranslationRequest);
+ method @Deprecated @NonNull public android.view.translation.TranslationRequest.Builder addTranslationRequestValue(@NonNull android.view.translation.TranslationRequestValue);
+ method @Deprecated @NonNull public android.view.translation.TranslationRequest.Builder addViewTranslationRequest(@NonNull android.view.translation.ViewTranslationRequest);
method @NonNull public android.view.translation.TranslationRequest build();
method @NonNull public android.view.translation.TranslationRequest.Builder setFlags(int);
method @NonNull public android.view.translation.TranslationRequest.Builder setTranslationRequestValues(@NonNull java.util.List<android.view.translation.TranslationRequestValue>);
@@ -52894,7 +52900,7 @@
public final class TranslationRequestValue implements android.os.Parcelable {
method public int describeContents();
method @NonNull public static android.view.translation.TranslationRequestValue forText(@NonNull CharSequence);
- method @NonNull public CharSequence getText();
+ method @Nullable public CharSequence getText();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationRequestValue> CREATOR;
}
@@ -52926,12 +52932,13 @@
public final class TranslationResponseValue implements android.os.Parcelable {
method public int describeContents();
method @NonNull public static android.view.translation.TranslationResponseValue forError();
- method @Nullable public CharSequence getDictionaryDescription();
+ method @NonNull public android.os.Bundle getExtras();
method public int getStatusCode();
method @Nullable public CharSequence getText();
method @Nullable public CharSequence getTransliteration();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationResponseValue> CREATOR;
+ field public static final String EXTRA_DEFINITIONS = "android.view.translation.extra.DEFINITIONS";
field public static final int STATUS_ERROR = 1; // 0x1
field public static final int STATUS_SUCCESS = 0; // 0x0
}
@@ -52939,7 +52946,7 @@
public static final class TranslationResponseValue.Builder {
ctor public TranslationResponseValue.Builder(int);
method @NonNull public android.view.translation.TranslationResponseValue build();
- method @NonNull public android.view.translation.TranslationResponseValue.Builder setDictionaryDescription(@NonNull CharSequence);
+ method @NonNull public android.view.translation.TranslationResponseValue.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.view.translation.TranslationResponseValue.Builder setText(@NonNull CharSequence);
method @NonNull public android.view.translation.TranslationResponseValue.Builder setTransliteration(@NonNull CharSequence);
}
@@ -52971,7 +52978,7 @@
method public void onFinished();
method public void onPaused();
method public default void onResumed(@NonNull android.icu.util.ULocale, @NonNull android.icu.util.ULocale);
- method @Deprecated public void onStarted(@NonNull String, @NonNull String);
+ method @Deprecated public default void onStarted(@NonNull String, @NonNull String);
method public default void onStarted(@NonNull android.icu.util.ULocale, @NonNull android.icu.util.ULocale);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 96a23b2..95d9119 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -211,6 +211,7 @@
field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
+ field public static final String READ_GLOBAL_APP_SEARCH_DATA = "android.permission.READ_GLOBAL_APP_SEARCH_DATA";
field public static final String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
field public static final String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY";
field public static final String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE";
@@ -317,7 +318,6 @@
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
- field public static final int durationBetweenRequestsMillis;
field public static final int hotwordDetectionService;
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int minExtensionVersion = 16844305; // 0x1010611
@@ -2399,12 +2399,9 @@
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
- field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD = "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
- field public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
- field public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
field public static final String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET";
field public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
field public static final String EXTRA_INSTANT_APP_BUNDLES = "android.intent.extra.INSTANT_APP_BUNDLES";
@@ -2416,13 +2413,11 @@
field public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
field public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
field public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
- field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
field public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
field public static final String EXTRA_REASON = "android.intent.extra.REASON";
field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
- field public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000
@@ -5001,11 +4996,15 @@
public final class SatellitePvt implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public android.location.SatellitePvt.ClockInfo getClockInfo();
+ method @Nullable public android.location.SatellitePvt.ClockInfo getClockInfo();
+ method public int getFlags();
method @FloatRange public double getIonoDelayMeters();
- method @NonNull public android.location.SatellitePvt.PositionEcef getPositionEcef();
+ method @Nullable public android.location.SatellitePvt.PositionEcef getPositionEcef();
method @FloatRange public double getTropoDelayMeters();
- method @NonNull public android.location.SatellitePvt.VelocityEcef getVelocityEcef();
+ method @Nullable public android.location.SatellitePvt.VelocityEcef getVelocityEcef();
+ method public boolean hasIono();
+ method public boolean hasPositionVelocityClockInfo();
+ method public boolean hasTropo();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.SatellitePvt> CREATOR;
}
@@ -5014,6 +5013,7 @@
ctor public SatellitePvt.Builder();
method @NonNull public android.location.SatellitePvt build();
method @NonNull public android.location.SatellitePvt.Builder setClockInfo(@NonNull android.location.SatellitePvt.ClockInfo);
+ method @NonNull public android.location.SatellitePvt.Builder setFlags(int);
method @NonNull public android.location.SatellitePvt.Builder setIonoDelayMeters(@FloatRange double);
method @NonNull public android.location.SatellitePvt.Builder setPositionEcef(@NonNull android.location.SatellitePvt.PositionEcef);
method @NonNull public android.location.SatellitePvt.Builder setTropoDelayMeters(@FloatRange double);
@@ -9893,9 +9893,9 @@
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onGenerateDisplayHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String, @NonNull android.view.displayhash.DisplayHashResultCallback);
method @NonNull public abstract java.util.Map<java.lang.String,android.service.displayhash.DisplayHashParams> onGetDisplayHashAlgorithms();
+ method public abstract int onGetIntervalBetweenRequestsMillis();
method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash);
field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHashingService";
- field public static final String SERVICE_META_DATA = "android.displayhash.display_hashing_service";
}
}
@@ -10410,19 +10410,20 @@
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public void onConnected();
method public void onCreateTranslationSession(@NonNull android.view.translation.TranslationContext, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @Deprecated public abstract void onCreateTranslationSession(@NonNull android.view.translation.TranslationContext, int);
+ method @Deprecated public void onCreateTranslationSession(@NonNull android.view.translation.TranslationContext, int);
method public void onDisconnected();
method public abstract void onFinishTranslationSession(int);
method public abstract void onTranslationCapabilitiesRequest(int, int, @NonNull java.util.function.Consumer<java.util.Set<android.view.translation.TranslationCapability>>);
- method public abstract void onTranslationRequest(@NonNull android.view.translation.TranslationRequest, int, @Nullable android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback);
+ method @Deprecated public void onTranslationRequest(@NonNull android.view.translation.TranslationRequest, int, @Nullable android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback);
+ method public void onTranslationRequest(@NonNull android.view.translation.TranslationRequest, int, @Nullable android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.view.translation.TranslationResponse>);
method public final void updateTranslationCapability(@NonNull android.view.translation.TranslationCapability);
field public static final String SERVICE_INTERFACE = "android.service.translation.TranslationService";
field public static final String SERVICE_META_DATA = "android.translation_service";
}
- public static interface TranslationService.OnTranslationResultCallback {
+ @Deprecated public static interface TranslationService.OnTranslationResultCallback {
method @Deprecated public void onError();
- method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse);
+ method @Deprecated public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse);
}
}
@@ -10473,7 +10474,7 @@
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int);
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int);
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
- method @Nullable public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
+ method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition();
method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
@@ -10498,7 +10499,7 @@
method public abstract void onAvailabilityChanged(int);
method public void onHotwordDetectionServiceInitialized(int);
method public void onHotwordDetectionServiceRestarted();
- method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
+ method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
}
public static class AlwaysOnHotwordDetector.EventPayload {
@@ -10544,6 +10545,7 @@
public abstract class HotwordDetectionService extends android.app.Service {
ctor public HotwordDetectionService();
+ method public static int getMaxCustomInitializationStatus();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method @Deprecated public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.Callback);
method public void onDetect(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload, long, @NonNull android.service.voice.HotwordDetectionService.Callback);
@@ -10551,16 +10553,14 @@
method public void onDetect(@NonNull android.service.voice.HotwordDetectionService.Callback);
method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle, @NonNull android.service.voice.HotwordDetectionService.Callback);
method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer);
- field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1; // 0x1
- field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_2 = 2; // 0x2
field public static final int INITIALIZATION_STATUS_SUCCESS = 0; // 0x0
field public static final int INITIALIZATION_STATUS_UNKNOWN = 100; // 0x64
field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService";
}
public static final class HotwordDetectionService.Callback {
- method public void onDetected(@Nullable android.service.voice.HotwordDetectedResult);
- method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
+ method public void onDetected(@NonNull android.service.voice.HotwordDetectedResult);
+ method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
}
public interface HotwordDetector {
@@ -10581,7 +10581,7 @@
method public void onHotwordDetectionServiceRestarted();
method public void onRecognitionPaused();
method public void onRecognitionResumed();
- method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
+ method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
}
public final class HotwordRejectedResult implements android.os.Parcelable {
@@ -14733,7 +14733,7 @@
method public String getDataDirectorySuffix();
method public String getErrorString(android.content.Context, int);
method public int getPackageId(android.content.res.Resources, String);
- method @NonNull public long[] getTimestamps();
+ method @NonNull public android.webkit.WebViewFactory.StartupTimestamps getStartupTimestamps();
method @Deprecated public void invokeDrawGlFunctor(android.view.View, long, boolean);
method public boolean isMultiProcessEnabled();
method public boolean isTraceTagEnabled();
@@ -14749,12 +14749,6 @@
method public static android.content.pm.PackageInfo getLoadedPackageInfo();
method public static int loadWebViewNativeLibraryFromPackage(String, ClassLoader);
method public static void prepareWebViewInZygote();
- field public static final int ADD_ASSETS_END = 4; // 0x4
- field public static final int ADD_ASSETS_START = 3; // 0x3
- field public static final int CREATE_CONTEXT_END = 2; // 0x2
- field public static final int CREATE_CONTEXT_START = 1; // 0x1
- field public static final int GET_CLASS_LOADER_END = 6; // 0x6
- field public static final int GET_CLASS_LOADER_START = 5; // 0x5
field public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; // 0x2
field public static final int LIBLOAD_FAILED_JNI_CALL = 7; // 0x7
field public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; // 0x4
@@ -14765,11 +14759,20 @@
field public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8; // 0x8
field public static final int LIBLOAD_SUCCESS = 0; // 0x0
field public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; // 0x1
- field public static final int NATIVE_LOAD_END = 8; // 0x8
- field public static final int NATIVE_LOAD_START = 7; // 0x7
- field public static final int PROVIDER_CLASS_FOR_NAME_END = 10; // 0xa
- field public static final int PROVIDER_CLASS_FOR_NAME_START = 9; // 0x9
- field public static final int WEBVIEW_LOAD_START = 0; // 0x0
+ }
+
+ public static class WebViewFactory.StartupTimestamps {
+ method public long getAddAssetsEnd();
+ method public long getAddAssetsStart();
+ method public long getCreateContextEnd();
+ method public long getCreateContextStart();
+ method public long getGetClassLoaderEnd();
+ method public long getGetClassLoaderStart();
+ method public long getNativeLoadEnd();
+ method public long getNativeLoadStart();
+ method public long getProviderClassForNameEnd();
+ method public long getProviderClassForNameStart();
+ method public long getWebViewLoadStart();
}
public interface WebViewFactoryProvider {
@@ -14919,6 +14922,7 @@
public static interface WebViewProvider.ViewDelegate {
method public default void autofill(android.util.SparseArray<android.view.autofill.AutofillValue>);
+ method public default void dispatchCreateViewTranslationRequest(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @Nullable android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
method public boolean dispatchKeyEvent(android.view.KeyEvent);
method public android.view.View findFocus(android.view.View);
method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d366e35d..12d9bee 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -50,6 +50,10 @@
field public static final String UNDEFINED = "android.permission-group.UNDEFINED";
}
+ public static final class R.attr {
+ field public static final int requestForegroundServiceExemption;
+ }
+
public static final class R.bool {
field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
@@ -452,7 +456,7 @@
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
- method public void forceUpdateUserSetupComplete();
+ method public void forceUpdateUserSetupComplete(int);
method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String);
method public long getLastBugReportRequestTime();
@@ -806,6 +810,7 @@
}
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ method public boolean hasRequestForegroundServiceExemption();
method public boolean isPrivilegedApp();
method public boolean isSystemApp();
field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8
@@ -860,6 +865,10 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ProviderInfoList> CREATOR;
}
+ public final class SharedLibraryInfo implements android.os.Parcelable {
+ method @NonNull public java.util.List<java.lang.String> getAllCodePaths();
+ }
+
public final class ShortcutInfo implements android.os.Parcelable {
method public boolean isVisibleToPublisher();
}
@@ -2094,6 +2103,7 @@
public final class DeviceConfig {
field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
field public static final String NAMESPACE_ANDROID = "android";
+ field public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
}
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 793d0ef..6c001f3 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -169,6 +169,7 @@
filegroup {
name: "framework-jobscheduler-shared-srcs",
srcs: [
+ ":modules-utils-preconditions-srcs",
"com/android/internal/util/ArrayUtils.java",
"com/android/internal/util/BitUtils.java",
"com/android/internal/util/CollectionUtils.java",
@@ -178,7 +179,6 @@
"com/android/internal/util/FastXmlSerializer.java",
"com/android/internal/util/FunctionalUtils.java",
"com/android/internal/util/ParseUtils.java",
- "com/android/internal/util/Preconditions.java",
"com/android/internal/util/RingBufferIndices.java",
"com/android/internal/util/StatLogger.java",
"com/android/internal/util/XmlUtils.java",
@@ -189,9 +189,9 @@
filegroup {
name: "framework-permission-s-shared-srcs",
srcs: [
+ ":modules-utils-preconditions-srcs",
"com/android/internal/infra/AndroidFuture.java",
"com/android/internal/infra/ServiceConnector.java",
- "com/android/internal/util/Preconditions.java",
"com/android/internal/infra/AndroidFuture.aidl",
"com/android/internal/infra/IAndroidFuture.aidl",
"android/os/HandlerExecutor.java",
@@ -323,6 +323,7 @@
filegroup {
name: "framework-telephony-common-shared-srcs",
srcs: [
+ ":modules-utils-preconditions-srcs",
"android/os/RegistrantList.java",
"android/os/Registrant.java",
"android/util/IndentingPrintWriter.java",
@@ -336,7 +337,6 @@
"com/android/internal/util/HexDump.java",
"com/android/internal/util/IState.java",
"com/android/internal/util/IndentingPrintWriter.java",
- "com/android/internal/util/Preconditions.java",
"com/android/internal/util/State.java",
"com/android/internal/util/StateMachine.java",
"com/android/internal/util/UserIcons.java",
@@ -348,10 +348,10 @@
filegroup {
name: "framework-cellbroadcast-shared-srcs",
srcs: [
+ ":modules-utils-preconditions-srcs",
"android/os/HandlerExecutor.java",
"android/util/LocalLog.java",
"com/android/internal/util/IState.java",
- "com/android/internal/util/Preconditions.java",
"com/android/internal/util/State.java",
"com/android/internal/util/StateMachine.java",
],
@@ -360,10 +360,10 @@
filegroup {
name: "framework-ims-common-shared-srcs",
srcs: [
+ ":modules-utils-preconditions-srcs",
"android/os/RegistrantList.java",
"android/os/Registrant.java",
"com/android/internal/os/SomeArgs.java",
- "com/android/internal/util/Preconditions.java",
],
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 01ea026..04c784e 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -373,7 +373,6 @@
* #FLAG_REQUEST_MULTI_FINGER_GESTURES} is disabled this flag has no effect.
*
* @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE
- * @hide
*/
public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x0002000;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 198fa65..854c9f2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -136,6 +136,7 @@
import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationController;
+import android.view.translation.UiTranslationSpec;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -969,8 +970,7 @@
private UiTranslationController mUiTranslationController;
private SplashScreen mSplashScreen;
- /** @hide */
- SplashScreenView mSplashScreenView;
+ private SplashScreenView mSplashScreenView;
private final WindowControllerCallback mWindowControllerCallback =
new WindowControllerCallback() {
@@ -1630,16 +1630,14 @@
}
}
- /**
- * Clear the splash screen view if exist.
- * @hide
- */
- public void detachSplashScreenView() {
- synchronized (this) {
- if (mSplashScreenView != null) {
- mSplashScreenView = null;
- }
- }
+ /** @hide */
+ public void setSplashScreenView(SplashScreenView v) {
+ mSplashScreenView = v;
+ }
+
+ /** @hide */
+ SplashScreenView getSplashScreenView() {
+ return mSplashScreenView;
}
/**
@@ -8815,11 +8813,13 @@
* @hide
*/
public void updateUiTranslationState(int state, TranslationSpec sourceSpec,
- TranslationSpec targetSpec, List<AutofillId> viewIds) {
+ TranslationSpec targetSpec, List<AutofillId> viewIds,
+ UiTranslationSpec uiTranslationSpec) {
if (mUiTranslationController == null) {
mUiTranslationController = new UiTranslationController(this, getApplicationContext());
}
- mUiTranslationController.updateUiTranslationState(state, sourceSpec, targetSpec, viewIds);
+ mUiTranslationController.updateUiTranslationState(
+ state, sourceSpec, targetSpec, viewIds, uiTranslationSpec);
}
class HostCallbacks extends FragmentHostCallback<Activity> {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 80f1e6e..306b54d 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -327,6 +327,9 @@
private static final String KEY_LAUNCHED_FROM_BUBBLE =
"android.activity.launchTypeBubble";
+ /** See {@link #setTransientLaunch()}. */
+ private static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch";
+
/**
* @see #setLaunchCookie
* @hide
@@ -414,6 +417,7 @@
private int mSplashScreenThemeResId;
private boolean mRemoveWithTaskOrganizer;
private boolean mLaunchedFromBubble;
+ private boolean mTransientLaunch;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -1166,6 +1170,7 @@
mSplashScreenThemeResId = opts.getInt(KEY_SPLASH_SCREEN_THEME);
mRemoveWithTaskOrganizer = opts.getBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER);
mLaunchedFromBubble = opts.getBoolean(KEY_LAUNCHED_FROM_BUBBLE);
+ mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
}
/**
@@ -1663,6 +1668,28 @@
}
/**
+ * Sets whether the activity launch is part of a transient operation. If it is, it will not
+ * cause lifecycle changes in existing activities even if it were to occlude them (ie. other
+ * activities occluded by this one will not be paused or stopped until the launch is committed).
+ * As a consequence, it will start immediately since it doesn't need to wait for other
+ * lifecycles to evolve. Current user is recents.
+ * @hide
+ */
+ public ActivityOptions setTransientLaunch() {
+ mTransientLaunch = true;
+ return this;
+ }
+
+ /**
+ * @see #setTransientLaunch()
+ * @return whether the activity launch is part of a transient operation.
+ * @hide
+ */
+ public boolean getTransientLaunch() {
+ return mTransientLaunch;
+ }
+
+ /**
* 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.
@@ -1902,6 +1929,9 @@
if (mLaunchedFromBubble) {
b.putBoolean(KEY_LAUNCHED_FROM_BUBBLE, mLaunchedFromBubble);
}
+ if (mTransientLaunch) {
+ b.putBoolean(KEY_TRANSIENT_LAUNCH, mTransientLaunch);
+ }
return b;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ff210e1..f626d4b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -178,6 +178,7 @@
import android.view.contentcapture.IContentCaptureManager;
import android.view.contentcapture.IContentCaptureOptionsCallback;
import android.view.translation.TranslationSpec;
+import android.view.translation.UiTranslationSpec;
import android.webkit.WebView;
import android.window.SizeConfigurationBuckets;
import android.window.SplashScreen;
@@ -1843,13 +1844,15 @@
@Override
public void updateUiTranslationState(IBinder activityToken, int state,
- TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds) {
+ TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds,
+ UiTranslationSpec uiTranslationSpec) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = activityToken;
args.arg2 = state;
args.arg3 = sourceSpec;
args.arg4 = targetSpec;
args.arg5 = viewIds;
+ args.arg6 = uiTranslationSpec;
sendMessage(H.UPDATE_UI_TRANSLATION_STATE, args);
}
}
@@ -2212,7 +2215,7 @@
final SomeArgs args = (SomeArgs) msg.obj;
updateUiTranslationState((IBinder) args.arg1, (int) args.arg2,
(TranslationSpec) args.arg3, (TranslationSpec) args.arg4,
- (List<AutofillId>) args.arg5);
+ (List<AutofillId>) args.arg5, (UiTranslationSpec) args.arg6);
break;
case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK:
handleSetContentCaptureOptionsCallback((String) msg.obj);
@@ -3506,7 +3509,8 @@
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
- r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo));
+ r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
+ appContext.getAttributionSource());
if (r.state != null) {
r.state.setClassLoader(cl);
}
@@ -3798,7 +3802,8 @@
for (int i=0; i<N; i++) {
ReferrerIntent intent = intents.get(i);
intent.setExtrasClassLoader(r.activity.getClassLoader());
- intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo));
+ intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
+ r.activity.getAttributionSource());
r.activity.mFragments.noteStateNotSaved();
mInstrumentation.callActivityOnNewIntent(r.activity, intent);
}
@@ -4046,10 +4051,7 @@
final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity);
final SplashScreenView view = builder.createFromParcel(parcelable).build();
decorView.addView(view);
- view.cacheRootWindow(r.window);
- view.makeSystemUIColorsTransparent();
- r.activity.mSplashScreenView = view;
- view.attachHostActivity(r.activity);
+ view.attachHostActivityAndSetSystemUIColors(r.activity, r.window);
view.requestLayout();
// Ensure splash screen view is shown before remove the splash screen window.
final ViewRootImpl impl = decorView.getViewRootImpl();
@@ -4091,12 +4093,13 @@
@Override
public void handOverSplashScreenView(@NonNull ActivityClientRecord r) {
- if (r.activity.mSplashScreenView != null) {
- synchronized (this) {
- if (mSplashScreenGlobal != null) {
- mSplashScreenGlobal.dispatchOnExitAnimation(r.token,
- r.activity.mSplashScreenView);
- }
+ final SplashScreenView v = r.activity.getSplashScreenView();
+ if (v == null) {
+ return;
+ }
+ synchronized (this) {
+ if (mSplashScreenGlobal != null) {
+ mSplashScreenGlobal.dispatchOnExitAnimation(r.token, v);
}
}
}
@@ -4194,13 +4197,15 @@
}
private void updateUiTranslationState(IBinder activityToken, int state,
- TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds) {
+ TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds,
+ UiTranslationSpec uiTranslationSpec) {
final ActivityClientRecord r = mActivities.get(activityToken);
if (r == null) {
Log.w(TAG, "updateUiTranslationState(): no activity for " + activityToken);
return;
}
- r.activity.updateUiTranslationState(state, sourceSpec, targetSpec, viewIds);
+ r.activity.updateUiTranslationState(
+ state, sourceSpec, targetSpec, viewIds, uiTranslationSpec);
}
private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
@@ -4236,10 +4241,15 @@
if (data.info.splitName != null) {
context = (ContextImpl) context.createContextForSplit(data.info.splitName);
}
+ if (data.info.attributionTags != null && data.info.attributionTags.length > 0) {
+ final String attributionTag = data.info.attributionTags[0];
+ context = (ContextImpl) context.createAttributionContext(attributionTag);
+ }
java.lang.ClassLoader cl = context.getClassLoader();
data.intent.setExtrasClassLoader(cl);
data.intent.prepareToEnterProcess(
- isProtectedComponent(data.info) || isProtectedBroadcast(data.intent));
+ isProtectedComponent(data.info) || isProtectedBroadcast(data.intent),
+ context.getAttributionSource());
data.setExtrasClassLoader(cl);
receiver = packageInfo.getAppFactory()
.instantiateReceiver(cl, data.info.name, data.intent);
@@ -4430,6 +4440,10 @@
if (data.info.splitName != null) {
context = (ContextImpl) context.createContextForSplit(data.info.splitName);
}
+ if (data.info.attributionTags != null && data.info.attributionTags.length > 0) {
+ final String attributionTag = data.info.attributionTags[0];
+ context = (ContextImpl) context.createAttributionContext(attributionTag);
+ }
// Service resources must be initialized with the same loaders as the application
// context.
context.getResources().addLoaders(
@@ -4464,7 +4478,8 @@
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
- data.intent.prepareToEnterProcess(isProtectedComponent(createData.info));
+ data.intent.prepareToEnterProcess(isProtectedComponent(createData.info),
+ s.getAttributionSource());
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
@@ -4494,7 +4509,8 @@
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
- data.intent.prepareToEnterProcess(isProtectedComponent(createData.info));
+ data.intent.prepareToEnterProcess(isProtectedComponent(createData.info),
+ s.getAttributionSource());
boolean doRebind = s.onUnbind(data.intent);
try {
if (doRebind) {
@@ -4583,7 +4599,8 @@
try {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
- data.args.prepareToEnterProcess(isProtectedComponent(createData.info));
+ data.args.prepareToEnterProcess(isProtectedComponent(createData.info),
+ s.getAttributionSource());
}
int res;
if (!data.taskRemoved) {
@@ -5236,7 +5253,8 @@
try {
if (ri.mData != null) {
ri.mData.setExtrasClassLoader(r.activity.getClassLoader());
- ri.mData.prepareToEnterProcess(isProtectedComponent(r.activityInfo));
+ ri.mData.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
+ r.activity.getAttributionSource());
}
if (DEBUG_RESULTS) Slog.v(TAG,
"Delivering result to activity " + r + " : " + ri);
@@ -5887,7 +5905,7 @@
public final void applyConfigurationToResources(Configuration config) {
synchronized (mResourcesManager) {
- mResourcesManager.applyConfigurationToResourcesLocked(config, null);
+ mResourcesManager.applyConfigurationToResources(config, null);
}
}
@@ -5975,7 +5993,7 @@
synchronized (mResourcesManager) {
// Update all affected Resources objects to use new ResourcesImpl
- mResourcesManager.applyNewResourceDirsLocked(ai, oldResDirs);
+ mResourcesManager.applyNewResourceDirs(ai, oldResDirs);
}
}
@@ -6231,7 +6249,7 @@
synchronized (mResourcesManager) {
// Update affected Resources objects to use new ResourcesImpl
- mResourcesManager.applyNewResourceDirsLocked(aInfo, oldResDirs);
+ mResourcesManager.applyNewResourceDirs(aInfo, oldResDirs);
}
} catch (RemoteException e) {
}
@@ -6474,7 +6492,7 @@
* reflect configuration changes. The configuration object passed
* in AppBindData can be safely assumed to be up to date
*/
- mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+ mResourcesManager.applyConfigurationToResources(data.config, data.compatInfo);
mCurDefaultDisplayDpi = data.config.densityDpi;
// This calls mResourcesManager so keep it within the synchronized block.
@@ -7337,6 +7355,10 @@
throw new RuntimeException(e);
}
}
+ if (info.attributionTags != null && info.attributionTags.length > 0) {
+ final String attributionTag = info.attributionTags[0];
+ c = c.createAttributionContext(attributionTag);
+ }
try {
final java.lang.ClassLoader cl = c.getClassLoader();
@@ -7509,7 +7531,7 @@
// We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it.
- if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
+ if (mResourcesManager.applyConfigurationToResources(globalConfig,
null /* compat */,
mInitialApplication.getResources().getDisplayAdjustments())) {
mConfigurationController.updateLocaleListFromAppContext(
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 010f4e4..ed00436 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -7394,20 +7394,27 @@
/** @hide */
public void setUserRestriction(int code, boolean restricted, IBinder token) {
- setUserRestriction(code, restricted, token, /*exceptionPackages*/null);
+ setUserRestriction(code, restricted, token, (Map<String, String[]>) null);
}
- /** @hide */
+ /**
+ * An empty array of attribution tags means exclude all tags under that package.
+ * @hide
+ */
public void setUserRestriction(int code, boolean restricted, IBinder token,
- String[] exceptionPackages) {
- setUserRestrictionForUser(code, restricted, token, exceptionPackages, mContext.getUserId());
+ @Nullable Map<String, String[]> excludedPackageTags) {
+ setUserRestrictionForUser(code, restricted, token, excludedPackageTags,
+ mContext.getUserId());
}
- /** @hide */
+ /**
+ * An empty array of attribution tags means exclude all tags under that package.
+ * @hide
+ */
public void setUserRestrictionForUser(int code, boolean restricted, IBinder token,
- String[] exceptionPackages, int userId) {
+ @Nullable Map<String, String[]> excludedPackageTags, int userId) {
try {
- mService.setUserRestriction(code, restricted, token, userId, exceptionPackages);
+ mService.setUserRestriction(code, restricted, token, userId, excludedPackageTags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -7949,7 +7956,7 @@
*/
public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
try {
- return mService.checkOperationRaw(op, uid, packageName);
+ return mService.checkOperationRaw(op, uid, packageName, null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 341b9c5..2de0ddb 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -29,6 +29,7 @@
import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.TriFunction;
/**
@@ -45,12 +46,14 @@
* @param code The op code to check.
* @param uid The UID for which to check.
* @param packageName The package for which to check.
- * @param superImpl The super implementation.
+ * @param attributionTag The attribution tag for which to check.
* @param raw Whether to check the raw op i.e. not interpret the mode based on UID state.
+ * @param superImpl The super implementation.
* @return The app op check result.
*/
- int checkOperation(int code, int uid, String packageName, boolean raw,
- QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl);
+ int checkOperation(int code, int uid, String packageName, @Nullable String attributionTag,
+ boolean raw,
+ QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl);
/**
* Allows overriding check audio operation behavior.
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 0dbe3ba..6d92201 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -107,8 +107,7 @@
mCompatConfiguration = new Configuration();
}
mCompatConfiguration.setTo(mConfiguration);
- if (mResourcesManager.applyCompatConfigurationLocked(displayDensity,
- mCompatConfiguration)) {
+ if (mResourcesManager.applyCompatConfiguration(displayDensity, mCompatConfiguration)) {
config = mCompatConfiguration;
}
return config;
@@ -199,7 +198,7 @@
// configuration also needs to set to the adjustments for consistency.
appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
}
- mResourcesManager.applyConfigurationToResourcesLocked(config, compat,
+ mResourcesManager.applyConfigurationToResources(config, compat,
appResources.getDisplayAdjustments());
updateLocaleListFromAppContext(app.getApplicationContext());
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9ce37e4..73c17b9 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1774,7 +1774,8 @@
intent.setExtrasClassLoader(getClassLoader());
// TODO: determine at registration time if caller is
// protecting themselves with signature permission
- intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent));
+ intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent),
+ getAttributionSource());
}
return intent;
} catch (RemoteException e) {
@@ -3035,8 +3036,16 @@
}
}
+ final String attributionTag;
+ if (activityInfo.attributionTags != null && activityInfo.attributionTags.length > 0) {
+ attributionTag = activityInfo.attributionTags[0];
+ } else {
+ attributionTag = null;
+ }
+
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY,
- null, null, activityInfo.splitName, activityToken, null, 0, classLoader, null);
+ attributionTag, null, activityInfo.splitName, activityToken, null, 0, classLoader,
+ null);
context.mContextType = CONTEXT_TYPE_ACTIVITY;
context.mIsConfigurationBasedContext = true;
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 918309e..4555c172 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -46,6 +46,7 @@
import android.os.SharedMemory;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
+import android.view.translation.UiTranslationSpec;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
@@ -160,5 +161,6 @@
IUiAutomationConnection instrumentationUiConnection,
in ApplicationInfo targetInfo);
void updateUiTranslationState(IBinder activityToken, int state, in TranslationSpec sourceSpec,
- in TranslationSpec targetSpec, in List<AutofillId> viewIds);
+ in TranslationSpec targetSpec, in List<AutofillId> viewIds,
+ in UiTranslationSpec uiTranslationSpec);
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index ea6c874..4446fb7 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1651,7 +1651,8 @@
intent.setExtrasClassLoader(cl);
// TODO: determine at registration time if caller is
// protecting themselves with signature permission
- intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent));
+ intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent),
+ mContext.getAttributionSource());
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2dcdd07..fc2c6ac 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -174,6 +174,7 @@
*/
public static final @ServiceNotificationPolicy int FOREGROUND_SERVICE_DEFERRED = 2;
+ @ServiceNotificationPolicy
private int mFgsDeferBehavior;
/**
@@ -4614,9 +4615,9 @@
* foreground service. By default, the system can choose to defer
* visibility of the notification for a short time after the service is
* started. Pass
- * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE BEHAVIOR_IMMEDIATE_DISPLAY}
+ * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE FOREGROUND_SERVICE_IMMEDIATE}
* to this method in order to guarantee that visibility is never deferred. Pass
- * {@link Notification#FOREGROUND_SERVICE_DEFERRED BEHAVIOR_DEFERRED_DISPLAY}
+ * {@link Notification#FOREGROUND_SERVICE_DEFERRED FOREGROUND_SERVICE_DEFERRED}
* to request that visibility is deferred whenever possible.
*
* <p class="note">Note that deferred visibility is not guaranteed. There
@@ -4624,13 +4625,13 @@
* service's associated Notification immediately even when the app has used
* this method to explicitly request deferred display.</p>
* @param behavior One of
- * {@link Notification#FOREGROUND_SERVICE_DEFAULT BEHAVIOR_DEFAULT},
- * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE BEHAVIOR_IMMEDIATE_DISPLAY},
- * or {@link Notification#FOREGROUND_SERVICE_DEFERRED BEHAVIOR_DEFERRED_DISPLAY}
+ * {@link Notification#FOREGROUND_SERVICE_DEFAULT FOREGROUND_SERVICE_DEFAULT},
+ * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE FOREGROUND_SERVICE_IMMEDIATE},
+ * or {@link Notification#FOREGROUND_SERVICE_DEFERRED FOREGROUND_SERVICE_DEFERRED}
* @return
*/
@NonNull
- public Builder setForegroundServiceBehavior(int behavior) {
+ public Builder setForegroundServiceBehavior(@ServiceNotificationPolicy int behavior) {
mN.mFgsDeferBehavior = behavior;
return this;
}
@@ -5309,8 +5310,7 @@
// the change's state in NotificationManagerService were very complex. These behavior
// changes are entirely visual, and should otherwise be undetectable by apps.
@SuppressWarnings("AndroidFrameworkCompatChange")
- private void calculateLargeIconDimens(boolean largeIconShown,
- @NonNull StandardTemplateParams p,
+ private void calculateRightIconDimens(Icon rightIcon, boolean isPromotedPicture,
@NonNull TemplateBindResult result) {
final Resources resources = mContext.getResources();
final float density = resources.getDisplayMetrics().density;
@@ -5323,9 +5323,9 @@
final float viewHeightDp = resources.getDimension(
R.dimen.notification_right_icon_size) / density;
float viewWidthDp = viewHeightDp; // icons are 1:1 by default
- if (largeIconShown && (p.mPromotePicture
+ if (rightIcon != null && (isPromotedPicture
|| mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S)) {
- Drawable drawable = mN.mLargeIcon.loadDrawable(mContext);
+ Drawable drawable = rightIcon.loadDrawable(mContext);
if (drawable != null) {
int iconWidth = drawable.getIntrinsicWidth();
int iconHeight = drawable.getIntrinsicHeight();
@@ -5337,8 +5337,8 @@
}
}
final float extraMarginEndDpIfVisible = viewWidthDp + iconMarginDp;
- result.setRightIconState(largeIconShown, viewWidthDp,
- extraMarginEndDpIfVisible, expanderSizeDp);
+ result.setRightIconState(rightIcon != null /* visible */, viewWidthDp,
+ viewHeightDp, extraMarginEndDpIfVisible, expanderSizeDp);
}
/**
@@ -5349,19 +5349,45 @@
if (mN.mLargeIcon == null && mN.largeIcon != null) {
mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
}
- boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon;
- calculateLargeIconDimens(showLargeIcon, p, result);
- if (showLargeIcon) {
+
+ // Determine the left and right icons
+ Icon leftIcon = p.mHideLeftIcon ? null : mN.mLargeIcon;
+ Icon rightIcon = p.mHideRightIcon ? null
+ : (p.mPromotedPicture != null ? p.mPromotedPicture : mN.mLargeIcon);
+
+ // Apply the left icon (without duplicating the bitmap)
+ if (leftIcon != rightIcon || leftIcon == null) {
+ // If the leftIcon is explicitly hidden or different from the rightIcon, then set it
+ // explicitly and make sure it won't take the right_icon drawable.
+ contentView.setImageViewIcon(R.id.left_icon, leftIcon);
+ contentView.setIntTag(R.id.left_icon, R.id.tag_uses_right_icon_drawable, 0);
+ } else {
+ // If the leftIcon equals the rightIcon, just set the flag to use the right_icon
+ // drawable. This avoids the view having two copies of the same bitmap.
+ contentView.setIntTag(R.id.left_icon, R.id.tag_uses_right_icon_drawable, 1);
+ }
+
+ // Always calculate dimens to populate `result` for the GONE case
+ boolean isPromotedPicture = p.mPromotedPicture != null;
+ calculateRightIconDimens(rightIcon, isPromotedPicture, result);
+
+ // Bind the right icon
+ if (rightIcon != null) {
contentView.setViewLayoutWidth(R.id.right_icon,
result.mRightIconWidthDp, TypedValue.COMPLEX_UNIT_DIP);
+ contentView.setViewLayoutHeight(R.id.right_icon,
+ result.mRightIconHeightDp, TypedValue.COMPLEX_UNIT_DIP);
contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
- contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
- processLargeLegacyIcon(mN.mLargeIcon, contentView, p);
+ contentView.setImageViewIcon(R.id.right_icon, rightIcon);
+ contentView.setIntTag(R.id.right_icon, R.id.tag_keep_when_showing_left_icon,
+ isPromotedPicture ? 1 : 0);
+ processLargeLegacyIcon(rightIcon, contentView, p);
} else {
// The "reset" doesn't clear the drawable, so we do it here. This clear is
// important because the presence of a drawable in this view (regardless of the
// visibility) is used by NotificationGroupingUtil to set the visibility.
contentView.setImageViewIcon(R.id.right_icon, null);
+ contentView.setIntTag(R.id.right_icon, R.id.tag_keep_when_showing_left_icon, 0);
}
}
@@ -5662,11 +5688,6 @@
R.dimen.call_notification_collapsible_indent);
}
big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
- if (p.mCallStyleActions) {
- // Use "wrap_content" (unlike normal emphasized mode) and allow prioritizing the
- // required actions (Answer, Decline, and Hang Up).
- big.setBoolean(R.id.actions, "setPrioritizedWrapMode", true);
- }
if (numActions > 0 && !p.mHideActions) {
big.setViewVisibility(R.id.actions_container, View.VISIBLE);
big.setViewVisibility(R.id.actions, View.VISIBLE);
@@ -5683,7 +5704,7 @@
// Clear the drawable
button.setInt(R.id.action0, "setBackgroundResource", 0);
}
- if (p.mCallStyleActions && i > 0) {
+ if (emphazisedMode && i > 0) {
// Clear start margin from non-first buttons to reduce the gap between them.
// (8dp remaining gap is from all buttons' standard 4dp inset).
button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
@@ -6053,11 +6074,13 @@
.viewType(StandardTemplateParams.VIEW_TYPE_MINIMIZED)
.highlightExpander(false)
.fillTextsFrom(this);
- if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
+ if (!useRegularSubtext || TextUtils.isEmpty(p.summaryText)) {
p.summaryText(createSummaryText());
}
RemoteViews header = makeNotificationHeader(p);
header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
+ // The low priority header has no app name and shows the text
+ header.setBoolean(R.id.notification_header, "styleTextAsTitle", true);
return header;
}
@@ -6103,26 +6126,21 @@
// change the background bgColor
CharSequence title = action.title;
ColorStateList[] outResultColor = new ColorStateList[1];
- int background = getBackgroundColor(p);
+ int background = getSecondaryAccentColor(p);
if (isLegacy()) {
title = ContrastColorUtil.clearColorSpans(title);
} else {
title = ensureColorSpanContrast(title, background, outResultColor);
}
button.setTextViewText(R.id.action0, processTextSpans(title));
- final int textColor;
boolean hasColorOverride = outResultColor[0] != null;
if (hasColorOverride) {
// There's a span spanning the full text, let's take it and use it as the
// background color
background = outResultColor[0].getDefaultColor();
- textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
- background, mInNightMode);
- } else if (mTintActionButtons && !mInNightMode && !isBackgroundColorized(p)) {
- textColor = getAccentColor(p);
- } else {
- textColor = getPrimaryTextColor(p);
}
+ final int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
+ background, mInNightMode);
button.setTextColor(R.id.action0, textColor);
// We only want about 20% alpha for the ripple
final int rippleColor = (textColor & 0x00ffffff) | 0x33000000;
@@ -6130,11 +6148,10 @@
ColorStateList.valueOf(rippleColor));
button.setColorStateList(R.id.action0, "setButtonBackground",
ColorStateList.valueOf(background));
- button.setBoolean(R.id.action0, "setHasStroke", !hasColorOverride);
if (p.mCallStyleActions) {
button.setImageViewIcon(R.id.action0, action.getIcon());
boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
- button.setBoolean(R.id.action0, "setWrapModePriority", priority);
+ button.setBoolean(R.id.action0, "setIsPriority", priority);
int minWidthDimen =
priority ? R.dimen.call_notification_system_action_min_width : 0;
button.setIntDimen(R.id.action0, "setMinimumWidth", minWidthDimen);
@@ -6320,6 +6337,22 @@
}
/**
+ * Gets the secondary accent color for colored UI elements. If we're tinting with the theme
+ * accent, this is the theme accent color, otherwise this would be identical to
+ * {@link #getSmallIconColor(StandardTemplateParams)}.
+ */
+ private @ColorInt int getSecondaryAccentColor(StandardTemplateParams p) {
+ if (isBackgroundColorized(p)) {
+ return getSecondaryTextColor(p);
+ }
+ int color = obtainThemeColor(R.attr.colorAccentSecondary, COLOR_INVALID);
+ if (color != COLOR_INVALID) {
+ return color;
+ }
+ return getContrastColor(p);
+ }
+
+ /**
* Gets the "surface protection" color from the theme, or a variant of the normal background
* color when colorized, or when not using theme color tints.
*/
@@ -7392,25 +7425,11 @@
return super.makeContentView(increasedHeight);
}
- Icon oldLargeIcon = mBuilder.mN.mLargeIcon;
- mBuilder.mN.mLargeIcon = mPictureIcon;
- // The legacy largeIcon might not allow us to clear the image, as it's taken in
- // replacement if the other one is null. Because we're restoring these legacy icons
- // for old listeners, this is in general non-null.
- Bitmap largeIconLegacy = mBuilder.mN.largeIcon;
- mBuilder.mN.largeIcon = null;
-
StandardTemplateParams p = mBuilder.mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
.fillTextsFrom(mBuilder)
- .promotePicture(true);
- RemoteViews contentView = getStandardView(mBuilder.getBaseLayoutResource(),
- p, null /* result */);
-
- mBuilder.mN.mLargeIcon = oldLargeIcon;
- mBuilder.mN.largeIcon = largeIconLegacy;
-
- return contentView;
+ .promotedPicture(mPictureIcon);
+ return getStandardView(mBuilder.getBaseLayoutResource(), p, null /* result */);
}
/**
@@ -7422,25 +7441,11 @@
return super.makeHeadsUpContentView(increasedHeight);
}
- Icon oldLargeIcon = mBuilder.mN.mLargeIcon;
- mBuilder.mN.mLargeIcon = mPictureIcon;
- // The legacy largeIcon might not allow us to clear the image, as it's taken in
- // replacement if the other one is null. Because we're restoring these legacy icons
- // for old listeners, this is in general non-null.
- Bitmap largeIconLegacy = mBuilder.mN.largeIcon;
- mBuilder.mN.largeIcon = null;
-
StandardTemplateParams p = mBuilder.mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP)
.fillTextsFrom(mBuilder)
- .promotePicture(true);
- RemoteViews contentView = getStandardView(mBuilder.getHeadsUpBaseLayoutResource(),
- p, null /* result */);
-
- mBuilder.mN.mLargeIcon = oldLargeIcon;
- mBuilder.mN.largeIcon = largeIconLegacy;
-
- return contentView;
+ .promotedPicture(mPictureIcon);
+ return getStandardView(mBuilder.getHeadsUpBaseLayoutResource(), p, null /* result */);
}
/**
@@ -7535,14 +7540,21 @@
mShowBigPictureWhenCollapsed = extras.getBoolean(EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED);
+ mPictureIcon = getPictureIcon(extras);
+ }
+
+ /** @hide */
+ @Nullable
+ public static Icon getPictureIcon(@Nullable Bundle extras) {
+ if (extras == null) return null;
// When this style adds a picture, we only add one of the keys. If both were added,
// it would most likely be a legacy app trying to override the picture in some way.
// Because of that case it's better to give precedence to the legacy field.
Bitmap bitmapPicture = extras.getParcelable(EXTRA_PICTURE);
if (bitmapPicture != null) {
- mPictureIcon = Icon.createWithBitmap(bitmapPicture);
+ return Icon.createWithBitmap(bitmapPicture);
} else {
- mPictureIcon = extras.getParcelable(EXTRA_PICTURE_ICON);
+ return extras.getParcelable(EXTRA_PICTURE_ICON);
}
}
@@ -8318,7 +8330,8 @@
.hideProgress(true)
.title(isHeaderless ? conversationTitle : null)
.text(null)
- .hideLargeIcon(hideRightIcons || isOneToOne)
+ .hideLeftIcon(isOneToOne)
+ .hideRightIcon(hideRightIcons || isOneToOne)
.headerTextSecondary(isHeaderless ? null : conversationTitle);
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
isConversationLayout
@@ -9116,9 +9129,10 @@
StandardTemplateParams p = mBuilder.mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
- .hideTime(numActionsToShow > 1) // hide if actions wider than a large icon
- .hideSubText(numActionsToShow > 1) // hide if actions wider than a large icon
- .hideLargeIcon(numActionsToShow > 0) // large icon or actions; not both
+ .hideTime(numActionsToShow > 1) // hide if actions wider than a right icon
+ .hideSubText(numActionsToShow > 1) // hide if actions wider than a right icon
+ .hideLeftIcon(false) // allow large icon on left when grouped
+ .hideRightIcon(numActionsToShow > 0) // right icon or actions; not both
.hideProgress(true)
.fillTextsFrom(mBuilder);
TemplateBindResult result = new TemplateBindResult();
@@ -9522,7 +9536,8 @@
.viewType(viewType)
.callStyleActions(true)
.allowTextWithProgress(true)
- .hideLargeIcon(true)
+ .hideLeftIcon(true)
+ .hideRightIcon(true)
.hideAppName(isCollapsed)
.titleViewId(R.id.conversation_text)
.title(title)
@@ -12106,6 +12121,7 @@
private static class TemplateBindResult {
boolean mRightIconVisible;
float mRightIconWidthDp;
+ float mRightIconHeightDp;
/**
* The margin end that needs to be added to the heading so that it won't overlap
@@ -12130,10 +12146,11 @@
*/
public final MarginSet mTitleMarginSet = new MarginSet();
- public void setRightIconState(boolean visible, float widthDp,
+ public void setRightIconState(boolean visible, float widthDp, float heightDp,
float marginEndDpIfVisible, float expanderSizeDp) {
mRightIconVisible = visible;
mRightIconWidthDp = widthDp;
+ mRightIconHeightDp = heightDp;
mHeadingExtraMarginSet.setValues(0, marginEndDpIfVisible);
mHeadingFullMarginSet.setValues(expanderSizeDp, marginEndDpIfVisible + expanderSizeDp);
mTitleMarginSet.setValues(0, marginEndDpIfVisible + expanderSizeDp);
@@ -12232,7 +12249,9 @@
boolean mHideActions;
boolean mHideProgress;
boolean mHideSnoozeButton;
- boolean mPromotePicture;
+ boolean mHideLeftIcon;
+ boolean mHideRightIcon;
+ Icon mPromotedPicture;
boolean mCallStyleActions;
boolean mAllowTextWithProgress;
int mTitleViewId;
@@ -12242,7 +12261,6 @@
CharSequence headerTextSecondary;
CharSequence summaryText;
int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
- boolean hideLargeIcon;
boolean allowColorization = true;
boolean mHighlightExpander = false;
@@ -12256,7 +12274,9 @@
mHideActions = false;
mHideProgress = false;
mHideSnoozeButton = false;
- mPromotePicture = false;
+ mHideLeftIcon = false;
+ mHideRightIcon = false;
+ mPromotedPicture = null;
mCallStyleActions = false;
mAllowTextWithProgress = false;
mTitleViewId = R.id.title;
@@ -12266,7 +12286,6 @@
summaryText = null;
headerTextSecondary = null;
maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
- hideLargeIcon = false;
allowColorization = true;
mHighlightExpander = false;
return this;
@@ -12331,8 +12350,8 @@
return this;
}
- final StandardTemplateParams promotePicture(boolean promotePicture) {
- this.mPromotePicture = promotePicture;
+ final StandardTemplateParams promotedPicture(Icon promotedPicture) {
+ this.mPromotedPicture = promotedPicture;
return this;
}
@@ -12366,8 +12385,14 @@
return this;
}
- final StandardTemplateParams hideLargeIcon(boolean hideLargeIcon) {
- this.hideLargeIcon = hideLargeIcon;
+
+ final StandardTemplateParams hideLeftIcon(boolean hideLeftIcon) {
+ this.mHideLeftIcon = hideLeftIcon;
+ return this;
+ }
+
+ final StandardTemplateParams hideRightIcon(boolean hideRightIcon) {
+ this.mHideRightIcon = hideRightIcon;
return this;
}
@@ -12404,7 +12429,8 @@
// Minimally decorated custom views do not show certain pieces of chrome that have
// always been shown when using DecoratedCustomViewStyle.
boolean hideOtherFields = decorationType <= DECORATION_MINIMAL;
- hideLargeIcon(hideOtherFields);
+ hideLeftIcon(false); // The left icon decoration is better than showing nothing.
+ hideRightIcon(hideOtherFields);
hideProgress(hideOtherFields);
hideActions(hideOtherFields);
return this;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 74134e1..792336d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -75,6 +75,11 @@
private static ResourcesManager sResourcesManager;
/**
+ * Internal lock object
+ */
+ private final Object mLock = new Object();
+
+ /**
* The global compatibility settings.
*/
private CompatibilityInfo mResCompatibilityInfo;
@@ -275,7 +280,7 @@
* try as hard as possible to release any open FDs.
*/
public void invalidatePath(String path) {
- synchronized (this) {
+ synchronized (mLock) {
int count = 0;
for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
@@ -304,7 +309,7 @@
}
public Configuration getConfiguration() {
- synchronized (this) {
+ synchronized (mLock) {
return mResConfiguration;
}
}
@@ -351,13 +356,15 @@
config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
}
- public boolean applyCompatConfigurationLocked(int displayDensity,
+ public boolean applyCompatConfiguration(int displayDensity,
@NonNull Configuration compatConfiguration) {
- if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
- mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
- return true;
+ synchronized (mLock) {
+ if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
+ mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
+ return true;
+ }
+ return false;
}
- return false;
}
/**
@@ -376,7 +383,7 @@
final Pair<Integer, DisplayAdjustments> key =
Pair.create(displayId, displayAdjustmentsCopy);
SoftReference<Display> sd;
- synchronized (this) {
+ synchronized (mLock) {
sd = mAdjustedDisplays.get(key);
}
if (sd != null) {
@@ -392,7 +399,7 @@
}
final Display display = dm.getCompatibleDisplay(displayId, key.second);
if (display != null) {
- synchronized (this) {
+ synchronized (mLock) {
mAdjustedDisplays.put(key, new SoftReference<>(display));
}
}
@@ -407,7 +414,7 @@
* @param resources The {@link Resources} backing the display adjustments.
*/
public Display getAdjustedDisplay(final int displayId, Resources resources) {
- synchronized (this) {
+ synchronized (mLock) {
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
if (dm == null) {
// may be null early in system startup
@@ -425,7 +432,7 @@
ApkAssets apkAssets;
// Optimistically check if this ApkAssets exists somewhere else.
- synchronized (this) {
+ synchronized (mLock) {
final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(key);
if (apkAssetsRef != null) {
apkAssets = apkAssetsRef.get();
@@ -447,7 +454,7 @@
key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
}
- synchronized (this) {
+ synchronized (mLock) {
mCachedApkAssets.put(key, new WeakReference<>(apkAssets));
}
@@ -559,7 +566,7 @@
* @hide
*/
public void dump(String prefix, PrintWriter printWriter) {
- synchronized (this) {
+ synchronized (mLock) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
for (int i = 0; i < prefix.length() / 2; i++) {
pw.increaseIndent();
@@ -688,7 +695,7 @@
*/
boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
@Nullable Configuration overrideConfig) {
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources activityResources
= activityToken != null ? mActivityResourceReferences.get(activityToken) : null;
if (activityResources == null) {
@@ -834,7 +841,7 @@
+ " with key=" + key);
}
- synchronized (this) {
+ synchronized (mLock) {
// Force the creation of an ActivityResourcesStruct.
getOrCreateActivityResourcesStructLocked(token);
}
@@ -842,7 +849,7 @@
// Update any existing Activity Resources references.
updateResourcesForActivity(token, overrideConfig, displayId);
- synchronized (this) {
+ synchronized (mLock) {
Resources resources = findResourcesForActivityLocked(token, key,
classLoader);
if (resources != null) {
@@ -868,7 +875,7 @@
*/
private void rebaseKeyForActivity(IBinder activityToken, ResourcesKey key,
boolean overridesActivityDisplay) {
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
@@ -960,7 +967,7 @@
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#createApkAssetsSupplierNotLocked");
try {
- if (DEBUG && Thread.holdsLock(this)) {
+ if (DEBUG && Thread.holdsLock(mLock)) {
Slog.w(TAG, "Calling thread " + Thread.currentThread().getName()
+ " is holding mLock", new Throwable());
}
@@ -994,7 +1001,7 @@
@Nullable
private Resources createResources(@NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
- synchronized (this) {
+ synchronized (mLock) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
@@ -1015,7 +1022,7 @@
@NonNull ResourcesKey key, @NonNull Configuration initialOverrideConfig,
@Nullable Integer overrideDisplayId, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
- synchronized (this) {
+ synchronized (mLock) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
@@ -1130,7 +1137,7 @@
if (displayId == INVALID_DISPLAY) {
throw new IllegalArgumentException("displayId can not be INVALID_DISPLAY");
}
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
@@ -1269,67 +1276,64 @@
public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
- synchronized(this) {
- return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
- }
- }
-
- public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
- @Nullable CompatibilityInfo compat) {
- return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
+ return applyConfigurationToResources(config, compat, null /* adjustments */);
}
/** Applies the global configuration to the managed resources. */
- public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
+ public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) {
- try {
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
- "ResourcesManager#applyConfigurationToResourcesLocked");
+ synchronized (mLock) {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#applyConfigurationToResources");
- if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
- if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
- + mResConfiguration.seq + ", newSeq=" + config.seq);
- return false;
- }
-
- // Things might have changed in display manager, so clear the cached displays.
- mAdjustedDisplays.clear();
-
- int changes = mResConfiguration.updateFrom(config);
- if (compat != null && (mResCompatibilityInfo == null ||
- !mResCompatibilityInfo.equals(compat))) {
- mResCompatibilityInfo = compat;
- changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
- | ActivityInfo.CONFIG_SCREEN_SIZE
- | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
- }
-
- DisplayMetrics displayMetrics = getDisplayMetrics();
- if (adjustments != null) {
- // Currently the only case where the adjustment takes effect is to simulate placing
- // an app in a rotated display.
- adjustments.adjustGlobalAppMetrics(displayMetrics);
- }
- Resources.updateSystemConfiguration(config, displayMetrics, compat);
-
- ApplicationPackageManager.configurationChanged();
-
- Configuration tmpConfig = new Configuration();
-
- for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
- ResourcesKey key = mResourceImpls.keyAt(i);
- WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
- ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
- if (r != null) {
- applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
- } else {
- mResourceImpls.removeAt(i);
+ if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+ if (DEBUG || DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Skipping new config: curSeq="
+ + mResConfiguration.seq + ", newSeq=" + config.seq);
+ }
+ return false;
}
- }
- return changes != 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ // Things might have changed in display manager, so clear the cached displays.
+ mAdjustedDisplays.clear();
+
+ int changes = mResConfiguration.updateFrom(config);
+ if (compat != null && (mResCompatibilityInfo == null
+ || !mResCompatibilityInfo.equals(compat))) {
+ mResCompatibilityInfo = compat;
+ changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+ | ActivityInfo.CONFIG_SCREEN_SIZE
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+ }
+
+ DisplayMetrics displayMetrics = getDisplayMetrics();
+ if (adjustments != null) {
+ // Currently the only case where the adjustment takes effect is to simulate
+ // placing an app in a rotated display.
+ adjustments.adjustGlobalAppMetrics(displayMetrics);
+ }
+ Resources.updateSystemConfiguration(config, displayMetrics, compat);
+
+ ApplicationPackageManager.configurationChanged();
+
+ Configuration tmpConfig = new Configuration();
+
+ for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
+ ResourcesKey key = mResourceImpls.keyAt(i);
+ WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
+ if (r != null) {
+ applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
+ } else {
+ mResourceImpls.removeAt(i);
+ }
+ }
+
+ return changes != 0;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
}
}
@@ -1378,7 +1382,7 @@
* @param libAssets The library asset paths to add.
*/
public void appendLibAssetsForMainAssetPath(String assetPath, String[] libAssets) {
- synchronized (this) {
+ synchronized (mLock) {
// Record which ResourcesImpl need updating
// (and what ResourcesKey they should update to).
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
@@ -1414,54 +1418,56 @@
}
// TODO(adamlesinski): Make this accept more than just overlay directories.
- final void applyNewResourceDirsLocked(@NonNull final ApplicationInfo appInfo,
+ void applyNewResourceDirs(@NonNull final ApplicationInfo appInfo,
@Nullable final String[] oldPaths) {
- try {
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
- "ResourcesManager#applyNewResourceDirsLocked");
+ synchronized (mLock) {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#applyNewResourceDirsLocked");
- String baseCodePath = appInfo.getBaseCodePath();
+ String baseCodePath = appInfo.getBaseCodePath();
- final int myUid = Process.myUid();
- String[] newSplitDirs = appInfo.uid == myUid
- ? appInfo.splitSourceDirs
- : appInfo.splitPublicSourceDirs;
+ final int myUid = Process.myUid();
+ String[] newSplitDirs = appInfo.uid == myUid
+ ? appInfo.splitSourceDirs
+ : appInfo.splitPublicSourceDirs;
- // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
- String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
- String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
- appInfo.overlayPaths);
+ // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
+ String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
+ String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
+ appInfo.overlayPaths);
- final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
- final int implCount = mResourceImpls.size();
- for (int i = 0; i < implCount; i++) {
- final ResourcesKey key = mResourceImpls.keyAt(i);
- final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
- final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+ final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+ final int implCount = mResourceImpls.size();
+ for (int i = 0; i < implCount; i++) {
+ final ResourcesKey key = mResourceImpls.keyAt(i);
+ final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
- if (impl == null) {
- continue;
+ if (impl == null) {
+ continue;
+ }
+
+ if (key.mResDir == null
+ || key.mResDir.equals(baseCodePath)
+ || ArrayUtils.contains(oldPaths, key.mResDir)) {
+ updatedResourceKeys.put(impl, new ResourcesKey(
+ baseCodePath,
+ copiedSplitDirs,
+ copiedResourceDirs,
+ key.mLibDirs,
+ key.mDisplayId,
+ key.mOverrideConfiguration,
+ key.mCompatInfo,
+ key.mLoaders
+ ));
+ }
}
- if (key.mResDir == null
- || key.mResDir.equals(baseCodePath)
- || ArrayUtils.contains(oldPaths, key.mResDir)) {
- updatedResourceKeys.put(impl, new ResourcesKey(
- baseCodePath,
- copiedSplitDirs,
- copiedResourceDirs,
- key.mLibDirs,
- key.mDisplayId,
- key.mOverrideConfiguration,
- key.mCompatInfo,
- key.mLoaders
- ));
- }
+ redirectResourcesToNewImplLocked(updatedResourceKeys);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
-
- redirectResourcesToNewImplLocked(updatedResourceKeys);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
@@ -1556,7 +1562,7 @@
public boolean overrideTokenDisplayAdjustments(IBinder token,
@Nullable Consumer<DisplayAdjustments> override) {
boolean handled = false;
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources tokenResources = mActivityResourceReferences.get(token);
if (tokenResources == null) {
return false;
@@ -1589,7 +1595,7 @@
@Override
public void onLoadersChanged(@NonNull Resources resources,
@NonNull List<ResourcesLoader> newLoader) {
- synchronized (ResourcesManager.this) {
+ synchronized (mLock) {
final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
if (oldKey == null) {
throw new IllegalArgumentException("Cannot modify resource loaders of"
@@ -1617,7 +1623,7 @@
**/
@Override
public void onLoaderUpdated(@NonNull ResourcesLoader loader) {
- synchronized (ResourcesManager.this) {
+ synchronized (mLock) {
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceImplKeys =
new ArrayMap<>();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 02e64b8..38b19ae 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11974,15 +11974,14 @@
/**
* @hide
- * Force update user setup completed status.
+ * Force update user setup completed status for the given {@code userId}.
* @throws {@link SecurityException} if the caller has no
- * {@code android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS} or the caller is
- * not {@link UserHandle#SYSTEM_USER}
+ * {@code android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}.
*/
@TestApi
- public void forceUpdateUserSetupComplete() {
+ public void forceUpdateUserSetupComplete(@UserIdInt int userId) {
try {
- mService.forceUpdateUserSetupComplete();
+ mService.forceUpdateUserSetupComplete(userId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9f76bd1..db2fc0d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -406,7 +406,7 @@
boolean isDeviceProvisioningConfigApplied();
void setDeviceProvisioningConfigApplied();
- void forceUpdateUserSetupComplete();
+ void forceUpdateUserSetupComplete(int userId);
void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
boolean isBackupServiceEnabled(in ComponentName admin);
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index b216e91..a71cffe 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -48,6 +48,13 @@
String SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED = "set_auto_detection_enabled";
/**
+ * A shell command that prints whether the telephony-based time zone detection feature is
+ * supported on the device.
+ * @hide
+ */
+ String SHELL_COMMAND_IS_TELEPHONY_DETECTION_SUPPORTED = "is_telephony_detection_supported";
+
+ /**
* A shell command that prints whether the geolocation-based time zone detection feature is
* supported on the device.
* @hide
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 152de44..ba3fc1e 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -78,6 +78,10 @@
static final int VIEW_MODE_ERROR = 2;
static final int VIEW_MODE_DEFAULT = 3;
+ // Set of valid colors resources.
+ private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_neutral1_0;
+ private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_accent3_1000;
+
// When we're inflating the initialLayout for a AppWidget, we only allow
// views that are allowed in RemoteViews.
private static final LayoutInflater.Filter INFLATER_FILTER =
@@ -97,6 +101,7 @@
private boolean mOnLightBackground;
private SizeF mCurrentSize = null;
private RemoteViews.ColorResources mColorResources = null;
+ private SparseIntArray mColorMapping = null;
// Stores the last remote views last inflated.
private RemoteViews mLastInflatedRemoteViews = null;
private long mLastInflatedRemoteViewsId = -1;
@@ -888,12 +893,30 @@
* {@link android.R.color#system_neutral1_500}.
*/
public void setColorResources(@NonNull SparseIntArray colorMapping) {
- mColorResources = RemoteViews.ColorResources.create(mContext, colorMapping);
+ if (mColorMapping != null && isSameColorMapping(mColorMapping, colorMapping)) {
+ return;
+ }
+ mColorMapping = colorMapping.clone();
+ mColorResources = RemoteViews.ColorResources.create(mContext, mColorMapping);
mLayoutId = -1;
mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
}
+ /** Check if, in the current context, the two color mappings are equivalent. */
+ private boolean isSameColorMapping(SparseIntArray oldColors, SparseIntArray newColors) {
+ if (oldColors.size() != newColors.size()) {
+ return false;
+ }
+ for (int i = 0; i < oldColors.size(); i++) {
+ if (oldColors.keyAt(i) != newColors.keyAt(i)
+ || oldColors.valueAt(i) != newColors.valueAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Reset the dynamically overloaded resources, reverting to the default values for
* all the colors.
@@ -904,6 +927,7 @@
public void resetColorResources() {
if (mColorResources != null) {
mColorResources = null;
+ mColorMapping = null;
mLayoutId = -1;
mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 4032663..52d4c71 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1190,6 +1190,11 @@
return devices;
}
+ /** {@hide} */
+ public void prepareToEnterProcess(AttributionSource attributionSource) {
+ setAttributionSource(attributionSource);
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothDevice) {
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index c5c4277..dfef47d 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -586,6 +586,10 @@
* @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
*/
public Builder setDeviceAddress(String deviceAddress) {
+ if (deviceAddress == null) {
+ mDeviceAddress = deviceAddress;
+ return this;
+ }
return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC);
}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 6ab1975..54b39bd 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -1034,14 +1034,14 @@
}
/** {@hide} */
- public void prepareToEnterProcess() {
+ public void prepareToEnterProcess(AttributionSource source) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
final Item item = mItems.get(i);
if (item.mIntent != null) {
// We can't recursively claim that this data is from a protected
// component, since it may have been filled in by a malicious app
- item.mIntent.prepareToEnterProcess(false);
+ item.mIntent.prepareToEnterProcess(false, source);
}
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7c7cfdb..a436fa4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -31,6 +31,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.AppGlobals;
+import android.bluetooth.BluetoothDevice;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -2022,12 +2023,9 @@
* <p>
* Output: Nothing.
* </p>
- *
- * @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE)
- @SystemApi
public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD =
"android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
@@ -2188,12 +2186,7 @@
* <p>
* Type: String
* </p>
- *
- * E.g. {@link android.Manifest.permission_group.CONTACTS}
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_PERMISSION_GROUP_NAME =
"android.intent.extra.PERMISSION_GROUP_NAME";
@@ -5342,28 +5335,19 @@
* {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
*
* E.g. an attribution tag could be location_provider, com.google.android.gms.*, etc.
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
/**
* A long representing the start timestamp (epoch time in millis) of the permission usage
* when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
/**
* A long representing the end timestamp (epoch time in millis) of the permission usage when
* used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
/**
@@ -11456,7 +11440,7 @@
/**
* @hide
*/
- public void prepareToEnterProcess(boolean fromProtectedComponent) {
+ public void prepareToEnterProcess(boolean fromProtectedComponent, AttributionSource source) {
// We just entered destination process, so we should be able to read all
// parcelables inside.
setDefusable(true);
@@ -11464,10 +11448,10 @@
if (mSelector != null) {
// We can't recursively claim that this data is from a protected
// component, since it may have been filled in by a malicious app
- mSelector.prepareToEnterProcess(false);
+ mSelector.prepareToEnterProcess(false, source);
}
if (mClipData != null) {
- mClipData.prepareToEnterProcess();
+ mClipData.prepareToEnterProcess(source);
}
if (mContentUserHint != UserHandle.USER_CURRENT) {
@@ -11480,6 +11464,16 @@
if (fromProtectedComponent) {
mLocalFlags |= LOCAL_FLAG_FROM_PROTECTED_COMPONENT;
}
+
+ // Special attribution fix-up logic for any BluetoothDevice extras
+ // passed via Bluetooth intents
+ if (mAction != null && mAction.startsWith("android.bluetooth.")
+ && hasExtra(BluetoothDevice.EXTRA_DEVICE)) {
+ final BluetoothDevice device = getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (device != null) {
+ device.prepareToEnterProcess(source);
+ }
+ }
}
/** @hide */
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index fbed907..03f6380e 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -17,8 +17,8 @@
package android.content.pm;
import android.annotation.IntDef;
-import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.app.Activity;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
@@ -1174,13 +1174,6 @@
*/
public WindowLayout windowLayout;
- /**
- * Attribution tags for finer grained calls if a {@link
- * android.content.Context#sendBroadcast(Intent, String)} is used with a permission.
- */
- @SuppressLint("MissingNullability")
- public String[] attributionTags;
-
public ActivityInfo() {
}
@@ -1209,7 +1202,6 @@
mMaxAspectRatio = orig.mMaxAspectRatio;
mMinAspectRatio = orig.mMinAspectRatio;
supportsSizeChanges = orig.supportsSizeChanges;
- attributionTags = orig.attributionTags;
}
/**
@@ -1532,15 +1524,6 @@
if (supportsSizeChanges) {
pw.println(prefix + "supportsSizeChanges=true");
}
- if (attributionTags != null && attributionTags.length > 0) {
- StringBuilder tags = new StringBuilder();
- tags.append(attributionTags[0]);
- for (int i = 1; i < attributionTags.length; i++) {
- tags.append(", ");
- tags.append(attributionTags[i]);
- }
- pw.println(prefix + "attributionTags=[" + tags + "]");
- }
super.dumpBack(pw, prefix, dumpFlags);
}
@@ -1586,7 +1569,6 @@
dest.writeFloat(mMaxAspectRatio);
dest.writeFloat(mMinAspectRatio);
dest.writeBoolean(supportsSizeChanges);
- dest.writeString8Array(attributionTags);
}
/**
@@ -1706,7 +1688,6 @@
mMaxAspectRatio = source.readFloat();
mMinAspectRatio = source.readFloat();
supportsSizeChanges = source.readBoolean();
- attributionTags = source.createString8Array();
}
/**
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index afaecec..63f93bf 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -92,7 +92,7 @@
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
- ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_SHORT_LABEL_RES_ID)
+ ).addProperty(new AppSearchSchema.LongPropertyConfig.Builder(KEY_SHORT_LABEL_RES_ID)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.build()
@@ -108,7 +108,7 @@
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
- ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_LONG_LABEL_RES_ID)
+ ).addProperty(new AppSearchSchema.LongPropertyConfig.Builder(KEY_LONG_LABEL_RES_ID)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.build()
@@ -124,7 +124,7 @@
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
.build()
- ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(
+ ).addProperty(new AppSearchSchema.LongPropertyConfig.Builder(
KEY_DISABLED_MESSAGE_RES_ID)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.build()
@@ -170,7 +170,7 @@
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
.build()
- ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_IMPLICIT_RANK)
+ ).addProperty(new AppSearchSchema.LongPropertyConfig.Builder(KEY_IMPLICIT_RANK)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.build()
@@ -184,7 +184,7 @@
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
.build()
- ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_ICON_RES_ID)
+ ).addProperty(new AppSearchSchema.LongPropertyConfig.Builder(KEY_ICON_RES_ID)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.build()
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index a3e0473..8b0e992 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -778,9 +778,18 @@
*/
public static final int PRIVATE_FLAG_EXT_PROFILEABLE = 1 << 0;
+ /**
+ * Value for {@link #privateFlagsExt}: whether this application has requested
+ * exemption from the foreground service restriction introduced in S
+ * (https://developer.android.com/about/versions/12/foreground-services).
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION = 1 << 1;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_EXT_" }, value = {
PRIVATE_FLAG_EXT_PROFILEABLE,
+ PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoPrivateFlagsExt {}
@@ -2445,6 +2454,17 @@
}
/**
+ * @return whether the app has requested exemption from the foreground service restrictions.
+ * This does not take any affect for now.
+ * @hide
+ */
+ @TestApi
+ public boolean hasRequestForegroundServiceExemption() {
+ return (privateFlagsExt
+ & ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION) != 0;
+ }
+
+ /**
* @hide
*/
@Override protected ApplicationInfo getApplicationInfo() {
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index c67d00e..42847c8 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -16,8 +16,14 @@
package android.content.pm;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcel;
@@ -52,6 +58,25 @@
public String splitName;
/**
+ * Set of attribution tags that should be automatically applied to this
+ * component.
+ * <p>
+ * When this component represents an {@link Activity}, {@link Service},
+ * {@link ContentResolver} or {@link BroadcastReceiver}, each instance will
+ * be automatically configured with {@link Context#createAttributionContext}
+ * using the first attribution tag contained here.
+ * <p>
+ * Additionally, when this component represents a {@link BroadcastReceiver}
+ * and the sender of a broadcast requires the receiver to hold one or more
+ * specific permissions, those permission checks will be performed using
+ * each of the attributions tags contained here.
+ *
+ * @see Context#createAttributionContext(String)
+ */
+ @SuppressLint({"MissingNullability", "MutableBareField"})
+ public String[] attributionTags;
+
+ /**
* A string resource identifier (in the package's resources) containing
* a user-readable description of the component. From the "description"
* attribute or, if not set, 0.
@@ -87,6 +112,7 @@
applicationInfo = orig.applicationInfo;
processName = orig.processName;
splitName = orig.splitName;
+ attributionTags = orig.attributionTags;
descriptionRes = orig.descriptionRes;
enabled = orig.enabled;
exported = orig.exported;
@@ -172,6 +198,15 @@
if (splitName != null) {
pw.println(prefix + "splitName=" + splitName);
}
+ if (attributionTags != null && attributionTags.length > 0) {
+ StringBuilder tags = new StringBuilder();
+ tags.append(attributionTags[0]);
+ for (int i = 1; i < attributionTags.length; i++) {
+ tags.append(", ");
+ tags.append(attributionTags[i]);
+ }
+ pw.println(prefix + "attributionTags=[" + tags + "]");
+ }
pw.println(prefix + "enabled=" + enabled + " exported=" + exported
+ " directBootAware=" + directBootAware);
if (descriptionRes != 0) {
@@ -200,6 +235,7 @@
applicationInfo.writeToParcel(dest, parcelableFlags);
dest.writeString8(processName);
dest.writeString8(splitName);
+ dest.writeString8Array(attributionTags);
dest.writeInt(descriptionRes);
dest.writeInt(enabled ? 1 : 0);
dest.writeInt(exported ? 1 : 0);
@@ -211,6 +247,7 @@
applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
processName = source.readString8();
splitName = source.readString8();
+ attributionTags = source.createString8Array();
descriptionRes = source.readInt();
enabled = (source.readInt() != 0);
exported = (source.readInt() != 0);
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 9d381ef..804a06b 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -81,7 +81,4 @@
AndroidFuture<ParceledListSlice> getShortcuts(String packageName, int matchFlags, int userId);
AndroidFuture pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId);
-
- AndroidFuture updateShortcutVisibility(String callingPkg, String packageName,
- in byte[] certificate, in boolean visible, int userId);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a1d419e..edf0e57 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4775,8 +4775,7 @@
* @param flags Additional option flags to modify the data returned.
* @return A {@link ServiceInfo} object containing information about the
* service.
- * @throws NameNotFoundException if a package with the given name cannot be
- * found on the system.
+ * @throws NameNotFoundException if the component cannot be found on the system.
*/
@NonNull
public abstract ServiceInfo getServiceInfo(@NonNull ComponentName component,
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index a60e642..13ff602 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -20,6 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,6 +30,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* This class provides information for a shared library. There are
@@ -190,7 +192,8 @@
*
* @hide
*/
- public List<String> getAllCodePaths() {
+ @TestApi
+ public @NonNull List<String> getAllCodePaths() {
if (getPath() != null) {
// Builtin library.
ArrayList<String> list = new ArrayList<>();
@@ -198,7 +201,7 @@
return list;
} else {
// Static or dynamic library.
- return mCodePaths;
+ return Objects.requireNonNull(mCodePaths);
}
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 2a36c11..d77fa91 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -40,7 +40,6 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
@@ -790,23 +789,6 @@
}
}
- /**
- * Granting another app the access to the shortcuts you own. You must provide the package name
- * and their SHA256 certificate digest in order to granting the access.
- *
- * Once granted, the other app can retain a copy of all the shortcuts you own when calling
- * {@link LauncherApps#getShortcuts(LauncherApps.ShortcutQuery, UserHandle)}.
- */
- public void updateShortcutVisibility(@NonNull final String packageName,
- @Nullable final byte[] certificate, final boolean visible) {
- try {
- getFutureOrThrow(mService.updateShortcutVisibility(mContext.getPackageName(),
- packageName, certificate, visible, injectMyUserId()));
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
private static <T> T getFutureOrThrow(@NonNull AndroidFuture<T> future) {
try {
return future.get();
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 8bc3734..1eb4504 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -49,6 +49,9 @@
"include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert"
}
]
+ },
+ {
+ "name": "CtsPackageManagerBootTestCases"
}
]
}
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index 980f10d..d407b10 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -397,7 +397,7 @@
}
// CompatibilityMode is global state.
- if (!ParsingPackageUtils.sCompatibilityModeEnabled) {
+ if (!android.content.pm.PackageParser.sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
@@ -713,6 +713,7 @@
componentInfo.directBootAware = mainComponent.isDirectBootAware();
componentInfo.enabled = mainComponent.isEnabled();
componentInfo.splitName = mainComponent.getSplitName();
+ componentInfo.attributionTags = mainComponent.getAttributionTags();
}
private static void assignSharedFieldsForPackageItemInfo(
@@ -806,7 +807,9 @@
public static int appInfoPrivateFlagsExt(ParsingPackageRead pkg) {
// @formatter:off
int privateFlagsExt =
- flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE);
+ flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE)
+ | flag(pkg.hasRequestForegroundServiceExemption(),
+ ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION);
// @formatter:on
return privateFlagsExt;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index cea50cb..2413e6d 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -338,6 +338,8 @@
ParsingPackage setTheme(int theme);
+ ParsingPackage setRequestForegroundServiceExemption(boolean requestForegroundServiceExemption);
+
ParsingPackage setUpgradeKeySets(@NonNull Set<String> upgradeKeySets);
ParsingPackage setUse32BitAbi(boolean use32BitAbi);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 7114886..b0342aa 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -464,6 +464,7 @@
CROSS_PROFILE,
ENABLED,
DISALLOW_PROFILING,
+ REQUEST_FOREGROUND_SERVICE_EXEMPTION,
})
public @interface Values {}
private static final long EXTERNAL_STORAGE = 1L;
@@ -512,6 +513,7 @@
private static final long CROSS_PROFILE = 1L << 43;
private static final long ENABLED = 1L << 44;
private static final long DISALLOW_PROFILING = 1L << 45;
+ private static final long REQUEST_FOREGROUND_SERVICE_EXEMPTION = 1L << 46;
}
private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
@@ -2199,6 +2201,11 @@
}
@Override
+ public boolean hasRequestForegroundServiceExemption() {
+ return getBoolean(Booleans.REQUEST_FOREGROUND_SERVICE_EXEMPTION);
+ }
+
+ @Override
public ParsingPackageImpl setBaseRevisionCode(int value) {
baseRevisionCode = value;
return this;
@@ -2420,6 +2427,11 @@
}
@Override
+ public ParsingPackageImpl setRequestForegroundServiceExemption(boolean value) {
+ return setBoolean(Booleans.REQUEST_FOREGROUND_SERVICE_EXEMPTION, value);
+ }
+
+ @Override
public ParsingPackageImpl setUiOptions(int value) {
uiOptions = value;
return this;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 1c2c59f..35a2b9a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -906,10 +906,15 @@
*/
@ApplicationInfo.NativeHeapZeroInitialized
int getNativeHeapZeroInitialized();
-
@Nullable
Boolean hasRequestRawExternalStorageAccess();
+ /**
+ * @see ApplicationInfo#hasRequestForegroundServiceExemption()
+ * @see R.styleable#AndroidManifest_requestForegroundServiceExemption
+ */
+ boolean hasRequestForegroundServiceExemption();
+
// TODO(b/135203078): Hide and enforce going through PackageInfoUtils
ApplicationInfo toAppInfoWithoutState();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 22d75ef..022ba16 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2033,6 +2033,12 @@
.AndroidManifestApplication_requestRawExternalStorageAccess,
false));
}
+ if (sa.hasValue(
+ R.styleable.AndroidManifestApplication_requestForegroundServiceExemption)) {
+ pkg.setRequestForegroundServiceExemption(sa.getBoolean(R.styleable
+ .AndroidManifestApplication_requestForegroundServiceExemption,
+ false));
+ }
} finally {
sa.recycle();
}
@@ -2810,15 +2816,7 @@
}
}
- @SuppressWarnings("AndroidFrameworkCompatChange")
private void convertSplitPermissions(ParsingPackage pkg) {
- // STOPSHIP(b/183905675): REMOVE THIS TERRIBLE, HORRIBLE, NO GOOD, VERY BAD HACK
- if ("com.android.chrome".equals(pkg.getPackageName())
- && pkg.getVersionCode() <= 445500399
- && pkg.getTargetSdkVersion() > Build.VERSION_CODES.R) {
- pkg.setTargetSdkVersion(Build.VERSION_CODES.R);
- }
-
final int listSize = mSplitPermissionInfos.size();
for (int is = 0; is < listSize; is++) {
final PermissionManager.SplitPermissionInfo spi = mSplitPermissionInfos.get(is);
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 9285ccb..6f478ac 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -82,9 +82,6 @@
@Nullable
ActivityInfo.WindowLayout windowLayout;
- @Nullable
- String[] attributionTags;
-
public ParsedActivity(ParsedActivity other) {
super(other);
this.theme = other.theme;
@@ -110,7 +107,6 @@
this.rotationAnimation = other.rotationAnimation;
this.colorMode = other.colorMode;
this.windowLayout = other.windowLayout;
- this.attributionTags = other.attributionTags;
}
/**
@@ -176,7 +172,6 @@
alias.requestedVrComponent = target.requestedVrComponent;
alias.directBootAware = target.directBootAware;
alias.setProcessName(target.getProcessName());
- alias.attributionTags = target.attributionTags;
return alias;
// Not all attributes from the target ParsedActivity are copied to the alias.
@@ -304,7 +299,6 @@
} else {
dest.writeBoolean(false);
}
- dest.writeString8Array(this.attributionTags);
}
public ParsedActivity() {
@@ -338,7 +332,6 @@
if (in.readBoolean()) {
windowLayout = new ActivityInfo.WindowLayout(in);
}
- this.attributionTags = in.createString8Array();
}
public static final Parcelable.Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
@@ -452,9 +445,4 @@
public ActivityInfo.WindowLayout getWindowLayout() {
return windowLayout;
}
-
- @Nullable
- public String[] getAttributionTags() {
- return attributionTags;
- }
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index aa740bd..92a90e9 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -102,7 +102,8 @@
R.styleable.AndroidManifestActivity_name,
R.styleable.AndroidManifestActivity_process,
R.styleable.AndroidManifestActivity_roundIcon,
- R.styleable.AndroidManifestActivity_splitName);
+ R.styleable.AndroidManifestActivity_splitName,
+ R.styleable.AndroidManifestActivity_attributionTags);
if (result.isError()) {
return result;
}
@@ -212,11 +213,6 @@
pkg.setVisibleToInstantApps(true);
}
- String attributionTags = sa.getString(R.styleable.AndroidManifestActivity_attributionTags);
- if (attributionTags != null) {
- activity.attributionTags = attributionTags.split("\\|");
- }
-
return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
false /*isAlias*/, visibleToEphemeral, input,
R.styleable.AndroidManifestActivity_parentActivityName,
@@ -281,7 +277,8 @@
R.styleable.AndroidManifestActivityAlias_name,
null /*processAttr*/,
R.styleable.AndroidManifestActivityAlias_roundIcon,
- null /*splitNameAttr*/);
+ null /*splitNameAttr*/,
+ R.styleable.AndroidManifestActivityAlias_attributionTags);
if (result.isError()) {
return result;
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
index a5e394d..033e30f 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
@@ -39,6 +39,8 @@
@Nullable
String splitName;
+ @Nullable
+ String[] attributionTags;
public ParsedMainComponent() {
}
@@ -51,6 +53,7 @@
this.exported = other.exported;
this.order = other.order;
this.splitName = other.splitName;
+ this.attributionTags = other.attributionTags;
}
public ParsedMainComponent setProcessName(String processName) {
@@ -84,6 +87,7 @@
dest.writeBoolean(this.exported);
dest.writeInt(this.order);
dest.writeString(this.splitName);
+ dest.writeString8Array(this.attributionTags);
}
protected ParsedMainComponent(Parcel in) {
@@ -94,6 +98,7 @@
this.exported = in.readBoolean();
this.order = in.readInt();
this.splitName = in.readString();
+ this.attributionTags = in.createString8Array();
}
public static final Parcelable.Creator<ParsedMainComponent> CREATOR =
@@ -135,6 +140,11 @@
return splitName;
}
+ @Nullable
+ public String[] getAttributionTags() {
+ return attributionTags;
+ }
+
public ParsedMainComponent setDirectBootAware(boolean value) {
directBootAware = value;
return this;
@@ -149,4 +159,9 @@
splitName = value;
return this;
}
+
+ public ParsedMainComponent setAttributionTags(@Nullable String[] value) {
+ attributionTags = value;
+ return this;
+ }
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
index f70d62b..54bcbdd 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
@@ -48,7 +48,8 @@
TypedArray array, int flags, boolean useRoundIcon, ParseInput input,
int bannerAttr, int descriptionAttr, @Nullable Integer directBootAwareAttr,
@Nullable Integer enabledAttr, int iconAttr, int labelAttr, int logoAttr, int nameAttr,
- @Nullable Integer processAttr, int roundIconAttr, @Nullable Integer splitNameAttr) {
+ @Nullable Integer processAttr, int roundIconAttr, @Nullable Integer splitNameAttr,
+ @Nullable Integer attributionTagsAttr) {
ParseResult<Component> result = ParsedComponentUtils.parseComponent(component, tag, pkg,
array, useRoundIcon, input, bannerAttr, descriptionAttr, iconAttr, labelAttr,
logoAttr, nameAttr, roundIconAttr);
@@ -94,6 +95,13 @@
component.splitName = array.getNonConfigurationString(splitNameAttr, 0);
}
+ if (attributionTagsAttr != null) {
+ final String attributionTags = array.getNonConfigurationString(attributionTagsAttr, 0);
+ if (attributionTags != null) {
+ component.attributionTags = attributionTags.split("\\|");
+ }
+ }
+
return input.success(component);
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
index 4deab56..28fd919 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
@@ -74,7 +74,8 @@
R.styleable.AndroidManifestProvider_name,
R.styleable.AndroidManifestProvider_process,
R.styleable.AndroidManifestProvider_roundIcon,
- R.styleable.AndroidManifestProvider_splitName);
+ R.styleable.AndroidManifestProvider_splitName,
+ R.styleable.AndroidManifestProvider_attributionTags);
if (result.isError()) {
return result;
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
index 739bee2..ae107ce 100644
--- a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
@@ -68,7 +68,8 @@
R.styleable.AndroidManifestService_name,
R.styleable.AndroidManifestService_process,
R.styleable.AndroidManifestService_roundIcon,
- R.styleable.AndroidManifestService_splitName
+ R.styleable.AndroidManifestService_splitName,
+ R.styleable.AndroidManifestService_attributionTags
);
if (result.isError()) {
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 45b956b..5b727cc 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -35,6 +35,7 @@
import com.android.internal.R;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.graphics.cam.Cam;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
@@ -555,9 +556,8 @@
final int alpha = MathUtils.constrain((int) (baseAlpha * alphaMod + 0.5f), 0, 255);
if (validLStar) {
- final double[] labColor = new double[3];
- ColorUtils.colorToLAB(baseColor, labColor);
- baseColor = ColorUtils.LABToColor(lStar, labColor[1], labColor[2]);
+ final Cam baseCam = ColorUtils.colorToCAM(baseColor);
+ baseColor = ColorUtils.CAMToColor(baseCam.getHue(), baseCam.getChroma(), lStar);
}
return (baseColor & 0xFFFFFF) | (alpha << 24);
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 80b5078..8e3de61 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -21,9 +21,11 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.ImageFormat;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
+import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.SizeList;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
@@ -220,6 +222,7 @@
private InitializerFuture mInitFuture = null;
private ServiceConnection mConnection = null;
private ICameraExtensionsProxyService mProxy = null;
+ private boolean mSupportsAdvancedExtensions = false;
// Singleton, don't allow construction
private CameraExtensionManagerGlobal() {}
@@ -245,6 +248,11 @@
public void onServiceConnected(ComponentName component, IBinder binder) {
mProxy = ICameraExtensionsProxyService.Stub.asInterface(binder);
mInitFuture.setStatus(true);
+ try {
+ mSupportsAdvancedExtensions = mProxy.advancedExtensionsSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote IPC failed!");
+ }
}
};
ctx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE |
@@ -334,6 +342,10 @@
}
}
+ public boolean areAdvancedExtensionsSupported() {
+ return mSupportsAdvancedExtensions;
+ }
+
public IPreviewExtenderImpl initializePreviewExtension(int extensionType)
throws RemoteException {
synchronized (mLock) {
@@ -355,6 +367,17 @@
}
}
}
+
+ public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType)
+ throws RemoteException {
+ synchronized (mLock) {
+ if (mProxy != null) {
+ return mProxy.initializeAdvancedExtension(extensionType);
+ } else {
+ return null;
+ }
+ }
+ }
}
/**
@@ -374,23 +397,60 @@
/**
* @hide
*/
+ public static boolean areAdvancedExtensionsSupported() {
+ return CameraExtensionManagerGlobal.get().areAdvancedExtensionsSupported();
+ }
+
+ /**
+ * @hide
+ */
public static boolean isExtensionSupported(String cameraId, int extensionType,
CameraCharacteristics chars) {
- Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders;
+ if (areAdvancedExtensionsSupported()) {
+ try {
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extensionType);
+ return extender.isExtensionAvailable(cameraId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query extension availability! Extension service does not"
+ + " respond!");
+ return false;
+ }
+ } else {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders;
+ try {
+ extenders = initializeExtension(extensionType);
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+
+ try {
+ return extenders.first.isExtensionAvailable(cameraId, chars.getNativeMetadata()) &&
+ extenders.second.isExtensionAvailable(cameraId, chars.getNativeMetadata());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query extension availability! Extension service does not"
+ + " respond!");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static IAdvancedExtenderImpl initializeAdvancedExtension(@Extension int extensionType) {
+ IAdvancedExtenderImpl extender;
try {
- extenders = initializeExtension(extensionType);
- } catch (IllegalArgumentException e) {
- return false;
+ extender = CameraExtensionManagerGlobal.get().initializeAdvancedExtension(
+ extensionType);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to initialize extension: " + extensionType);
}
- try {
- return extenders.first.isExtensionAvailable(cameraId, chars.getNativeMetadata()) &&
- extenders.second.isExtensionAvailable(cameraId, chars.getNativeMetadata());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to query extension availability! Extension service does not"
- + " respond!");
- return false;
+ if (extender == null) {
+ throw new IllegalArgumentException("Unknown extension: " + extensionType);
}
+
+ return extender;
}
/**
@@ -487,13 +547,21 @@
throw new IllegalArgumentException("Unsupported extension");
}
- Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
- initializeExtension(extension);
StreamConfigurationMap streamMap = mChars.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- extenders.first.init(mCameraId, mChars.getNativeMetadata());
- return generateSupportedSizes(extenders.first.getSupportedResolutions(),
- ImageFormat.PRIVATE, streamMap);
+ if (areAdvancedExtensionsSupported()) {
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+ extender.init(mCameraId);
+ return generateSupportedSizes(
+ extender.getSupportedPreviewOutputResolutions(mCameraId),
+ ImageFormat.PRIVATE, streamMap);
+ } else {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.first.init(mCameraId, mChars.getNativeMetadata());
+ return generateSupportedSizes(extenders.first.getSupportedResolutions(),
+ ImageFormat.PRIVATE, streamMap);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
+ " not respond!");
@@ -536,31 +604,47 @@
throw new IllegalArgumentException("Unsupported extension");
}
- Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
- initializeExtension(extension);
StreamConfigurationMap streamMap = mChars.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- if (format == ImageFormat.YUV_420_888) {
- extenders.second.init(mCameraId, mChars.getNativeMetadata());
- if (extenders.second.getCaptureProcessor() == null) {
- // Extensions that don't implement any capture processor are limited to
- // JPEG only!
- return new ArrayList<>();
+ if (areAdvancedExtensionsSupported()) {
+ switch(format) {
+ case ImageFormat.YUV_420_888:
+ case ImageFormat.JPEG:
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported format: " + format);
}
- return generateSupportedSizes(extenders.second.getSupportedResolutions(),
- format, streamMap);
- } else if (format == ImageFormat.JPEG) {
- extenders.second.init(mCameraId, mChars.getNativeMetadata());
- if (extenders.second.getCaptureProcessor() != null) {
- // The framework will perform the additional encoding pass on the
- // processed YUV_420 buffers.
- return generateJpegSupportedSizes(
- extenders.second.getSupportedResolutions(), streamMap);
- } else {
- return generateSupportedSizes(null, format, streamMap);
- }
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+ extender.init(mCameraId);
+ return generateSupportedSizes(extender.getSupportedCaptureOutputResolutions(
+ mCameraId), format, streamMap);
} else {
- throw new IllegalArgumentException("Unsupported format: " + format);
+ if (format == ImageFormat.YUV_420_888) {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ if (extenders.second.getCaptureProcessor() == null) {
+ // Extensions that don't implement any capture processor are limited to
+ // JPEG only!
+ return new ArrayList<>();
+ }
+ return generateSupportedSizes(extenders.second.getSupportedResolutions(),
+ format, streamMap);
+ } else if (format == ImageFormat.JPEG) {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ if (extenders.second.getCaptureProcessor() != null) {
+ // The framework will perform the additional encoding pass on the
+ // processed YUV_420 buffers.
+ return generateJpegSupportedSizes(
+ extenders.second.getSupportedResolutions(), streamMap);
+ } else {
+ return generateSupportedSizes(null, format, streamMap);
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported format: " + format);
+ }
}
} finally {
unregisterClient(clientId);
@@ -608,6 +692,23 @@
if (!isExtensionSupported(mCameraId, extension, mChars)) {
throw new IllegalArgumentException("Unsupported extension");
}
+
+ if (areAdvancedExtensionsSupported()) {
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+ extender.init(mCameraId);
+ android.hardware.camera2.extension.Size sz =
+ new android.hardware.camera2.extension.Size();
+ sz.width = captureOutputSize.getWidth();
+ sz.height = captureOutputSize.getHeight();
+ LatencyRange latencyRange = extender.getEstimatedCaptureLatencyRange(mCameraId,
+ sz, format);
+ if (latencyRange != null) {
+ return new Range(latencyRange.min, latencyRange.max);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
+ + " not respond!");
} finally {
unregisterClient(clientId);
}
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 6ff68c1..d32341f 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -230,6 +230,33 @@
}
/**
+ * Takes ownership of the passed-in properties object
+ *
+ * <p>For internal use only</p>
+ * @hide
+ */
+ public CaptureResult(String cameraId, CameraMetadataNative results, CaptureRequest parent,
+ int requestId, long frameNumber) {
+ if (results == null) {
+ throw new IllegalArgumentException("results was null");
+ }
+
+ if (parent == null) {
+ throw new IllegalArgumentException("parent was null");
+ }
+
+ mResults = CameraMetadataNative.move(results);
+ if (mResults.isEmpty()) {
+ throw new AssertionError("Results must not be empty");
+ }
+ setNativeInstance(mResults);
+ mCameraId = cameraId;
+ mRequest = parent;
+ mSequenceId = requestId;
+ mFrameNumber = frameNumber;
+ }
+
+ /**
* Returns a copy of the underlying {@link CameraMetadataNative}.
* @hide
*/
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index df8eecc..ac7f2ca 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -94,6 +94,36 @@
}
/**
+ * Takes ownership of the passed-in camera metadata and the partial results
+ *
+ * @param partials a list of partial results; {@code null} will be substituted for an empty list
+ * @hide
+ */
+ public TotalCaptureResult(String logicalCameraId, CameraMetadataNative results,
+ CaptureRequest parent, int requestId, long frameNumber, List<CaptureResult> partials,
+ int sessionId, PhysicalCaptureResultInfo[] physicalResults) {
+ super(logicalCameraId, results, parent, requestId, frameNumber);
+
+ if (partials == null) {
+ mPartialResults = new ArrayList<>();
+ } else {
+ mPartialResults = partials;
+ }
+
+ mSessionId = sessionId;
+
+ mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
+ for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
+ TotalCaptureResult physicalResult = new TotalCaptureResult(
+ onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(),
+ parent, requestId, frameNumber, /*partials*/null, sessionId,
+ new PhysicalCaptureResultInfo[0]);
+ mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
+ physicalResult);
+ }
+ }
+
+ /**
* Creates a request-less result.
*
* <p><strong>For testing only.</strong></p>
diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
new file mode 100644
index 0000000..a61bb33
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.Size;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.view.Surface;
+
+/** @hide */
+parcelable CameraOutputConfig
+{
+ Size size;
+ Surface surface;
+ int imageFormat;
+ int capacity;
+
+ const int TYPE_SURFACE = 0;
+ const int TYPE_IMAGEREADER = 1;
+ const int TYPE_MULTIRES_IMAGEREADER = 2;
+ int type;
+
+ OutputConfigId outputId;
+ int surfaceGroupId;
+ String physicalCameraId;
+ List<OutputConfigId> surfaceSharingOutputConfigs;
+}
diff --git a/core/java/android/hardware/camera2/extension/CameraSessionConfig.aidl b/core/java/android/hardware/camera2/extension/CameraSessionConfig.aidl
new file mode 100644
index 0000000..97ce183
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/CameraSessionConfig.aidl
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.CameraOutputConfig;
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/** @hide */
+parcelable CameraSessionConfig
+{
+ List<CameraOutputConfig> outputConfigs;
+ CameraMetadataNative sessionParameter;
+ int sessionTemplateId;
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl b/core/java/android/hardware/camera2/extension/CaptureFailure.aidl
similarity index 62%
copy from apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
copy to core/java/android/hardware/camera2/extension/CaptureFailure.aidl
index 4686de8..d48696c 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
+++ b/core/java/android/hardware/camera2/extension/CaptureFailure.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2020, The Android Open Source Project
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.hardware.camera2.extension;
-/** {@hide} */
-parcelable AppSearchBatchResult;
\ No newline at end of file
+import android.hardware.camera2.CaptureRequest;
+
+/** @hide */
+parcelable CaptureFailure
+{
+ CaptureRequest request;
+ int reason;
+ boolean dropped;
+ int sequenceId;
+ long frameNumber;
+ String errorPhysicalCameraId;
+}
diff --git a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
new file mode 100644
index 0000000..f279c59
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.ISessionProcessorImpl;
+import android.hardware.camera2.extension.LatencyRange;
+import android.hardware.camera2.extension.Size;
+import android.hardware.camera2.extension.SizeList;
+
+/** @hide */
+interface IAdvancedExtenderImpl
+{
+ boolean isExtensionAvailable(in String cameraId);
+ void init(in String cameraId);
+ LatencyRange getEstimatedCaptureLatencyRange(in String cameraId, in Size outputSize,
+ int format);
+ @nullable List<SizeList> getSupportedPreviewOutputResolutions(in String cameraId);
+ @nullable List<SizeList> getSupportedCaptureOutputResolutions(in String cameraId);
+ ISessionProcessorImpl getSessionProcessor();
+}
diff --git a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
index 2a6d22c..bc29e9a 100644
--- a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
+++ b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
@@ -15,6 +15,7 @@
*/
package android.hardware.camera2.extension;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
@@ -23,6 +24,8 @@
{
long registerClient();
void unregisterClient(long clientId);
+ boolean advancedExtensionsSupported();
@nullable IPreviewExtenderImpl initializePreviewExtension(int extensionType);
@nullable IImageCaptureExtenderImpl initializeImageExtension(int extensionType);
+ @nullable IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType);
}
diff --git a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
new file mode 100644
index 0000000..6ab0ad2
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.Request;
+
+/** @hide */
+interface ICaptureCallback
+{
+ void onCaptureStarted(int captureSequenceId, long timestamp);
+ void onCaptureProcessStarted(int captureSequenceId);
+ void onCaptureFailed(int captureSequenceId);
+ void onCaptureSequenceCompleted(int captureSequenceId);
+ void onCaptureSequenceAborted(int captureSequenceId);
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl b/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl
similarity index 61%
copy from apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
copy to core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl
index 4686de8..f365469 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
+++ b/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2020, The Android Open Source Project
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.hardware.camera2.extension;
-/** {@hide} */
-parcelable AppSearchBatchResult;
\ No newline at end of file
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.ParcelImage;
+
+/** @hide */
+interface IImageProcessorImpl
+{
+ void onNextImageAvailable(in OutputConfigId outputConfigId, in ParcelImage image);
+}
diff --git a/core/java/android/hardware/camera2/extension/IRequestCallback.aidl b/core/java/android/hardware/camera2/extension/IRequestCallback.aidl
new file mode 100644
index 0000000..5f308b7
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IRequestCallback.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.CaptureFailure;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.extension.ParcelTotalCaptureResult;
+
+/** @hide */
+interface IRequestCallback
+{
+ void onCaptureStarted(int requestId, long frameNumber, long timestamp);
+ void onCaptureProgressed(int requestId, in ParcelCaptureResult partialResult);
+ void onCaptureCompleted(int requestId, in ParcelTotalCaptureResult totalCaptureResult);
+ void onCaptureFailed(int requestId, in CaptureFailure captureFailure);
+ void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId);
+ void onCaptureSequenceCompleted(int sequenceId, long frameNumber);
+ void onCaptureSequenceAborted(int sequenceId);
+}
diff --git a/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl
new file mode 100644
index 0000000..52595a8
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.IRequestCallback;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.Request;
+
+/** @hide */
+interface IRequestProcessorImpl
+{
+ void setImageProcessor(in OutputConfigId outputConfigId, in IImageProcessorImpl imageProcessor);
+ boolean submit(in Request request, in IRequestCallback callback);
+ boolean submitBurst(in List<Request> requests, in IRequestCallback callback);
+ boolean setRepeating(in Request request, in IRequestCallback callback);
+ void abortCaptures();
+ void stopRepeating();
+}
diff --git a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
new file mode 100644
index 0000000..6fdf4df
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.CameraSessionConfig;
+import android.hardware.camera2.extension.ICaptureCallback;
+import android.hardware.camera2.extension.IRequestProcessorImpl;
+import android.hardware.camera2.extension.OutputSurface;
+
+/** @hide */
+interface ISessionProcessorImpl
+{
+ CameraSessionConfig initSession(in String cameraId, in OutputSurface previewSurface,
+ in OutputSurface imageCaptureSurface);
+ void deInitSession();
+ void onCaptureSessionStart(IRequestProcessorImpl requestProcessor);
+ void onCaptureSessionEnd();
+ int startRepeating(in ICaptureCallback callback);
+ void stopRepeating();
+ int startCapture(in ICaptureCallback callback, int jpegRotation, int jpegQuality);
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl b/core/java/android/hardware/camera2/extension/LatencyRange.aidl
similarity index 76%
copy from apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
copy to core/java/android/hardware/camera2/extension/LatencyRange.aidl
index 4686de8..9bedbb0 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
+++ b/core/java/android/hardware/camera2/extension/LatencyRange.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2020, The Android Open Source Project
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.hardware.camera2.extension;
-/** {@hide} */
-parcelable AppSearchBatchResult;
\ No newline at end of file
+/** @hide */
+parcelable LatencyRange
+{
+ long min;
+ long max;
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl b/core/java/android/hardware/camera2/extension/OutputConfigId.aidl
similarity index 77%
copy from apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
copy to core/java/android/hardware/camera2/extension/OutputConfigId.aidl
index 4686de8..b27f29a 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
+++ b/core/java/android/hardware/camera2/extension/OutputConfigId.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2020, The Android Open Source Project
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.hardware.camera2.extension;
-/** {@hide} */
-parcelable AppSearchBatchResult;
\ No newline at end of file
+/** @hide */
+parcelable OutputConfigId
+{
+ int id;
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl b/core/java/android/hardware/camera2/extension/OutputSurface.aidl
similarity index 67%
copy from apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl
copy to core/java/android/hardware/camera2/extension/OutputSurface.aidl
index 299c9957..8415379 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl
+++ b/core/java/android/hardware/camera2/extension/OutputSurface.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2020, The Android Open Source Project
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.hardware.camera2.extension;
-import android.app.appsearch.AppSearchResult;
+import android.hardware.camera2.extension.Size;
+import android.view.Surface;
-/** {@hide} */
-oneway interface IAppSearchResultCallback {
- void onResult(in AppSearchResult result);
+/** @hide */
+parcelable OutputSurface
+{
+ Surface surface;
+ Size size;
+ int imageFormat;
}
diff --git a/core/java/android/hardware/camera2/extension/ParcelCaptureResult.aidl b/core/java/android/hardware/camera2/extension/ParcelCaptureResult.aidl
new file mode 100644
index 0000000..f99b256
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ParcelCaptureResult.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/** @hide */
+parcelable ParcelCaptureResult
+{
+ String cameraId;
+ CameraMetadataNative results;
+ CaptureRequest parent;
+ int sequenceId;
+ long frameNumber;
+}
diff --git a/core/java/android/hardware/camera2/extension/ParcelTotalCaptureResult.aidl b/core/java/android/hardware/camera2/extension/ParcelTotalCaptureResult.aidl
new file mode 100644
index 0000000..8021a57
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ParcelTotalCaptureResult.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
+
+/** @hide */
+parcelable ParcelTotalCaptureResult
+{
+ String logicalCameraId;
+ CameraMetadataNative results;
+ CaptureRequest parent;
+ int sequenceId;
+ long frameNumber;
+ List<ParcelCaptureResult> partials;
+ int sessionId;
+ List<PhysicalCaptureResultInfo> physicalResult;
+}
diff --git a/core/java/android/hardware/camera2/extension/Request.aidl b/core/java/android/hardware/camera2/extension/Request.aidl
new file mode 100644
index 0000000..d9934dc
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/Request.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/** @hide */
+parcelable Request
+{
+ List<OutputConfigId> targetOutputConfigIds;
+ CameraMetadataNative parameters;
+ int templateId;
+ int requestId;
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
new file mode 100644
index 0000000..abc487d
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -0,0 +1,917 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.impl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraExtensionCharacteristics;
+import android.hardware.camera2.CameraExtensionSession;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.extension.CameraOutputConfig;
+import android.hardware.camera2.extension.CameraSessionConfig;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
+import android.hardware.camera2.extension.ICaptureCallback;
+import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.IRequestCallback;
+import android.hardware.camera2.extension.IRequestProcessorImpl;
+import android.hardware.camera2.extension.ISessionProcessorImpl;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.OutputSurface;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.extension.ParcelImage;
+import android.hardware.camera2.extension.ParcelTotalCaptureResult;
+import android.hardware.camera2.extension.Request;
+import android.hardware.camera2.params.ExtensionSessionConfiguration;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSession {
+ private static final String TAG = "CameraAdvancedExtensionSessionImpl";
+
+ private final Executor mExecutor;
+ private final CameraDevice mCameraDevice;
+ private final long mExtensionClientId;
+ private final Handler mHandler;
+ private final HandlerThread mHandlerThread;
+ private final CameraExtensionSession.StateCallback mCallbacks;
+ private final IAdvancedExtenderImpl mAdvancedExtender;
+ // maps camera outputs to extension output ids
+ private final HashMap<Surface, Integer> mSurfaceIdMap = new HashMap<>();
+ // maps camera extension output ids to camera registered image readers
+ private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
+ private final RequestProcessor mRequestProcessor = new RequestProcessor();
+
+ private Surface mClientRepeatingRequestSurface;
+ private Surface mClientCaptureSurface;
+ private CameraCaptureSession mCaptureSession = null;
+ private ISessionProcessorImpl mSessionProcessor = null;
+
+ private boolean mInitialized;
+
+
+ // Lock to synchronize cross-thread access to device public interface
+ final Object mInterfaceLock = new Object(); // access from this class and Session only!
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CAMERA)
+ public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession(
+ @NonNull CameraDevice cameraDevice, @NonNull Context ctx,
+ @NonNull ExtensionSessionConfiguration config)
+ throws CameraAccessException, RemoteException {
+ long clientId = CameraExtensionCharacteristics.registerClient(ctx);
+ if (clientId < 0) {
+ throw new UnsupportedOperationException("Unsupported extension!");
+ }
+
+ String cameraId = cameraDevice.getId();
+ CameraManager manager = ctx.getSystemService(CameraManager.class);
+ CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
+ CameraExtensionCharacteristics extensionChars = new CameraExtensionCharacteristics(ctx,
+ cameraId, chars);
+
+ if (!CameraExtensionCharacteristics.isExtensionSupported(cameraDevice.getId(),
+ config.getExtension(), chars)) {
+ throw new UnsupportedOperationException("Unsupported extension type: " +
+ config.getExtension());
+ }
+
+ if (config.getOutputConfigurations().isEmpty() ||
+ config.getOutputConfigurations().size() > 2) {
+ throw new IllegalArgumentException("Unexpected amount of output surfaces, received: " +
+ config.getOutputConfigurations().size() + " expected <= 2");
+ }
+
+ int suitableSurfaceCount = 0;
+ List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
+ config.getExtension(), SurfaceTexture.class);
+ Surface repeatingRequestSurface = CameraExtensionUtils.getRepeatingRequestSurface(
+ config.getOutputConfigurations(), supportedPreviewSizes);
+ if (repeatingRequestSurface != null) {
+ suitableSurfaceCount++;
+ }
+
+ HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>();
+ for (int format : CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+ List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes(
+ config.getExtension(), format);
+ if (supportedSizes != null) {
+ supportedCaptureSizes.put(format, supportedSizes);
+ }
+ }
+ Surface burstCaptureSurface = CameraExtensionUtils.getBurstCaptureSurface(
+ config.getOutputConfigurations(), supportedCaptureSizes);
+ if (burstCaptureSurface != null) {
+ suitableSurfaceCount++;
+ }
+
+ if (suitableSurfaceCount != config.getOutputConfigurations().size()) {
+ throw new IllegalArgumentException("One or more unsupported output surfaces found!");
+ }
+
+ IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension(
+ config.getExtension());
+ extender.init(cameraId);
+
+ CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(clientId,
+ extender, cameraDevice, repeatingRequestSurface, burstCaptureSurface,
+ config.getStateCallback(), config.getExecutor());
+ ret.initialize();
+
+ return ret;
+ }
+
+ private CameraAdvancedExtensionSessionImpl(long extensionClientId,
+ @NonNull IAdvancedExtenderImpl extender, @NonNull CameraDevice cameraDevice,
+ @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
+ @NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor) {
+ mExtensionClientId = extensionClientId;
+ mAdvancedExtender = extender;
+ mCameraDevice = cameraDevice;
+ mCallbacks = callback;
+ mExecutor = executor;
+ mClientRepeatingRequestSurface = repeatingRequestSurface;
+ mClientCaptureSurface = burstCaptureSurface;
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mInitialized = false;
+ }
+
+ /**
+ * @hide
+ */
+ public synchronized void initialize() throws CameraAccessException, RemoteException {
+ if (mInitialized) {
+ Log.d(TAG, "Session already initialized");
+ return;
+ }
+
+ OutputSurface previewSurface = initializeParcelable(mClientRepeatingRequestSurface);
+ OutputSurface captureSurface = initializeParcelable(mClientCaptureSurface);
+ mSessionProcessor = mAdvancedExtender.getSessionProcessor();
+ CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mCameraDevice.getId(),
+ previewSurface, captureSurface);
+ List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
+ // map camera output ids to output configurations
+ HashMap<Integer, OutputConfiguration> cameraOutputs = new HashMap<>();
+ for (CameraOutputConfig output : outputConfigs) {
+ OutputConfiguration cameraOutput = null;
+ switch(output.type) {
+ case CameraOutputConfig.TYPE_SURFACE:
+ if (output.surface == null) {
+ Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
+ ", skipping!");
+ continue;
+ }
+ cameraOutput = new OutputConfiguration(output.surfaceGroupId,
+ output.surface);
+ break;
+ case CameraOutputConfig.TYPE_IMAGEREADER:
+ if ((output.imageFormat == ImageFormat.UNKNOWN) || (output.size.width <= 0) ||
+ (output.size.height <= 0)) {
+ Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
+ ", skipping!");
+ continue;
+ }
+ ImageReader reader = ImageReader.newInstance(output.size.width,
+ output.size.height, output.imageFormat, output.capacity);
+ mReaderMap.put(output.outputId.id, reader);
+ cameraOutput = new OutputConfiguration(output.surfaceGroupId,
+ reader.getSurface());
+ break;
+ case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER:
+ // TBD
+ default:
+ throw new IllegalArgumentException("Unsupported output config type: " +
+ output.type);
+ }
+ cameraOutput.setPhysicalCameraId(output.physicalCameraId);
+ cameraOutputs.put(output.outputId.id, cameraOutput);
+ }
+
+ ArrayList<OutputConfiguration> outputList = new ArrayList<>();
+ for (CameraOutputConfig output : outputConfigs) {
+ if (!cameraOutputs.containsKey(output.outputId.id)) {
+ // Shared surface already removed by a previous iteration
+ continue;
+ }
+ OutputConfiguration outConfig = cameraOutputs.get(output.outputId.id);
+ if ((output.surfaceSharingOutputConfigs != null) &&
+ !output.surfaceSharingOutputConfigs.isEmpty()) {
+ outConfig.enableSurfaceSharing();
+ for (OutputConfigId outputId : output.surfaceSharingOutputConfigs) {
+ outConfig.addSurface(cameraOutputs.get(outputId.id).getSurface());
+ cameraOutputs.remove(outputId.id);
+ }
+ }
+ outputList.add(outConfig);
+ mSurfaceIdMap.put(outConfig.getSurface(), output.outputId.id);
+ }
+
+ SessionConfiguration sessionConfiguration = new SessionConfiguration(
+ SessionConfiguration.SESSION_REGULAR, outputList,
+ new CameraExtensionUtils.HandlerExecutor(mHandler), new SessionStateHandler());
+
+ if ((sessionConfig.sessionParameter != null) &&
+ (!sessionConfig.sessionParameter.isEmpty())) {
+ CaptureRequest.Builder requestBuilder = mCameraDevice.createCaptureRequest(
+ sessionConfig.sessionTemplateId);
+ CaptureRequest sessionRequest = requestBuilder.build();
+ CameraMetadataNative.update(sessionRequest.getNativeMetadata(),
+ sessionConfig.sessionParameter);
+ sessionConfiguration.setSessionParameters(sessionRequest);
+ }
+
+ mCameraDevice.createCaptureSession(sessionConfiguration);
+ }
+
+ private static ParcelCaptureResult initializeParcelable(CaptureResult result) {
+ ParcelCaptureResult ret = new ParcelCaptureResult();
+ ret.cameraId = result.getCameraId();
+ ret.results = result.getNativeMetadata();
+ ret.parent = result.getRequest();
+ ret.sequenceId = result.getSequenceId();
+ ret.frameNumber = result.getFrameNumber();
+
+ return ret;
+ }
+
+ private static ParcelTotalCaptureResult initializeParcelable(TotalCaptureResult totalResult) {
+ ParcelTotalCaptureResult ret = new ParcelTotalCaptureResult();
+ ret.logicalCameraId = totalResult.getCameraId();
+ ret.results = totalResult.getNativeMetadata();
+ ret.parent = totalResult.getRequest();
+ ret.sequenceId = totalResult.getSequenceId();
+ ret.frameNumber = totalResult.getFrameNumber();
+ ret.sessionId = totalResult.getSessionId();
+ ret.partials = new ArrayList<>(totalResult.getPartialResults().size());
+ for (CaptureResult partial : totalResult.getPartialResults()) {
+ ret.partials.add(initializeParcelable(partial));
+ }
+ Map<String, TotalCaptureResult> physicalResults =
+ totalResult.getPhysicalCameraTotalResults();
+ ret.physicalResult = new ArrayList<>(physicalResults.size());
+ for (TotalCaptureResult physicalResult : physicalResults.values()) {
+ ret.physicalResult.add(new PhysicalCaptureResultInfo(physicalResult.getCameraId(),
+ physicalResult.getNativeMetadata()));
+ }
+
+ return ret;
+ }
+
+ private static OutputSurface initializeParcelable(Surface s) {
+ OutputSurface ret = new OutputSurface();
+ if (s != null) {
+ ret.surface = s;
+ ret.size = new android.hardware.camera2.extension.Size();
+ Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
+ ret.size.width = surfaceSize.getWidth();
+ ret.size.height = surfaceSize.getHeight();
+ ret.imageFormat = SurfaceUtils.getSurfaceFormat(s);
+ } else {
+ ret.surface = null;
+ ret.size = new android.hardware.camera2.extension.Size();
+ ret.size.width = -1;
+ ret.size.height = -1;
+ ret.imageFormat = ImageFormat.UNKNOWN;
+ }
+
+ return ret;
+ }
+
+ @Override
+ public @NonNull CameraDevice getDevice() {
+ synchronized (mInterfaceLock) {
+ return mCameraDevice;
+ }
+ }
+
+ @Override
+ public int setRepeatingRequest(@NonNull CaptureRequest request, @NonNull Executor executor,
+ @NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
+ int seqId = -1;
+ synchronized (mInterfaceLock) {
+ if (!mInitialized) {
+ throw new IllegalStateException("Uninitialized component");
+ }
+
+ if (mClientRepeatingRequestSurface == null) {
+ throw new IllegalArgumentException("No registered preview surface");
+ }
+
+ if (!request.containsTarget(mClientRepeatingRequestSurface) ||
+ (request.getTargets().size() != 1)) {
+ throw new IllegalArgumentException("Invalid repeating request output target!");
+ }
+
+ try {
+ seqId = mSessionProcessor.startRepeating(new RequestCallbackHandler(request,
+ executor, listener));
+ } catch (RemoteException e) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
+ "Failed to enable repeating request, extension service failed to respond!");
+ }
+ }
+
+ return seqId;
+ }
+
+ @Override
+ public int capture(@NonNull CaptureRequest request,
+ @NonNull Executor executor,
+ @NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
+ int seqId = -1;
+ synchronized (mInterfaceLock) {
+ if (!mInitialized) {
+ throw new IllegalStateException("Uninitialized component");
+ }
+
+ if (mClientCaptureSurface == null) {
+ throw new IllegalArgumentException("No output surface registered for single"
+ + " requests!");
+ }
+
+ if (!request.containsTarget(mClientCaptureSurface) ||
+ (request.getTargets().size() != 1)) {
+ throw new IllegalArgumentException("Invalid single capture output target!");
+ }
+
+ try {
+ // This will override the extension capture stage jpeg parameters with the user set
+ // jpeg quality and rotation. This will guarantee that client configured jpeg
+ // parameters always have highest priority.
+ Integer jpegRotation = request.get(CaptureRequest.JPEG_ORIENTATION);
+ if (jpegRotation == null) {
+ jpegRotation = CameraExtensionUtils.JPEG_DEFAULT_ROTATION;
+ }
+ Byte jpegQuality = request.get(CaptureRequest.JPEG_QUALITY);
+ if (jpegQuality == null) {
+ jpegQuality = CameraExtensionUtils.JPEG_DEFAULT_QUALITY;
+ }
+
+ seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
+ executor, listener), jpegRotation, jpegQuality);
+ } catch (RemoteException e) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
+ "Failed to submit capture request, extension service failed to respond!");
+ }
+ }
+
+ return seqId;
+ }
+
+ @Override
+ public void stopRepeating() throws CameraAccessException {
+ synchronized (mInterfaceLock) {
+ if (!mInitialized) {
+ throw new IllegalStateException("Uninitialized component");
+ }
+
+ mCaptureSession.stopRepeating();
+
+ try {
+ mSessionProcessor.stopRepeating();
+ } catch (RemoteException e) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
+ "Failed to notify about the end of repeating request, extension service"
+ + " failed to respond!");
+ }
+ }
+ }
+
+ @Override
+ public void close() throws CameraAccessException {
+ synchronized (mInterfaceLock) {
+ if (mInitialized) {
+ try {
+ mCaptureSession.stopRepeating();
+ mSessionProcessor.stopRepeating();
+ mSessionProcessor.onCaptureSessionEnd();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to stop the repeating request or end the session,"
+ + " , extension service does not respond!") ;
+ }
+ mCaptureSession.close();
+ }
+ }
+ }
+
+ public void release() {
+ synchronized (mInterfaceLock) {
+ mInitialized = false;
+ mHandlerThread.quitSafely();
+
+ if (mSessionProcessor != null) {
+ try {
+ mSessionProcessor.deInitSession();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to de-initialize session processor, extension service"
+ + " does not respond!") ;
+ }
+ mSessionProcessor = null;
+ }
+
+ if (mExtensionClientId >= 0) {
+ CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
+ }
+
+ for (ImageReader reader : mReaderMap.values()) {
+ reader.close();
+ }
+ mReaderMap.clear();
+
+ mClientRepeatingRequestSurface = null;
+ mClientCaptureSurface = null;
+ }
+ }
+
+ private void notifyConfigurationFailure() {
+ synchronized (mInterfaceLock) {
+ if (mInitialized) {
+ return;
+ }
+ }
+
+ release();
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallbacks.onConfigureFailed(
+ CameraAdvancedExtensionSessionImpl.this));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private class SessionStateHandler extends
+ android.hardware.camera2.CameraCaptureSession.StateCallback {
+ @Override
+ public void onClosed(@NonNull CameraCaptureSession session) {
+ release();
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallbacks.onClosed(
+ CameraAdvancedExtensionSessionImpl.this));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void onConfigureFailed(@NonNull CameraCaptureSession session) {
+ notifyConfigurationFailure();
+ }
+
+ @Override
+ public void onConfigured(@NonNull CameraCaptureSession session) {
+ boolean status = true;
+ synchronized (mInterfaceLock) {
+ mCaptureSession = session;
+ try {
+ mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
+ mInitialized = true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to start capture session,"
+ + " extension service does not respond!");
+ status = false;
+ session.close();
+ }
+ }
+
+ if (status) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallbacks.onConfigured(CameraAdvancedExtensionSessionImpl.this));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } else {
+ notifyConfigurationFailure();
+ }
+ }
+ }
+
+ private final class RequestCallbackHandler extends ICaptureCallback.Stub {
+ private final CaptureRequest mClientRequest;
+ private final Executor mClientExecutor;
+ private final ExtensionCaptureCallback mClientCallbacks;
+
+ private RequestCallbackHandler(@NonNull CaptureRequest clientRequest,
+ @NonNull Executor clientExecutor,
+ @NonNull ExtensionCaptureCallback clientCallbacks) {
+ mClientRequest = clientRequest;
+ mClientExecutor = clientExecutor;
+ mClientCallbacks = clientCallbacks;
+ }
+
+ @Override
+ public void onCaptureStarted(int captureSequenceId, long timestamp) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureStarted(
+ CameraAdvancedExtensionSessionImpl.this, mClientRequest,
+ timestamp));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void onCaptureProcessStarted(int captureSequenceId) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureProcessStarted(
+ CameraAdvancedExtensionSessionImpl.this, mClientRequest));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void onCaptureFailed(int captureSequenceId) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureFailed(
+ CameraAdvancedExtensionSessionImpl.this, mClientRequest));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int captureSequenceId) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureSequenceCompleted(
+ CameraAdvancedExtensionSessionImpl.this, captureSequenceId));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(int captureSequenceId) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureSequenceAborted(
+ CameraAdvancedExtensionSessionImpl.this, captureSequenceId));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ private final class CaptureCallbackHandler extends CameraCaptureSession.CaptureCallback {
+ private final IRequestCallback mCallback;
+
+ public CaptureCallbackHandler(IRequestCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onCaptureBufferLost(CameraCaptureSession session, CaptureRequest request,
+ Surface target, long frameNumber) {
+ try {
+ if (request.getTag() instanceof Integer) {
+ Integer requestId = (Integer) request.getTag();
+ mCallback.onCaptureBufferLost(requestId, frameNumber,
+ mSurfaceIdMap.get(target));
+ } else {
+ Log.e(TAG, "Invalid capture request tag!");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify lost capture buffer, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ try {
+ if (request.getTag() instanceof Integer) {
+ Integer requestId = (Integer) request.getTag();
+ mCallback.onCaptureCompleted(requestId, initializeParcelable(result));
+ } else {
+ Log.e(TAG, "Invalid capture request tag!");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture result, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
+ CaptureFailure failure) {
+ try {
+ if (request.getTag() instanceof Integer) {
+ Integer requestId = (Integer) request.getTag();
+ android.hardware.camera2.extension.CaptureFailure captureFailure =
+ new android.hardware.camera2.extension.CaptureFailure();
+ captureFailure.request = request;
+ captureFailure.reason = failure.getReason();
+ captureFailure.errorPhysicalCameraId = failure.getPhysicalCameraId();
+ captureFailure.frameNumber = failure.getFrameNumber();
+ captureFailure.sequenceId = failure.getSequenceId();
+ captureFailure.dropped = !failure.wasImageCaptured();
+ mCallback.onCaptureFailed(requestId, captureFailure);
+ } else {
+ Log.e(TAG, "Invalid capture request tag!");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture failure, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
+ CaptureResult partialResult) {
+ try {
+ if (request.getTag() instanceof Integer) {
+ Integer requestId = (Integer) request.getTag();
+ mCallback.onCaptureProgressed(requestId, initializeParcelable(partialResult));
+ } else {
+ Log.e(TAG, "Invalid capture request tag!");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture partial result, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(CameraCaptureSession session, int sequenceId) {
+ try {
+ mCallback.onCaptureSequenceAborted(sequenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify aborted sequence, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId,
+ long frameNumber) {
+ try {
+ mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify sequence complete, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
+ long timestamp, long frameNumber) {
+ try {
+ if (request.getTag() instanceof Integer) {
+ Integer requestId = (Integer) request.getTag();
+ mCallback.onCaptureStarted(requestId, frameNumber, timestamp);
+ } else {
+ Log.e(TAG, "Invalid capture request tag!");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture started, extension service doesn't"
+ + " respond!");
+ }
+ }
+ }
+
+ private static final class ImageReaderHandler implements ImageReader.OnImageAvailableListener {
+ private final OutputConfigId mOutputConfigId;
+ private final IImageProcessorImpl mIImageProcessor;
+
+ private ImageReaderHandler(int outputConfigId,
+ IImageProcessorImpl iImageProcessor) {
+ mOutputConfigId = new OutputConfigId();
+ mOutputConfigId.id = outputConfigId;
+ mIImageProcessor = iImageProcessor;
+ }
+
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ if (mIImageProcessor == null) {
+ return;
+ }
+
+ Image img;
+ try {
+ img = reader.acquireNextImage();
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to acquire image, too many images pending!");
+ return;
+ }
+ if (img == null) {
+ Log.e(TAG, "Invalid image!");
+ return;
+ }
+
+ try {
+ reader.detachImage(img);
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to detach image");
+ img.close();
+ return;
+ }
+
+ ParcelImage parcelImage = new ParcelImage();
+ parcelImage.buffer = img.getHardwareBuffer();
+ if (img.getFenceFd() >= 0) {
+ try {
+ parcelImage.fence = ParcelFileDescriptor.fromFd(img.getFenceFd());
+ } catch (IOException e) {
+ Log.e(TAG,"Failed to parcel buffer fence!");
+ }
+ }
+ parcelImage.format = img.getFormat();
+ parcelImage.timestamp = img.getTimestamp();
+ parcelImage.transform = img.getTransform();
+ parcelImage.scalingMode = img.getScalingMode();
+ parcelImage.planeCount = img.getPlaneCount();
+ parcelImage.crop = img.getCropRect();
+
+ try {
+ mIImageProcessor.onNextImageAvailable(mOutputConfigId, parcelImage);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to propagate image buffer on output surface id: " +
+ mOutputConfigId + " extension service does not respond!");
+ } finally {
+ parcelImage.buffer.close();
+ img.close();
+ }
+ }
+ }
+
+ private final class RequestProcessor extends IRequestProcessorImpl.Stub {
+ @Override
+ public void setImageProcessor(OutputConfigId outputConfigId,
+ IImageProcessorImpl imageProcessor) {
+ synchronized (mInterfaceLock) {
+ if (mReaderMap.containsKey(outputConfigId.id)) {
+ mReaderMap.get(outputConfigId.id).setOnImageAvailableListener(
+ new ImageReaderHandler(outputConfigId.id, imageProcessor), mHandler);
+ } else {
+ Log.e(TAG, "ImageReader with output config id: " + outputConfigId.id +
+ " not found!");
+ }
+ }
+ }
+
+ @Override
+ public boolean submit(Request request, IRequestCallback callback) {
+ ArrayList<Request> captureList = new ArrayList<>();
+ captureList.add(request);
+ return submitBurst(captureList, callback);
+ }
+
+ @Override
+ public boolean submitBurst(List<Request> requests, IRequestCallback callback) {
+ synchronized (mInterfaceLock) {
+ try {
+ CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
+ ArrayList<CaptureRequest> captureRequests = new ArrayList<>();
+ for (Request request : requests) {
+ captureRequests.add(initializeCaptureRequest(mCameraDevice, request,
+ mSurfaceIdMap));
+ }
+ mCaptureSession.captureBurstRequests(captureRequests,
+ new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed to submit capture requests!");
+ return false;
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean setRepeating(Request request, IRequestCallback callback) {
+ synchronized (mInterfaceLock) {
+ try {
+ CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice,
+ request, mSurfaceIdMap);
+ CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
+ mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
+ new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed to enable repeating request!");
+ return false;
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void abortCaptures() {
+ synchronized (mInterfaceLock) {
+ try {
+ mCaptureSession.abortCaptures();
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed during capture abort!");
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
+ }
+ }
+ }
+
+ @Override
+ public void stopRepeating() {
+ synchronized (mInterfaceLock) {
+ try {
+ mCaptureSession.stopRepeating();
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed during repeating capture stop!");
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
+ }
+ }
+ }
+ }
+
+ private static CaptureRequest initializeCaptureRequest(CameraDevice cameraDevice,
+ Request request, HashMap<Surface, Integer> surfaceIdMap) throws CameraAccessException {
+ CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(request.templateId);
+ for (OutputConfigId configId : request.targetOutputConfigIds) {
+ boolean found = false;
+ for (Map.Entry<Surface, Integer> entry : surfaceIdMap.entrySet()) {
+ if (entry.getValue() == configId.id) {
+ builder.addTarget(entry.getKey());
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ Log.e(TAG, "Surface with output id: " + configId.id +
+ " not found among registered camera outputs!");
+ }
+ }
+
+ builder.setTag(request.requestId);
+ CaptureRequest ret = builder.build();
+ CameraMetadataNative.update(ret.getNativeMetadata(), request.parameters);
+ return ret;
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index b578bf8..11b137ca 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -25,6 +25,7 @@
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.CameraExtensionSession;
import android.hardware.camera2.CameraOfflineSession;
import android.hardware.camera2.CameraDevice;
@@ -138,6 +139,7 @@
private CameraCaptureSessionCore mCurrentSession;
private CameraExtensionSessionImpl mCurrentExtensionSession;
+ private CameraAdvancedExtensionSessionImpl mCurrentAdvancedExtensionSession;
private int mNextSessionId = 0;
private final int mAppTargetSdkVersion;
@@ -1343,6 +1345,12 @@
mCurrentExtensionSession.release();
mCurrentExtensionSession = null;
}
+
+ if (mCurrentAdvancedExtensionSession != null) {
+ mCurrentAdvancedExtensionSession.release();
+ mCurrentAdvancedExtensionSession = null;
+ }
+
// Only want to fire the onClosed callback once;
// either a normal close where the remote device is valid
// or a close after a startup error (no remote device but in error state)
@@ -2395,9 +2403,14 @@
public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
throws CameraAccessException {
try {
- mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(this,
- mContext,
- extensionConfiguration);
+ if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) {
+ mCurrentAdvancedExtensionSession =
+ CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession(
+ this, mContext, extensionConfiguration);
+ } else {
+ mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(
+ this, mContext, extensionConfiguration);
+ }
} catch (RemoteException e) {
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
}
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 936734b..3b1cb94 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -16,6 +16,9 @@
package android.hardware.camera2.impl;
+import static android.hardware.camera2.impl.CameraExtensionUtils.JPEG_DEFAULT_QUALITY;
+import static android.hardware.camera2.impl.CameraExtensionUtils.JPEG_DEFAULT_ROTATION;
+
import android.annotation.NonNull;
import android.graphics.ImageFormat;
import android.hardware.camera2.CaptureResult;
@@ -42,8 +45,6 @@
public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl {
public final static String TAG = "CameraExtensionJpeg";
private final static int JPEG_QUEUE_SIZE = 1;
- private final static int JPEG_DEFAULT_QUALITY = 100;
- private final static int JPEG_DEFAULT_ROTATION = 0;
private final Handler mHandler;
private final HandlerThread mHandlerThread;
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 3d771c01..5339f41 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -110,79 +110,10 @@
// Lock to synchronize cross-thread access to device public interface
final Object mInterfaceLock = new Object(); // access from this class and Session only!
- private static class SurfaceInfo {
- public int mWidth = 0;
- public int mHeight = 0;
- public int mFormat = PixelFormat.RGBA_8888;
- public long mUsage = 0;
- }
-
- private static final int SUPPORTED_CAPTURE_OUTPUT_FORMATS[] = {
- CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
- ImageFormat.JPEG
- };
-
- private static int nativeGetSurfaceWidth(Surface surface) {
- return SurfaceUtils.getSurfaceSize(surface).getWidth();
- }
-
- private static int nativeGetSurfaceHeight(Surface surface) {
- return SurfaceUtils.getSurfaceSize(surface).getHeight();
- }
-
private static int nativeGetSurfaceFormat(Surface surface) {
return SurfaceUtils.getSurfaceFormat(surface);
}
- private static Surface getBurstCaptureSurface(
- @NonNull List<OutputConfiguration> outputConfigs,
- @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
- for (OutputConfiguration config : outputConfigs) {
- SurfaceInfo surfaceInfo = querySurface(config.getSurface());
- for (int supportedFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
- if (surfaceInfo.mFormat == supportedFormat) {
- Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
- if (supportedCaptureSizes.containsKey(supportedFormat)) {
- if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
- return config.getSurface();
- } else {
- throw new IllegalArgumentException("Capture size not supported!");
- }
- }
- return config.getSurface();
- }
- }
- }
-
- return null;
- }
-
- private static @Nullable Surface getRepeatingRequestSurface(
- @NonNull List<OutputConfiguration> outputConfigs,
- @Nullable List<Size> supportedPreviewSizes) {
- for (OutputConfiguration config : outputConfigs) {
- SurfaceInfo surfaceInfo = querySurface(config.getSurface());
- if ((surfaceInfo.mFormat ==
- CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
- // The default RGBA_8888 is also implicitly supported because camera will
- // internally override it to
- // 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
- (surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
- Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
- surfaceInfo.mHeight);
- if ((supportedPreviewSizes == null) ||
- (!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
- throw new IllegalArgumentException("Repeating request surface size " +
- repeatingRequestSurfaceSize + " not supported!");
- }
-
- return config.getSurface();
- }
- }
-
- return null;
- }
-
/**
* @hide
*/
@@ -221,22 +152,22 @@
int suitableSurfaceCount = 0;
List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
config.getExtension(), SurfaceTexture.class);
- Surface repeatingRequestSurface = getRepeatingRequestSurface(
+ Surface repeatingRequestSurface = CameraExtensionUtils.getRepeatingRequestSurface(
config.getOutputConfigurations(), supportedPreviewSizes);
if (repeatingRequestSurface != null) {
suitableSurfaceCount++;
}
HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>();
- for (int format : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+ for (int format : CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes(
config.getExtension(), format);
if (supportedSizes != null) {
supportedCaptureSizes.put(format, supportedSizes);
}
}
- Surface burstCaptureSurface = getBurstCaptureSurface(config.getOutputConfigurations(),
- supportedCaptureSizes);
+ Surface burstCaptureSurface = CameraExtensionUtils.getBurstCaptureSurface(
+ config.getOutputConfigurations(), supportedCaptureSizes);
if (burstCaptureSurface != null) {
suitableSurfaceCount++;
}
@@ -266,15 +197,15 @@
return session;
}
- private CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
- @NonNull IPreviewExtenderImpl previewExtender,
- @NonNull List<Size> previewSizes,
- long extensionClientId,
- @NonNull CameraDevice cameraDevice,
- @Nullable Surface repeatingRequestSurface,
- @Nullable Surface burstCaptureSurface,
- @NonNull StateCallback callback,
- @NonNull Executor executor) {
+ public CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
+ @NonNull IPreviewExtenderImpl previewExtender,
+ @NonNull List<Size> previewSizes,
+ long extensionClientId,
+ @NonNull CameraDevice cameraDevice,
+ @Nullable Surface repeatingRequestSurface,
+ @Nullable Surface burstCaptureSurface,
+ @NonNull StateCallback callback,
+ @NonNull Executor executor) {
mExtensionClientId = extensionClientId;
mImageExtender = imageExtender;
mPreviewExtender = previewExtender;
@@ -290,57 +221,17 @@
mInitialized = false;
}
- private static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
- ImageWriter writer = null;
- Image img = null;
- SurfaceInfo surfaceInfo = new SurfaceInfo();
- int nativeFormat = nativeGetSurfaceFormat(s);
- int dataspace = SurfaceUtils.getSurfaceDataspace(s);
- // Jpeg surfaces cannot be queried for their usage and other parameters
- // in the usual way below. A buffer can only be de-queued after the
- // producer overrides the surface dimensions to (width*height) x 1.
- if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
- (dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
- surfaceInfo.mFormat = ImageFormat.JPEG;
- surfaceInfo.mWidth = nativeGetSurfaceWidth(s);
- surfaceInfo.mHeight = nativeGetSurfaceHeight(s);
- return surfaceInfo;
- }
-
- HardwareBuffer buffer = null;
- try {
- writer = ImageWriter.newInstance(s, 1);
- img = writer.dequeueInputImage();
- buffer = img.getHardwareBuffer();
- surfaceInfo.mFormat = buffer.getFormat();
- surfaceInfo.mWidth = buffer.getWidth();
- surfaceInfo.mHeight = buffer.getHeight();
- surfaceInfo.mUsage = buffer.getUsage();
- } catch (Exception e) {
- Log.e(TAG, "Failed to query surface, returning defaults!");
- } finally {
- if (buffer != null) {
- buffer.close();
- }
- if (img != null) {
- img.close();
- }
- if (writer != null) {
- writer.close();
- }
- }
-
- return surfaceInfo;
- }
-
private void initializeRepeatingRequestPipeline() throws RemoteException {
- SurfaceInfo repeatingSurfaceInfo = new SurfaceInfo();
+ CameraExtensionUtils.SurfaceInfo repeatingSurfaceInfo =
+ new CameraExtensionUtils.SurfaceInfo();
mPreviewProcessorType = mPreviewExtender.getProcessorType();
if (mClientRepeatingRequestSurface != null) {
- repeatingSurfaceInfo = querySurface(mClientRepeatingRequestSurface);
+ repeatingSurfaceInfo = CameraExtensionUtils.querySurface(
+ mClientRepeatingRequestSurface);
} else {
// Make the intermediate surface behave as any regular 'SurfaceTexture'
- SurfaceInfo captureSurfaceInfo = querySurface(mClientCaptureSurface);
+ CameraExtensionUtils.SurfaceInfo captureSurfaceInfo = CameraExtensionUtils.querySurface(
+ mClientCaptureSurface);
Size captureSize = new Size(captureSurfaceInfo.mWidth, captureSurfaceInfo.mHeight);
Size previewSize = findSmallestAspectMatchedSize(mSupportedPreviewSizes, captureSize);
repeatingSurfaceInfo.mWidth = previewSize.getWidth();
@@ -418,7 +309,8 @@
if (mImageProcessor != null) {
if (mClientCaptureSurface != null) {
- SurfaceInfo surfaceInfo = querySurface(mClientCaptureSurface);
+ CameraExtensionUtils.SurfaceInfo surfaceInfo = CameraExtensionUtils.querySurface(
+ mClientCaptureSurface);
if (surfaceInfo.mFormat == ImageFormat.JPEG) {
mImageJpegProcessor = new CameraExtensionJpegProcessor(mImageProcessor);
mImageProcessor = mImageJpegProcessor;
@@ -501,7 +393,7 @@
SessionConfiguration sessionConfig = new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR,
outputList,
- new HandlerExecutor(mHandler),
+ new CameraExtensionUtils.HandlerExecutor(mHandler),
new SessionStateHandler());
if (!sessionParamsList.isEmpty()) {
@@ -656,7 +548,8 @@
throw new UnsupportedOperationException("Failed to create still capture burst request");
}
- return mCaptureSession.captureBurstRequests(burstRequest, new HandlerExecutor(mHandler),
+ return mCaptureSession.captureBurstRequests(burstRequest,
+ new CameraExtensionUtils.HandlerExecutor(mHandler),
new BurstRequestHandler(request, executor, listener, requestMap,
mBurstCaptureImageCallback));
}
@@ -724,7 +617,7 @@
CaptureRequest repeatingRequest = createRequest(mCameraDevice,
captureStageList, mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW);
return mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
- new HandlerExecutor(mHandler), requestHandler);
+ new CameraExtensionUtils.HandlerExecutor(mHandler), requestHandler);
}
/** @hide */
@@ -1705,23 +1598,6 @@
return ret;
}
- private final class HandlerExecutor implements Executor {
- private final Handler mHandler;
-
- public HandlerExecutor(Handler handler) {
- mHandler = handler;
- }
-
- @Override
- public void execute(Runnable runCmd) {
- try {
- mHandler.post(runCmd);
- } catch (RejectedExecutionException e) {
- Log.w(TAG, "Handler thread unavailable, skipping message!");
- }
- }
- }
-
private static ParcelImage initializeParcelImage(Image img) {
ParcelImage parcelImage = new ParcelImage();
parcelImage.buffer = img.getHardwareBuffer();
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
new file mode 100644
index 0000000..950d716b
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.impl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
+import android.hardware.camera2.CameraExtensionCharacteristics;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.Image;
+import android.media.ImageWriter;
+import android.os.Handler;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+public final class CameraExtensionUtils {
+ private static final String TAG = "CameraExtensionUtils";
+
+ public final static int JPEG_DEFAULT_QUALITY = 100;
+ public final static int JPEG_DEFAULT_ROTATION = 0;
+
+ public static final int[] SUPPORTED_CAPTURE_OUTPUT_FORMATS = {
+ CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
+ ImageFormat.JPEG
+ };
+
+ public static class SurfaceInfo {
+ public int mWidth = 0;
+ public int mHeight = 0;
+ public int mFormat = PixelFormat.RGBA_8888;
+ public long mUsage = 0;
+ }
+
+ public static final class HandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public HandlerExecutor(Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void execute(Runnable runCmd) {
+ try {
+ mHandler.post(runCmd);
+ } catch (RejectedExecutionException e) {
+ Log.w(TAG, "Handler thread unavailable, skipping message!");
+ }
+ }
+ }
+
+ public static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
+ ImageWriter writer = null;
+ Image img = null;
+ SurfaceInfo surfaceInfo = new SurfaceInfo();
+ int nativeFormat = SurfaceUtils.getSurfaceFormat(s);
+ int dataspace = SurfaceUtils.getSurfaceDataspace(s);
+ // Jpeg surfaces cannot be queried for their usage and other parameters
+ // in the usual way below. A buffer can only be de-queued after the
+ // producer overrides the surface dimensions to (width*height) x 1.
+ if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
+ (dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
+ surfaceInfo.mFormat = ImageFormat.JPEG;
+ Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
+ surfaceInfo.mWidth = surfaceSize.getWidth();
+ surfaceInfo.mHeight = surfaceSize.getHeight();
+ return surfaceInfo;
+ }
+
+ HardwareBuffer buffer = null;
+ try {
+ writer = ImageWriter.newInstance(s, 1);
+ img = writer.dequeueInputImage();
+ buffer = img.getHardwareBuffer();
+ surfaceInfo.mFormat = buffer.getFormat();
+ surfaceInfo.mWidth = buffer.getWidth();
+ surfaceInfo.mHeight = buffer.getHeight();
+ surfaceInfo.mUsage = buffer.getUsage();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to query surface, returning defaults!");
+ } finally {
+ if (buffer != null) {
+ buffer.close();
+ }
+ if (img != null) {
+ img.close();
+ }
+ if (writer != null) {
+ writer.close();
+ }
+ }
+
+ return surfaceInfo;
+ }
+
+ public static Surface getBurstCaptureSurface(
+ @NonNull List<OutputConfiguration> outputConfigs,
+ @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
+ for (OutputConfiguration config : outputConfigs) {
+ SurfaceInfo surfaceInfo = querySurface(config.getSurface());
+ for (int supportedFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+ if (surfaceInfo.mFormat == supportedFormat) {
+ Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
+ if (supportedCaptureSizes.containsKey(supportedFormat)) {
+ if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
+ return config.getSurface();
+ } else {
+ throw new IllegalArgumentException("Capture size not supported!");
+ }
+ }
+ return config.getSurface();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public static @Nullable Surface getRepeatingRequestSurface(
+ @NonNull List<OutputConfiguration> outputConfigs,
+ @Nullable List<Size> supportedPreviewSizes) {
+ for (OutputConfiguration config : outputConfigs) {
+ SurfaceInfo surfaceInfo = querySurface(config.getSurface());
+ if ((surfaceInfo.mFormat ==
+ CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
+ // The default RGBA_8888 is also implicitly supported because camera will
+ // internally override it to
+ // 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
+ (surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
+ Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
+ surfaceInfo.mHeight);
+ if ((supportedPreviewSizes == null) ||
+ (!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
+ throw new IllegalArgumentException("Repeating request surface size " +
+ repeatingRequestSurfaceSize + " not supported!");
+ }
+
+ return config.getSurface();
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 2d58520..dce3fef 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -190,6 +190,8 @@
* has a preference.
* @param requestedModeId The preferred mode id for the top-most visible window that has a
* preference.
+ * @param requestedMaxRefreshRate The preferred highest refresh rate for the top-most visible
+ * window that has a preference.
* @param requestedMinimalPostProcessing The preferred minimal post processing setting for the
* display. This is true when there is at least one visible window that wants minimal post
* processng on.
@@ -197,8 +199,8 @@
* prior to call to performTraversalInTransactionFromWindowManager.
*/
public abstract void setDisplayProperties(int displayId, boolean hasContent,
- float requestedRefreshRate, int requestedModeId, boolean requestedMinimalPostProcessing,
- boolean inTraversal);
+ float requestedRefreshRate, int requestedModeId, float requestedMaxRefreshRate,
+ boolean requestedMinimalPostProcessing, boolean inTraversal);
/**
* Applies an offset to the contents of a display, for example to avoid burn-in.
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 9a27a99..55c90ce 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -69,8 +69,6 @@
private static final int MSG_SET_FEATURE_COMPLETED = 107;
private static final int MSG_CHALLENGE_GENERATED = 108;
private static final int MSG_FACE_DETECTED = 109;
- private static final int MSG_CHALLENGE_INTERRUPTED = 110;
- private static final int MSG_CHALLENGE_INTERRUPT_FINISHED = 111;
private static final int MSG_AUTHENTICATION_FRAME = 112;
private static final int MSG_ENROLLMENT_FRAME = 113;
@@ -102,8 +100,8 @@
@Override // binder call
public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) {
- mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0,
- face).sendToTarget();
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId,
+ isStrongBiometric ? 1 : 0, face).sendToTarget();
}
@Override // binder call
@@ -142,22 +140,12 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) {
- mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge)
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
+ mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, userId, challenge)
.sendToTarget();
}
@Override
- public void onChallengeInterrupted(int sensorId) {
- mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPTED, sensorId).sendToTarget();
- }
-
- @Override
- public void onChallengeInterruptFinished(int sensorId) {
- mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPT_FINISHED, sensorId).sendToTarget();
- }
-
- @Override
public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
mHandler.obtainMessage(MSG_AUTHENTICATION_FRAME, frame).sendToTarget();
}
@@ -434,16 +422,14 @@
*
* @see com.android.server.locksettings.LockSettingsService
*
- * TODO(b/171335732): should take userId
- *
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- 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, 0 /* userId */, mServiceReceiver,
+ mService.generateChallenge(mToken, sensorId, userId, mServiceReceiver,
mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -452,12 +438,13 @@
}
/**
- * Same as {@link #generateChallenge(int, GenerateChallengeCallback)}, but assumes the first
- * enumerated sensor.
+ * Same as {@link #generateChallenge(int, int, GenerateChallengeCallback)}, but assumes the
+ * first enumerated sensor.
+ *
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void generateChallenge(GenerateChallengeCallback callback) {
+ public void generateChallenge(int userId, GenerateChallengeCallback callback) {
final List<FaceSensorPropertiesInternal> faceSensorProperties =
getSensorPropertiesInternal();
if (faceSensorProperties.isEmpty()) {
@@ -466,7 +453,7 @@
}
final int sensorId = faceSensorProperties.get(0).sensorId;
- generateChallenge(sensorId, callback);
+ generateChallenge(sensorId, userId, callback);
}
/**
@@ -1120,25 +1107,16 @@
}
/**
- * Callback structure provided to {@link #generateChallenge(int, GenerateChallengeCallback)}.
+ * Callback structure provided to {@link #generateChallenge(int, int,
+ * GenerateChallengeCallback)}.
+ *
* @hide
*/
public interface GenerateChallengeCallback {
/**
* Invoked when a challenge has been generated.
*/
- void onGenerateChallengeResult(int sensorId, long challenge);
-
- /**
- * Invoked if the challenge has not been revoked and a subsequent caller/owner invokes
- * {@link #generateChallenge(int, GenerateChallengeCallback)}, but
- */
- default void onChallengeInterrupted(int sensorId) {}
-
- /**
- * Invoked when the interrupting client has finished (e.g. revoked its challenge).
- */
- default void onChallengeInterruptFinished(int sensorId) {}
+ void onGenerateChallengeResult(int sensorId, int userId, long challenge);
}
private class OnEnrollCancelListener implements OnCancelListener {
@@ -1212,18 +1190,13 @@
args.recycle();
break;
case MSG_CHALLENGE_GENERATED:
- sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */);
+ sendChallengeGenerated(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
+ (long) msg.obj /* challenge */);
break;
case MSG_FACE_DETECTED:
sendFaceDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
(boolean) msg.obj /* isStrongBiometric */);
break;
- case MSG_CHALLENGE_INTERRUPTED:
- sendChallengeInterrupted((int) msg.obj /* sensorId */);
- break;
- case MSG_CHALLENGE_INTERRUPT_FINISHED:
- sendChallengeInterruptFinished((int) msg.obj /* sensorId */);
- break;
case MSG_AUTHENTICATION_FRAME:
sendAuthenticationFrame((FaceAuthenticationFrame) msg.obj /* frame */);
break;
@@ -1251,11 +1224,11 @@
mGetFeatureCallback.onCompleted(success, features, featureState);
}
- private void sendChallengeGenerated(int sensorId, long challenge) {
+ private void sendChallengeGenerated(int sensorId, int userId, long challenge) {
if (mGenerateChallengeCallback == null) {
return;
}
- mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, challenge);
+ mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, userId, challenge);
}
private void sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
@@ -1266,22 +1239,6 @@
mFaceDetectionCallback.onFaceDetected(sensorId, userId, isStrongBiometric);
}
- private void sendChallengeInterrupted(int sensorId) {
- if (mGenerateChallengeCallback == null) {
- Slog.e(TAG, "sendChallengeInterrupted, callback null");
- return;
- }
- mGenerateChallengeCallback.onChallengeInterrupted(sensorId);
- }
-
- private void sendChallengeInterruptFinished(int sensorId) {
- if (mGenerateChallengeCallback == null) {
- Slog.e(TAG, "sendChallengeInterruptFinished, callback null");
- return;
- }
- mGenerateChallengeCallback.onChallengeInterruptFinished(sensorId);
- }
-
private void sendRemovedResult(Face face, int remaining) {
if (mRemovalCallback == null) {
return;
diff --git a/core/java/android/hardware/face/FaceServiceReceiver.java b/core/java/android/hardware/face/FaceServiceReceiver.java
index 9e62ca5..9e78592 100644
--- a/core/java/android/hardware/face/FaceServiceReceiver.java
+++ b/core/java/android/hardware/face/FaceServiceReceiver.java
@@ -72,17 +72,8 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
-
- }
-
- @Override
- public void onChallengeInterrupted(int sensorId) throws RemoteException {
-
- }
-
- @Override
- public void onChallengeInterruptFinished(int sensorId) throws RemoteException {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge)
+ throws RemoteException {
}
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index 0ccb395..c4d9bf2 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -33,9 +33,7 @@
void onRemoved(in Face face, int remaining);
void onFeatureSet(boolean success, int feature);
void onFeatureGet(boolean success, in int[] features, in boolean[] featureState);
- void onChallengeGenerated(int sensorId, long challenge);
- void onChallengeInterrupted(int sensorId);
- void onChallengeInterruptFinished(int sensorId);
+ void onChallengeGenerated(int sensorId, int userId, long challenge);
void onAuthenticationFrame(in FaceAuthenticationFrame frame);
void onEnrollmentFrame(in FaceEnrollFrame frame);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b52955d..8aeb5cd 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -475,10 +475,13 @@
}
/**
+ * Callbacks for generate challenge operations.
+ *
* @hide
*/
public interface GenerateChallengeCallback {
- void onChallengeGenerated(int sensorId, long challenge);
+ /** Called when a challenged has been generated. */
+ void onChallengeGenerated(int sensorId, int userId, long challenge);
}
/**
@@ -1124,7 +1127,8 @@
sendRemovedResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
break;
case MSG_CHALLENGE_GENERATED:
- sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */);
+ sendChallengeGenerated(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
+ (long) msg.obj /* challenge */);
break;
case MSG_FINGERPRINT_DETECTED:
sendFingerprintDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
@@ -1233,12 +1237,12 @@
}
}
- private void sendChallengeGenerated(int sensorId, long challenge) {
+ private void sendChallengeGenerated(int sensorId, int userId, long challenge) {
if (mGenerateChallengeCallback == null) {
Slog.e(TAG, "sendChallengeGenerated, callback null");
return;
}
- mGenerateChallengeCallback.onChallengeGenerated(sensorId, challenge);
+ mGenerateChallengeCallback.onChallengeGenerated(sensorId, userId, challenge);
}
private void sendFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) {
@@ -1454,8 +1458,8 @@
}
@Override // binder call
- public void onChallengeGenerated(int sensorId, long challenge) {
- mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge)
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
+ mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, userId, challenge)
.sendToTarget();
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java b/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
index 798e87b..a9779b5 100644
--- a/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
+++ b/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
@@ -61,7 +61,8 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge)
+ throws RemoteException {
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 1bd284d..9cea1fe 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -29,7 +29,7 @@
void onAuthenticationFailed();
void onError(int error, int vendorCode);
void onRemoved(in Fingerprint fp, int remaining);
- void onChallengeGenerated(int sensorId, long challenge);
+ void onChallengeGenerated(int sensorId, int userId, long challenge);
void onUdfpsPointerDown(int sensorId);
void onUdfpsPointerUp(int sensorId);
}
diff --git a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
index 1551e07..f4d22da 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
@@ -35,7 +35,7 @@
* UdfpsController will call this method when the HBM is enabled.
*
* @param hbmType The type of HBM that was enabled. See
- * {@link com.android.systemui.biometrics.HbmTypes}.
+ * {@link com.android.systemui.biometrics.UdfpsHbmTypes}.
* @param displayId The displayId for which the HBM is enabled. See
* {@link android.view.Display#getDisplayId()}.
*/
@@ -45,7 +45,7 @@
* UdfpsController will call this method when the HBM is disabled.
*
* @param hbmType The type of HBM that was disabled. See
- * {@link com.android.systemui.biometrics.HbmTypes}.
+ * {@link com.android.systemui.biometrics.UdfpsHbmTypes}.
* @param displayId The displayId for which the HBM is disabled. See
* {@link android.view.Display#getDisplayId()}.
*/
diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS
index 16c15e3..60d43fd 100644
--- a/core/java/android/hardware/hdmi/OWNERS
+++ b/core/java/android/hardware/hdmi/OWNERS
@@ -4,3 +4,4 @@
marvinramin@google.com
nchalko@google.com
+lcnathalie@google.com
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 7ebb646..d8050ed 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -34,6 +34,8 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
+import android.net.ConnectivityAnnotations.MultipathPreference;
+import android.net.ConnectivityAnnotations.RestrictBackgroundStatus;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.os.Build;
@@ -463,6 +465,7 @@
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+ @RestrictBackgroundStatus
public int getRestrictBackgroundStatus(int uid) {
try {
return mService.getRestrictBackgroundStatus(uid);
@@ -588,6 +591,7 @@
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+ @MultipathPreference
public int getMultipathPreference(@NonNull Network network) {
try {
return mService.getMultipathPreference(network);
diff --git a/core/java/android/net/NetworkStateSnapshot.java b/core/java/android/net/NetworkStateSnapshot.java
index 9df861a..3915634 100644
--- a/core/java/android/net/NetworkStateSnapshot.java
+++ b/core/java/android/net/NetworkStateSnapshot.java
@@ -104,7 +104,10 @@
return mSubscriberId;
}
- /** Get the legacy type of the network associated with this snapshot. */
+ /**
+ * Get the legacy type of the network associated with this snapshot.
+ * @return the legacy network type. See {@code ConnectivityManager#TYPE_*}.
+ */
public int getLegacyType() {
return mLegacyType;
}
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index d41c0b4..caab152 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -52,12 +52,17 @@
private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
@NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;
+ private static final String IS_TEST_MODE_PROFILE_KEY = "mIsTestModeProfile";
+ private final boolean mIsTestModeProfile;
+
private VcnConfig(
@NonNull String packageName,
- @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs) {
+ @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs,
+ boolean isTestModeProfile) {
mPackageName = packageName;
mGatewayConnectionConfigs =
Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs));
+ mIsTestModeProfile = isTestModeProfile;
validate();
}
@@ -77,6 +82,7 @@
new ArraySet<>(
PersistableBundleUtils.toList(
gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new));
+ mIsTestModeProfile = in.getBoolean(IS_TEST_MODE_PROFILE_KEY);
validate();
}
@@ -104,6 +110,15 @@
}
/**
+ * Returns whether or not this VcnConfig is restricted to test networks.
+ *
+ * @hide
+ */
+ public boolean isTestModeProfile() {
+ return mIsTestModeProfile;
+ }
+
+ /**
* Serializes this object to a PersistableBundle.
*
* @hide
@@ -119,13 +134,14 @@
new ArrayList<>(mGatewayConnectionConfigs),
VcnGatewayConnectionConfig::toPersistableBundle);
result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle);
+ result.putBoolean(IS_TEST_MODE_PROFILE_KEY, mIsTestModeProfile);
return result;
}
@Override
public int hashCode() {
- return Objects.hash(mPackageName, mGatewayConnectionConfigs);
+ return Objects.hash(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile);
}
@Override
@@ -136,7 +152,8 @@
final VcnConfig rhs = (VcnConfig) other;
return mPackageName.equals(rhs.mPackageName)
- && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
+ && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs)
+ && mIsTestModeProfile == rhs.mIsTestModeProfile;
}
// Parcelable methods
@@ -172,6 +189,8 @@
@NonNull
private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
+ private boolean mIsTestModeProfile = false;
+
public Builder(@NonNull Context context) {
Objects.requireNonNull(context, "context was null");
@@ -207,13 +226,29 @@
}
/**
+ * Restricts this VcnConfig to matching with test networks (only).
+ *
+ * <p>This method is for testing only, and must not be used by apps. Calling {@link
+ * VcnManager#setVcnConfig(ParcelUuid, VcnConfig)} with a VcnConfig where test-network usage
+ * is enabled will require the MANAGE_TEST_NETWORKS permission.
+ *
+ * @return this {@link Builder} instance, for chaining
+ * @hide
+ */
+ @NonNull
+ public Builder setIsTestModeProfile() {
+ mIsTestModeProfile = true;
+ return this;
+ }
+
+ /**
* Builds and validates the VcnConfig.
*
* @return an immutable VcnConfig instance
*/
@NonNull
public VcnConfig build() {
- return new VcnConfig(mPackageName, mGatewayConnectionConfigs);
+ return new VcnConfig(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile);
}
}
}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index f02346b..7eea0b1 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -15,6 +15,8 @@
*/
package android.net.vcn;
+import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.IntDef;
@@ -438,6 +440,8 @@
* distinguish between VcnGatewayConnectionConfigs configured on a single {@link
* VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations.
* @param tunnelConnectionParams the IKE tunnel connection configuration
+ * @throws IllegalArgumentException if the provided IkeTunnelConnectionParams is not
+ * configured to support MOBIKE
* @see IkeTunnelConnectionParams
* @see VcnManager.VcnStatusCallback#onGatewayConnectionError
*/
@@ -446,6 +450,10 @@
@NonNull IkeTunnelConnectionParams tunnelConnectionParams) {
Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null");
Objects.requireNonNull(tunnelConnectionParams, "tunnelConnectionParams was null");
+ if (!tunnelConnectionParams.getIkeSessionParams().hasIkeOption(IKE_OPTION_MOBIKE)) {
+ throw new IllegalArgumentException(
+ "MOBIKE must be configured for the provided IkeSessionParams");
+ }
mGatewayConnectionName = gatewayConnectionName;
mTunnelConnectionParams = tunnelConnectionParams;
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 9d1c1ff..390c3b9 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -74,6 +74,36 @@
public class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
+ /**
+ * Key for WiFi entry RSSI thresholds
+ *
+ * <p>The VCN will only migrate to a Carrier WiFi network that has a signal strength greater
+ * than, or equal to this threshold.
+ *
+ * <p>WARNING: The VCN does not listen for changes to this key made after VCN startup.
+ *
+ * @hide
+ */
+ @NonNull
+ public static final String VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY =
+ "vcn_network_selection_wifi_entry_rssi_threshold";
+
+ /**
+ * Key for WiFi entry RSSI thresholds
+ *
+ * <p>If the VCN's selected Carrier WiFi network has a signal strength less than this threshold,
+ * the VCN will attempt to migrate away from the Carrier WiFi network.
+ *
+ * <p>WARNING: The VCN does not listen for changes to this key made after VCN startup.
+ *
+ * @hide
+ */
+ @NonNull
+ public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY =
+ "vcn_network_selection_wifi_exit_rssi_threshold";
+
+ // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz
+
private static final Map<
VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
index 0e9ccf1..25a2574 100644
--- a/core/java/android/net/vcn/VcnTransportInfo.java
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -16,12 +16,9 @@
package android.net.vcn;
-import static android.net.NetworkCapabilities.REDACT_ALL;
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.NetworkCapabilities;
@@ -31,8 +28,6 @@
import android.os.Parcelable;
import android.telephony.SubscriptionManager;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.util.Objects;
/**
@@ -55,32 +50,17 @@
@Nullable private final WifiInfo mWifiInfo;
private final int mSubId;
- /**
- * The redaction scheme to use when parcelling.
- *
- * <p>The TransportInfo/NetworkCapabilities redaction mechanisms rely on redaction being
- * performed at parcelling time. This means that the redaction scheme must be stored for later
- * use.
- *
- * <p>Since the redaction scheme itself is not parcelled, this field is listed as a transient.
- *
- * <p>Defaults to REDACT_ALL when constructed using public constructors, or creating from
- * parcels.
- */
- private final transient long mRedactions;
-
public VcnTransportInfo(@NonNull WifiInfo wifiInfo) {
- this(wifiInfo, INVALID_SUBSCRIPTION_ID, REDACT_ALL);
+ this(wifiInfo, INVALID_SUBSCRIPTION_ID);
}
public VcnTransportInfo(int subId) {
- this(null /* wifiInfo */, subId, REDACT_ALL);
+ this(null /* wifiInfo */, subId);
}
- private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId, long redactions) {
+ private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) {
mWifiInfo = wifiInfo;
mSubId = subId;
- mRedactions = redactions;
}
/**
@@ -102,25 +82,14 @@
* SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
*
* @return the Subscription ID if a cellular underlying Network is present, else {@link
- * android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID}.
+ * android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
*/
public int getSubId() {
return mSubId;
}
- /**
- * Gets the redaction scheme
- *
- * @hide
- */
- @VisibleForTesting(visibility = PRIVATE)
- public long getRedaction() {
- return mRedactions;
- }
-
@Override
public int hashCode() {
- // mRedactions not hashed, as it is a transient, for control of parcelling
return Objects.hash(mWifiInfo, mSubId);
}
@@ -128,8 +97,6 @@
public boolean equals(Object o) {
if (!(o instanceof VcnTransportInfo)) return false;
final VcnTransportInfo that = (VcnTransportInfo) o;
-
- // mRedactions not compared, as it is a transient, for control of parcelling
return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId;
}
@@ -142,8 +109,12 @@
@Override
@NonNull
public TransportInfo makeCopy(long redactions) {
+ if ((redactions & NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS) != 0) {
+ return new VcnTransportInfo(null, INVALID_SUBSCRIPTION_ID);
+ }
+
return new VcnTransportInfo(
- mWifiInfo == null ? null : mWifiInfo.makeCopy(redactions), mSubId, redactions);
+ (mWifiInfo == null) ? null : mWifiInfo.makeCopy(redactions), mSubId);
}
@Override
@@ -158,16 +129,11 @@
return redactions;
}
- private boolean shouldParcelNetworkSettingsFields() {
- return (mRedactions & NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS) == 0;
- }
-
/** {@inheritDoc} */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(shouldParcelNetworkSettingsFields() ? mSubId : INVALID_SUBSCRIPTION_ID);
- dest.writeParcelable(
- shouldParcelNetworkSettingsFields() ? (Parcelable) mWifiInfo : null, flags);
+ dest.writeInt(mSubId);
+ dest.writeParcelable(mWifiInfo, flags);
}
@Override
@@ -189,9 +155,7 @@
return null;
}
- // Prevent further forwarding by redacting everything in future parcels from
- // this VcnTransportInfo
- return new VcnTransportInfo(wifiInfo, subId, REDACT_ALL);
+ return new VcnTransportInfo(wifiInfo, subId);
}
public VcnTransportInfo[] newArray(int size) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c97d1f8..4c8297a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3931,7 +3931,6 @@
getStartClockTime(),
whichBatteryScreenOffRealtime / 1000, whichBatteryScreenOffUptime / 1000,
getEstimatedBatteryCapacity(),
- getLearnedBatteryCapacity(),
getMinLearnedBatteryCapacity(),
getMaxLearnedBatteryCapacity(),
screenDozeTime / 1000);
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 3225667..8f36663 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -75,8 +75,10 @@
public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2;
private final int mDischargePercentage;
- private final long mStatsStartTimestampMs;
private final double mBatteryCapacityMah;
+ private final long mStatsStartTimestampMs;
+ private final long mStatsEndTimestampMs;
+ private final long mStatsDurationMs;
private final double mDischargedPowerLowerBound;
private final double mDischargedPowerUpperBound;
private final long mBatteryTimeRemainingMs;
@@ -90,6 +92,12 @@
private BatteryUsageStats(@NonNull Builder builder) {
mStatsStartTimestampMs = builder.mStatsStartTimestampMs;
+ mStatsEndTimestampMs = builder.mStatsEndTimestampMs;
+ if (builder.mStatsDurationMs != -1) {
+ mStatsDurationMs = builder.mStatsDurationMs;
+ } else {
+ mStatsDurationMs = mStatsEndTimestampMs - mStatsStartTimestampMs;
+ }
mBatteryCapacityMah = builder.mBatteryCapacityMah;
mDischargePercentage = builder.mDischargePercentage;
mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah;
@@ -141,6 +149,24 @@
}
/**
+ * Timestamp (as returned by System.currentTimeMillis()) of when the stats snapshot was taken,
+ * in milliseconds.
+ */
+ public long getStatsEndTimestamp() {
+ return mStatsEndTimestampMs;
+ }
+
+ /**
+ * Returns the duration of the stats session captured by this BatteryUsageStats.
+ * In rare cases, statsDuration != statsEndTimestamp - statsStartTimestamp. This may
+ * happen when BatteryUsageStats represents an accumulation of data across multiple
+ * non-contiguous sessions.
+ */
+ public long getStatsDuration() {
+ return mStatsDurationMs;
+ }
+
+ /**
* Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
* charged), in mAh
*/
@@ -229,6 +255,8 @@
private BatteryUsageStats(@NonNull Parcel source) {
mStatsStartTimestampMs = source.readLong();
+ mStatsEndTimestampMs = source.readLong();
+ mStatsDurationMs = source.readLong();
mBatteryCapacityMah = source.readDouble();
mDischargePercentage = source.readInt();
mDischargedPowerLowerBound = source.readDouble();
@@ -287,6 +315,8 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeLong(mStatsStartTimestampMs);
+ dest.writeLong(mStatsEndTimestampMs);
+ dest.writeLong(mStatsDurationMs);
dest.writeDouble(mBatteryCapacityMah);
dest.writeInt(mDischargePercentage);
dest.writeDouble(mDischargedPowerLowerBound);
@@ -441,6 +471,8 @@
private final String[] mCustomPowerComponentNames;
private final boolean mIncludePowerModels;
private long mStatsStartTimestampMs;
+ private long mStatsEndTimestampMs;
+ private long mStatsDurationMs = -1;
private double mBatteryCapacityMah;
private int mDischargePercentage;
private double mDischargedPowerLowerBoundMah;
@@ -494,6 +526,23 @@
}
/**
+ * Sets the timestamp of when the battery stats snapshot was taken, in milliseconds.
+ */
+ public Builder setStatsEndTimestamp(long statsEndTimestampMs) {
+ mStatsEndTimestampMs = statsEndTimestampMs;
+ return this;
+ }
+
+ /**
+ * Sets the duration of the stats session. The default value of this field is
+ * statsEndTimestamp - statsStartTimestamp.
+ */
+ public Builder setStatsDuration(long statsDurationMs) {
+ mStatsDurationMs = statsDurationMs;
+ return this;
+ }
+
+ /**
* Sets the battery discharge amount since BatteryStats reset as percentage of the full
* charge.
*/
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 38ed3fb..900659b 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -309,7 +309,7 @@
* booted, but it may increase when the hardware manufacturer provides an OTA update.
* <p>
* Possible non-zero values are defined in {@link Build.VERSION_CODES} starting with
- * {@link Build.VERSION_CODES#S}.
+ * {@link Build.VERSION_CODES#R}.
*/
public static final int MEDIA_PERFORMANCE_CLASS =
DeviceProperties.media_performance_class().orElse(0);
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 6e99b0d..dfd935d0 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -67,3 +67,6 @@
# RecoverySystem
per-file *Recovery* = file:/services/core/java/com/android/server/recoverysystem/OWNERS
+
+# Bugreporting
+per-file Bugreport* = file:/platform/frameworks/native:/cmds/dumpstate/OWNERS
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index f9e4f73..6bca336 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -250,6 +250,12 @@
public static final int EXT_OBB_RW_GID = 1079;
/**
+ * Defines the UID/GID for the Uwb service process.
+ * @hide
+ */
+ public static final int UWB_UID = 1083;
+
+ /**
* GID that corresponds to the INTERNET permission.
* Must match the value of AID_INET.
* @hide
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b8ad068..326012d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2026,6 +2026,9 @@
case USER_TYPE_SYSTEM_HEADLESS:
return FrameworkStatsLog
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS;
+ case USER_TYPE_PROFILE_CLONE:
+ return FrameworkStatsLog
+ .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_CLONE;
default:
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index d7d1902..3daa3a5 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -31,6 +31,7 @@
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.Uri;
+import android.provider.Settings.Config.SyncDisabledMode;
import android.provider.Settings.ResetMode;
import android.util.ArrayMap;
import android.util.Log;
@@ -580,6 +581,21 @@
*/
public static final String NAMESPACE_GAME_OVERLAY = "game_overlay";
+ /**
+ * Namespace for Constrain Display APIs related features.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
+
+ /**
+ * Trace error logger properties definitions.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_TRACE_ERROR_LOGGER = "trace_error_logger";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
@@ -832,6 +848,37 @@
}
/**
+ * Disables or re-enables bulk modifications ({@link #setProperties(Properties)}) to device
+ * config values. This is intended for use during tests to prevent a sync operation clearing
+ * config values, which could influence the outcome of the tests, i.e. by changing behavior.
+ *
+ * @param syncDisabledMode the mode to use, see {@link Settings.Config#SYNC_DISABLED_MODE_NONE},
+ * {@link Settings.Config#SYNC_DISABLED_MODE_PERSISTENT} and {@link
+ * Settings.Config#SYNC_DISABLED_MODE_UNTIL_REBOOT}
+ *
+ * @see #isSyncDisabled()
+ * @hide
+ */
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
+ public static void setSyncDisabled(@SyncDisabledMode int syncDisabledMode) {
+ ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+ Settings.Config.setSyncDisabled(contentResolver, syncDisabledMode);
+ }
+
+ /**
+ * Returns the current state of sync disabling, {@code true} when disabled, {@code false}
+ * otherwise.
+ *
+ * @see #setSyncDisabled(int)
+ * @hide
+ */
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
+ public static boolean isSyncDisabled() {
+ ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+ return Settings.Config.isSyncDisabled(contentResolver);
+ }
+
+ /**
* Add a listener for property changes.
* <p>
* This listener will be called whenever properties in the specified namespace change. Callbacks
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3bdc546..0c23ae6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -267,8 +267,40 @@
/** @hide */
public static final String EXTRA_NETWORK_TEMPLATE = "network_template";
+ /**
+ * The return values for {@link Settings.Config#set}
+ * @hide
+ */
+ @IntDef(prefix = "SET_ALL_RESULT_",
+ value = { SET_ALL_RESULT_FAILURE, SET_ALL_RESULT_SUCCESS, SET_ALL_RESULT_DISABLED })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface SetAllResult {}
+
+ /**
+ * A return value for {@link #KEY_CONFIG_SET_ALL_RETURN}, indicates failure.
+ * @hide
+ */
+ public static final int SET_ALL_RESULT_FAILURE = 0;
+
+ /**
+ * A return value for {@link #KEY_CONFIG_SET_ALL_RETURN}, indicates success.
+ * @hide
+ */
+ public static final int SET_ALL_RESULT_SUCCESS = 1;
+
+ /**
+ * A return value for {@link #KEY_CONFIG_SET_ALL_RETURN}, indicates a set all is disabled.
+ * @hide
+ */
+ public static final int SET_ALL_RESULT_DISABLED = 2;
+
/** @hide */
- public static final String KEY_CONFIG_SET_RETURN = "config_set_return";
+ public static final String KEY_CONFIG_SET_ALL_RETURN = "config_set_all_return";
+
+ /** @hide */
+ public static final String KEY_CONFIG_IS_SYNC_DISABLED_RETURN =
+ "config_is_sync_disabled_return";
/**
* An int extra specifying a subscription ID.
@@ -890,6 +922,21 @@
"android.settings.LOCALE_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of lockscreen.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_LOCKSCREEN_SETTINGS = "android.settings.LOCK_SCREEN_SETTINGS";
+
+ /**
* Activity Action: Show settings to configure input methods, in particular
* allowing the user to enable input methods.
* <p>
@@ -2324,6 +2371,11 @@
public static final String CALL_METHOD_PREFIX_KEY = "_prefix";
/**
+ * @hide - String argument extra to the fast-path call()-based requests
+ */
+ public static final String CALL_METHOD_SYNC_DISABLED_MODE_KEY = "_disabled_mode";
+
+ /**
* @hide - RemoteCallback monitor callback argument extra to the fast-path call()-based requests
*/
public static final String CALL_METHOD_MONITOR_CALLBACK_KEY = "_monitor_callback_key";
@@ -2386,6 +2438,15 @@
/** @hide - Private call() method to reset to defaults the 'configuration' table */
public static final String CALL_METHOD_LIST_CONFIG = "LIST_config";
+ /** @hide - Private call() method to disable / re-enable syncs to the 'configuration' table */
+ public static final String CALL_METHOD_SET_SYNC_DISABLED_CONFIG = "SET_SYNC_DISABLED_config";
+
+ /**
+ * @hide - Private call() method to return whether syncs are disabled for the 'configuration'
+ * table
+ */
+ public static final String CALL_METHOD_IS_SYNC_DISABLED_CONFIG = "IS_SYNC_DISABLED_config";
+
/** @hide - Private call() method to register monitor callback for 'configuration' table */
public static final String CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG =
"REGISTER_MONITOR_CALLBACK_config";
@@ -2762,11 +2823,11 @@
return true;
}
- public boolean setStringsForPrefix(ContentResolver cr, String prefix,
+ public @SetAllResult int setStringsForPrefix(ContentResolver cr, String prefix,
HashMap<String, String> keyValues) {
if (mCallSetAllCommand == null) {
// This NameValueCache does not support atomically setting multiple flags
- return false;
+ return SET_ALL_RESULT_FAILURE;
}
try {
Bundle args = new Bundle();
@@ -2776,10 +2837,10 @@
Bundle bundle = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(),
mCallSetAllCommand, null, args);
- return bundle.getBoolean(KEY_CONFIG_SET_RETURN);
+ return bundle.getInt(KEY_CONFIG_SET_ALL_RETURN);
} catch (RemoteException e) {
// Not supported by the remote side
- return false;
+ return SET_ALL_RESULT_FAILURE;
}
}
@@ -8636,7 +8697,6 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
- @Readable
public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
/**
@@ -9455,6 +9515,20 @@
"power_menu_locked_show_content";
/**
+ * Whether home controls should be accessible from the lockscreen
+ *
+ * @hide
+ */
+ public static final String LOCKSCREEN_SHOW_CONTROLS = "lockscreen_show_controls";
+
+ /**
+ * Whether wallet should be accessible from the lockscreen
+ *
+ * @hide
+ */
+ public static final String LOCKSCREEN_SHOW_WALLET = "lockscreen_show_wallet";
+
+ /**
* Specifies whether the web action API is enabled.
*
* @hide
@@ -9921,6 +9995,18 @@
"accessibility_floating_menu_opacity";
/**
+ * Prompts the user to the Accessibility button is replaced with the floating menu.
+ * <ul>
+ * <li> 0 = disabled </li>
+ * <li> 1 = enabled </li>
+ * </ul>
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT =
+ "accessibility_floating_menu_migration_tooltip_prompt";
+
+ /**
* Whether the Adaptive connectivity option is enabled.
*
* @hide
@@ -9974,6 +10060,12 @@
@Readable
public static final String NAS_SETTINGS_UPDATED = "nas_settings_updated";
+ /**
+ * Control whether Game Dashboard shortcut is always on for all games.
+ * @hide
+ */
+ @Readable
+ public static final String GAME_DASHBOARD_ALWAYS_ON = "game_dashboard_always_on";
/**
* These entries are considered common between the personal and the managed profile,
@@ -14188,6 +14280,15 @@
public static final String ARE_USER_DISABLED_HDR_FORMATS_ALLOWED =
"are_user_disabled_hdr_formats_allowed";
+ /**
+ * Whether or not syncs (bulk set operations) for {@link DeviceConfig} are disabled
+ * currently. The value is boolean (1 or 0). The value '1' means that {@link
+ * DeviceConfig#setProperties(DeviceConfig.Properties)} will return {@code false}.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_SYNC_DISABLED = "device_config_sync_disabled";
+
/** @hide */ public static String zenModeToString(int mode) {
if (mode == ZEN_MODE_IMPORTANT_INTERRUPTIONS) return "ZEN_MODE_IMPORTANT_INTERRUPTIONS";
if (mode == ZEN_MODE_ALARMS) return "ZEN_MODE_ALARMS";
@@ -16022,6 +16123,39 @@
* @hide
*/
public static final class Config extends NameValueTable {
+
+ /**
+ * The modes that can be used when disabling syncs to the 'config' settings.
+ * @hide
+ */
+ @IntDef(prefix = "DISABLE_SYNC_MODE_",
+ value = { SYNC_DISABLED_MODE_NONE, SYNC_DISABLED_MODE_PERSISTENT,
+ SYNC_DISABLED_MODE_UNTIL_REBOOT })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface SyncDisabledMode {}
+
+ /**
+ * Sync is not not disabled.
+ *
+ * @hide
+ */
+ public static final int SYNC_DISABLED_MODE_NONE = 0;
+
+ /**
+ * Disabling of Config bulk update / syncing is persistent, i.e. it survives a device
+ * reboot.
+ * @hide
+ */
+ public static final int SYNC_DISABLED_MODE_PERSISTENT = 1;
+
+ /**
+ * Disabling of Config bulk update / syncing is not persistent, i.e. it will not survive a
+ * device reboot.
+ * @hide
+ */
+ public static final int SYNC_DISABLED_MODE_UNTIL_REBOOT = 2;
+
private static final ContentProviderHolder sProviderHolder =
new ContentProviderHolder(DeviceConfig.CONTENT_URI);
@@ -16114,7 +16248,7 @@
* @param resolver to access the database with.
* @param namespace to which the names should be set.
* @param keyValues map of key names (without the prefix) to values.
- * @return
+ * @return true if the name/value pairs were set, false if setting was blocked
*
* @hide
*/
@@ -16127,12 +16261,15 @@
compositeKeyValueMap.put(
createCompositeName(namespace, entry.getKey()), entry.getValue());
}
- // If can't set given configuration that means it's bad
- if (!sNameValueCache.setStringsForPrefix(resolver, createPrefix(namespace),
- compositeKeyValueMap)) {
- throw new DeviceConfig.BadConfigException();
+ int result = sNameValueCache.setStringsForPrefix(
+ resolver, createPrefix(namespace), compositeKeyValueMap);
+ if (result == SET_ALL_RESULT_SUCCESS) {
+ return true;
+ } else if (result == SET_ALL_RESULT_DISABLED) {
+ return false;
}
- return true;
+ // If can't set given configuration that means it's bad
+ throw new DeviceConfig.BadConfigException();
}
/**
@@ -16168,6 +16305,50 @@
}
/**
+ * Bridge method between {@link DeviceConfig#setSyncDisabled(int)} and the
+ * {@link com.android.providers.settings.SettingsProvider} implementation.
+ *
+ * @hide
+ */
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
+ static void setSyncDisabled(
+ @NonNull ContentResolver resolver, @SyncDisabledMode int disableSyncMode) {
+ try {
+ Bundle args = new Bundle();
+ args.putInt(CALL_METHOD_SYNC_DISABLED_MODE_KEY, disableSyncMode);
+ IContentProvider cp = sProviderHolder.getProvider(resolver);
+ cp.call(resolver.getAttributionSource(),
+ sProviderHolder.mUri.getAuthority(), CALL_METHOD_SET_SYNC_DISABLED_CONFIG,
+ null, args);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't set sync disabled " + DeviceConfig.CONTENT_URI, e);
+ }
+ }
+
+ /**
+ * Bridge method between {@link DeviceConfig#isSyncDisabled()} and the
+ * {@link com.android.providers.settings.SettingsProvider} implementation.
+ *
+ * @hide
+ */
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
+ static boolean isSyncDisabled(@NonNull ContentResolver resolver) {
+ try {
+ Bundle args = Bundle.EMPTY;
+ IContentProvider cp = sProviderHolder.getProvider(resolver);
+ Bundle bundle = cp.call(resolver.getAttributionSource(),
+ sProviderHolder.mUri.getAuthority(), CALL_METHOD_IS_SYNC_DISABLED_CONFIG,
+ null, args);
+ return bundle.getBoolean(KEY_CONFIG_IS_SYNC_DISABLED_RETURN);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't query sync disabled " + DeviceConfig.CONTENT_URI, e);
+ }
+ return false;
+ }
+
+ /**
* Register callback for monitoring Config table.
*
* @param resolver Handle to the content resolver.
diff --git a/core/java/android/service/displayhash/DisplayHashingService.java b/core/java/android/service/displayhash/DisplayHashingService.java
index e8bb485..f22d40e 100644
--- a/core/java/android/service/displayhash/DisplayHashingService.java
+++ b/core/java/android/service/displayhash/DisplayHashingService.java
@@ -51,15 +51,9 @@
public static final String EXTRA_VERIFIED_DISPLAY_HASH =
"android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
- /**
- * Name under which a DisplayHashingService component publishes information
- * about itself. This meta-data must reference an XML resource containing a
- * {@link com.android.internal.R.styleable#DisplayHashingService} tag.
- *
- * @hide
- */
- @SystemApi
- public static final String SERVICE_META_DATA = "android.displayhash.display_hashing_service";
+ /** @hide **/
+ public static final String EXTRA_INTERVAL_BETWEEN_REQUESTS =
+ "android.service.displayhash.extra.INTERVAL_BETWEEN_REQUESTS";
/**
* The {@link Intent} action that must be declared as handled by a service in its manifest
@@ -149,6 +143,21 @@
callback.sendResult(data);
}
+ /**
+ * Call to get the interval required between display hash requests. Requests made faster than
+ * this will be throttled.
+ *
+ * @return the interval value required between requests.
+ */
+ public abstract int onGetIntervalBetweenRequestsMillis();
+
+ private void getDurationBetweenRequestsMillis(RemoteCallback callback) {
+ int durationBetweenRequestMillis = onGetIntervalBetweenRequestsMillis();
+ Bundle data = new Bundle();
+ data.putInt(EXTRA_INTERVAL_BETWEEN_REQUESTS, durationBetweenRequestMillis);
+ callback.sendResult(data);
+ }
+
private final class DisplayHashingServiceWrapper extends IDisplayHashingService.Stub {
@Override
public void generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds,
@@ -187,5 +196,12 @@
mHandler.sendMessage(obtainMessage(DisplayHashingService::getDisplayHashAlgorithms,
DisplayHashingService.this, callback));
}
+
+ @Override
+ public void getIntervalBetweenRequestsMillis(RemoteCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(DisplayHashingService::getDurationBetweenRequestsMillis,
+ DisplayHashingService.this, callback));
+ }
}
}
diff --git a/core/java/android/service/displayhash/IDisplayHashingService.aidl b/core/java/android/service/displayhash/IDisplayHashingService.aidl
index 56e1e0a..066cd51 100644
--- a/core/java/android/service/displayhash/IDisplayHashingService.aidl
+++ b/core/java/android/service/displayhash/IDisplayHashingService.aidl
@@ -58,4 +58,13 @@
* @param callback The callback invoked to send back the map of algorithms to DisplayHashParams.
*/
void getDisplayHashAlgorithms(in RemoteCallback callback);
+
+ /**
+ * Call to get the interval required between display hash requests. Requests made faster than
+ * this will be throttled. The result will be sent in the callback as an int with the key
+ * {@link #EXTRA_INTERVAL_BETWEEN_REQUESTS}.
+ *
+ * @param callback The callback invoked to send back the interval duration.
+ */
+ void getIntervalBetweenRequestsMillis(in RemoteCallback callback);
}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 9d0b582..2e4af3f 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -61,6 +61,7 @@
public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, ServiceConnection {
private static final String TAG = "QAWalletSClient";
+ public static final String SETTING_KEY = "lockscreen_show_wallet";
private final Handler mHandler;
private final Context mContext;
private final Queue<ApiCaller> mRequestQueue;
@@ -96,13 +97,12 @@
int currentUser = ActivityManager.getCurrentUser();
return currentUser == UserHandle.USER_SYSTEM
&& checkUserSetupComplete()
- && checkSecureSetting(Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED)
&& !new LockPatternUtils(mContext).isUserInLockdown(currentUser);
}
@Override
public boolean isWalletFeatureAvailableWhenDeviceLocked() {
- return checkSecureSetting(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT);
+ return checkSecureSetting(SETTING_KEY);
}
@Override
diff --git a/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java b/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java
index f585155..4f881b8 100644
--- a/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java
+++ b/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java
@@ -17,7 +17,6 @@
package android.service.translation;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.DeadObjectException;
import android.os.RemoteException;
import android.util.Log;
@@ -25,6 +24,7 @@
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
/**
* Callback to receive the {@link TranslationResponse} on successful translation.
@@ -32,13 +32,13 @@
* @hide
*/
final class OnTranslationResultCallbackWrapper implements
- TranslationService.OnTranslationResultCallback {
+ Consumer<TranslationResponse> {
private static final String TAG = "OnTranslationResultCallback";
private final @NonNull ITranslationCallback mCallback;
- private AtomicBoolean mCalled;
+ private final AtomicBoolean mCalled;
/**
* @hide
@@ -49,7 +49,7 @@
}
@Override
- public void onTranslationSuccess(@Nullable TranslationResponse response) {
+ public void accept(TranslationResponse response) {
assertNotCalled();
if (mCalled.getAndSet(response.isFinalResponse())) {
throw new IllegalStateException("Already called with complete response");
@@ -66,15 +66,6 @@
}
}
- /**
- * @deprecated use {@link #onTranslationSuccess} with error response instead.
- */
- @Override
- @Deprecated
- public void onError() {
- // no-op.
- }
-
private void assertNotCalled() {
if (mCalled.get()) {
throw new IllegalStateException("Already called");
diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java
index e7234cc..24ebe45 100644
--- a/core/java/android/service/translation/TranslationService.java
+++ b/core/java/android/service/translation/TranslationService.java
@@ -125,7 +125,9 @@
/**
* Interface definition for a callback to be invoked when the translation is compleled.
+ * @deprecated use a {@link Consumer} instead.
*/
+ @Deprecated
public interface OnTranslationResultCallback {
/**
* Notifies the Android System that a translation request
@@ -162,12 +164,12 @@
public void onTranslationRequest(TranslationRequest request, int sessionId,
ICancellationSignal transport, ITranslationCallback callback)
throws RemoteException {
- final OnTranslationResultCallback translationResultCallback =
+ final Consumer<TranslationResponse> consumer =
new OnTranslationResultCallbackWrapper(callback);
mHandler.sendMessage(obtainMessage(TranslationService::onTranslationRequest,
TranslationService.this, request, sessionId,
CancellationSignal.fromTransport(transport),
- translationResultCallback));
+ consumer));
}
@Override
@@ -242,8 +244,10 @@
* instead.
*/
@Deprecated
- public abstract void onCreateTranslationSession(@NonNull TranslationContext translationContext,
- int sessionId);
+ public void onCreateTranslationSession(@NonNull TranslationContext translationContext,
+ int sessionId) {
+ // no-op
+ }
/**
* TODO: fill in javadoc.
@@ -259,10 +263,45 @@
* @param sessionId
* @param callback
* @param cancellationSignal
+ * @deprecated use
+ * {@link #onTranslationRequest(TranslationRequest, int, CancellationSignal, Consumer)} instead.
*/
- public abstract void onTranslationRequest(@NonNull TranslationRequest request, int sessionId,
+ @Deprecated
+ public void onTranslationRequest(@NonNull TranslationRequest request, int sessionId,
@Nullable CancellationSignal cancellationSignal,
- @NonNull OnTranslationResultCallback callback);
+ @NonNull OnTranslationResultCallback callback) {
+ // no-op
+ }
+
+ /**
+ * Called to the service with a {@link TranslationRequest} to be translated.
+ *
+ * <p>The service must call {@code callback.accept()} with the {@link TranslationResponse}. If
+ * {@link TranslationRequest#FLAG_PARTIAL_RESPONSES} was set, the service may call
+ * {@code callback.accept()} multiple times with partial responses.</p>
+ *
+ * @param request
+ * @param sessionId
+ * @param callback
+ * @param cancellationSignal
+ */
+ //TODO: make abstract once aiai transitions.
+ public void onTranslationRequest(@NonNull TranslationRequest request, int sessionId,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull Consumer<TranslationResponse> callback) {
+ onTranslationRequest(request, sessionId, cancellationSignal,
+ new OnTranslationResultCallback() {
+ @Override
+ public void onTranslationSuccess(@NonNull TranslationResponse response) {
+ callback.accept(response);
+ }
+
+ @Override
+ public void onError() {
+ // null-op
+ }
+ });
+ }
/**
* TODO: fill in javadoc
diff --git a/core/java/android/service/voice/AbstractHotwordDetector.java b/core/java/android/service/voice/AbstractHotwordDetector.java
index 4896748..54ccf30 100644
--- a/core/java/android/service/voice/AbstractHotwordDetector.java
+++ b/core/java/android/service/voice/AbstractHotwordDetector.java
@@ -55,10 +55,8 @@
/**
* Detect hotword from an externally supplied stream of data.
*
- * @return a writeable file descriptor that clients can start writing data in the given format.
- * In order to stop detection, clients can close the given stream.
+ * @return true if the request to start recognition succeeded
*/
- @Nullable
@Override
public boolean startRecognition(
@NonNull ParcelFileDescriptor audioStream,
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index bacc6ec..fed28df 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -49,7 +49,6 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
-import android.service.voice.HotwordDetectionService.InitializationStatus;
import android.util.Log;
import android.util.Slog;
@@ -524,16 +523,19 @@
* @param result Info about the second stage detection result, provided by the
* {@link HotwordDetectionService}.
*/
- public void onRejected(@Nullable HotwordRejectedResult result) {
+ public void onRejected(@NonNull HotwordRejectedResult result) {
}
/**
* Called when the {@link HotwordDetectionService} is created by the system and given a
* short amount of time to report it's initialization state.
*
- * @param status Info about initialization state of {@link HotwordDetectionService}.
+ * @param status Info about initialization state of {@link HotwordDetectionService}; the
+ * allowed values are {@link HotwordDetectionService#INITIALIZATION_STATUS_SUCCESS},
+ * 1<->{@link HotwordDetectionService#getMaxCustomInitializationStatus()},
+ * {@link HotwordDetectionService#INITIALIZATION_STATUS_UNKNOWN}.
*/
- public void onHotwordDetectionServiceInitialized(@InitializationStatus int status) {
+ public void onHotwordDetectionServiceInitialized(int status) {
}
/**
@@ -1164,7 +1166,7 @@
}
@Override
- public void onRejected(HotwordRejectedResult result) {
+ public void onRejected(@NonNull HotwordRejectedResult result) {
if (DBG) {
Slog.d(TAG, "onRejected(" + result + ")");
} else {
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 473e7ae..3960e38 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -16,6 +16,8 @@
package android.service.voice;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -76,15 +78,12 @@
/** @hide */
public static final String KEY_INITIALIZATION_STATUS = "initialization_status";
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, prefix = { "INITIALIZATION_STATUS_" }, value = {
- INITIALIZATION_STATUS_SUCCESS,
- INITIALIZATION_STATUS_CUSTOM_ERROR_1,
- INITIALIZATION_STATUS_CUSTOM_ERROR_2,
- INITIALIZATION_STATUS_UNKNOWN,
- })
- public @interface InitializationStatus {}
+ /**
+ * The maximum number of initialization status for some application specific failed reasons.
+ *
+ * @hide
+ */
+ public static final int MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR = 2;
/**
* Indicates that the updated status is successful.
@@ -92,16 +91,6 @@
public static final int INITIALIZATION_STATUS_SUCCESS = 0;
/**
- * Indicates that the updated status is failure for some application specific reasons.
- */
- public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1;
-
- /**
- * Indicates that the updated status is failure for some application specific reasons.
- */
- public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_2 = 2;
-
- /**
* Indicates that the callback wasn’t invoked within the timeout.
* This is used by system.
*/
@@ -227,6 +216,19 @@
}
/**
+ * Returns the maximum number of initialization status for some application specific failed
+ * reasons.
+ *
+ * Note: The value 0 is reserved for success.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static int getMaxCustomInitializationStatus() {
+ return MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR;
+ }
+
+ /**
* Called when the device hardware (such as a DSP) detected the hotword, to request second stage
* validation before handing over the audio to the {@link AlwaysOnHotwordDetector}.
* <p>
@@ -296,9 +298,10 @@
* such data to the trusted process.
* @param callbackTimeoutMillis Timeout in milliseconds for the operation to invoke the
* statusCallback.
- * @param statusCallback Use this to return the updated result. This is non-null only when the
- * {@link HotwordDetectionService} is being initialized; and it is null if the state is updated
- * after that.
+ * @param statusCallback Use this to return the updated result; the allowed values are
+ * {@link #INITIALIZATION_STATUS_SUCCESS}, 1<->{@link #getMaxCustomInitializationStatus()}.
+ * This is non-null only when the {@link HotwordDetectionService} is being initialized; and it
+ * is null if the state is updated after that.
*
* @hide
*/
@@ -307,7 +310,7 @@
@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory,
@DurationMillisLong long callbackTimeoutMillis,
- @Nullable @InitializationStatus IntConsumer statusCallback) {
+ @Nullable IntConsumer statusCallback) {
// TODO: Handle the unimplemented case by throwing?
}
@@ -387,6 +390,10 @@
if (callback != null) {
intConsumer =
value -> {
+ if (value > getMaxCustomInitializationStatus()) {
+ throw new IllegalArgumentException(
+ "The initialization status is invalid for " + value);
+ }
try {
Bundle status = new Bundle();
status.putInt(KEY_INITIALIZATION_STATUS, value);
@@ -414,11 +421,15 @@
}
/**
- * Called when the detected result is valid.
+ * Informs the {@link HotwordDetector} that the keyphrase was detected.
+ *
+ * @param result Info about the detection result. This is provided to the
+ * {@link HotwordDetector}.
*/
- public void onDetected(@Nullable HotwordDetectedResult hotwordDetectedResult) {
+ public void onDetected(@NonNull HotwordDetectedResult result) {
+ requireNonNull(result);
try {
- mRemoteCallback.onDetected(hotwordDetectedResult);
+ mRemoteCallback.onDetected(result);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -433,7 +444,8 @@
* @param result Info about the second stage detection result. This is provided to
* the {@link HotwordDetector}.
*/
- public void onRejected(@Nullable HotwordRejectedResult result) {
+ public void onRejected(@NonNull HotwordRejectedResult result) {
+ requireNonNull(result);
try {
mRemoteCallback.onRejected(result);
} catch (RemoteException e) {
diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java
index 2fb4dbc..b2f810a 100644
--- a/core/java/android/service/voice/HotwordDetector.java
+++ b/core/java/android/service/voice/HotwordDetector.java
@@ -28,7 +28,6 @@
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.SharedMemory;
-import android.service.voice.HotwordDetectionService.InitializationStatus;
/**
* Basic functionality for hotword detectors.
@@ -160,15 +159,18 @@
* @param result Info about the second stage detection result, provided by the
* {@link HotwordDetectionService}.
*/
- void onRejected(@Nullable HotwordRejectedResult result);
+ void onRejected(@NonNull HotwordRejectedResult result);
/**
* Called when the {@link HotwordDetectionService} is created by the system and given a
* short amount of time to report it's initialization state.
*
- * @param status Info about initialization state of {@link HotwordDetectionService}.
+ * @param status Info about initialization state of {@link HotwordDetectionService}; the
+ * allowed values are {@link HotwordDetectionService#INITIALIZATION_STATUS_SUCCESS},
+ * 1<->{@link HotwordDetectionService#getMaxCustomInitializationStatus()},
+ * {@link HotwordDetectionService#INITIALIZATION_STATUS_UNKNOWN}.
*/
- void onHotwordDetectionServiceInitialized(@InitializationStatus int status);
+ void onHotwordDetectionServiceInitialized(int status);
/**
* Called with the {@link HotwordDetectionService} is restarted.
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index b38067b..f5c9591 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -41,4 +41,9 @@
public abstract boolean supportsLocalVoiceInteraction();
public abstract void stopLocalVoiceInteraction(IBinder callingActivity);
+
+ /**
+ * Returns whether the given package is currently in an active session
+ */
+ public abstract boolean hasActiveSession(String packageName);
}
\ No newline at end of file
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 53bde36..324d1ab 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -21,6 +21,7 @@
import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import android.annotation.FloatRange;
import android.annotation.NonNull;
@@ -1218,7 +1219,9 @@
mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
mCaller.getHandler());
mDisplay = mIWallpaperEngine.mDisplay;
- mDisplayContext = createDisplayContext(mDisplay);
+ // Use window context of TYPE_WALLPAPER so client can access UI resources correctly.
+ mDisplayContext = createDisplayContext(mDisplay)
+ .createWindowContext(TYPE_WALLPAPER, null /* options */);
mDisplayState = mDisplay.getState();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index b992007..37a97ca 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -40,6 +40,8 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.R;
+
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -238,7 +240,7 @@
* @param context with which {@code SpeechRecognizer} will be created
* @return {@code true} if recognition is available, {@code false} otherwise
*/
- public static boolean isRecognitionAvailable(final Context context) {
+ public static boolean isRecognitionAvailable(@NonNull final Context context) {
// TODO(b/176578753): make sure this works well with system speech recognizers.
final List<ResolveInfo> list = context.getPackageManager().queryIntentServices(
new Intent(RecognitionService.SERVICE_INTERFACE), 0);
@@ -246,6 +248,38 @@
}
/**
+ * Checks whether an on-device speech recognition service is available on the system. If this
+ * method returns {@code false},
+ * {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)} will
+ * fail.
+ *
+ * @param context with which on-device {@code SpeechRecognizer} will be created
+ * @return {@code true} if on-device recognition is available, {@code false} otherwise
+ */
+ public static boolean isOnDeviceRecognitionAvailable(@NonNull final Context context) {
+ ComponentName componentName =
+ ComponentName.unflattenFromString(
+ context.getString(R.string.config_defaultOnDeviceSpeechRecognitionService));
+ if (componentName == null) {
+ return false;
+ }
+
+ List<ResolveInfo> resolveInfos =
+ context.getPackageManager().queryIntentServices(
+ new Intent(RecognitionService.SERVICE_INTERFACE), 0);
+ if (resolveInfos == null) {
+ return false;
+ }
+
+ for (ResolveInfo ri : resolveInfos) {
+ if (ri.serviceInfo != null && componentName.equals(ri.serviceInfo.getComponentName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Factory method to create a new {@code SpeechRecognizer}. Please note that
* {@link #setRecognitionListener(RecognitionListener)} should be called before dispatching any
* command to the created {@code SpeechRecognizer}, otherwise no notifications will be
@@ -315,6 +349,8 @@
* notifications will be received.
*
* @param context in which to create {@code SpeechRecognizer}
+ * @throws UnsupportedOperationException iff {@link #isOnDeviceRecognitionAvailable(Context)}
+ * is false
* @return a new on-device {@code SpeechRecognizer}.
*/
@NonNull
@@ -324,6 +360,9 @@
throw new IllegalArgumentException("Context cannot be null");
}
checkIsCalledFromMainThread();
+ if (!isOnDeviceRecognitionAvailable(context)) {
+ throw new UnsupportedOperationException("On-device recognition is not available");
+ }
return new SpeechRecognizer(context, /* onDevice */ true);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 572e50a..c9a7949 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -72,7 +72,7 @@
DEFAULT_FLAGS.put("settings_contextual_home", "false");
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "true");
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
- DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
}
private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/AttachedSurfaceControl.java
similarity index 70%
rename from core/java/android/view/ViewRoot.java
rename to core/java/android/view/AttachedSurfaceControl.java
index 3c75598..bcc5b56 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -22,16 +22,19 @@
/**
* Provides an interface to the root-Surface of a View Hierarchy or Window. This
* is used in combination with the {@link android.view.SurfaceControl} API to enable
- * attaching app created SurfaceControl to the ViewRoot's surface hierarchy, and enable
- * SurfaceTransactions to be performed in sync with the ViewRoot drawing. This object
- * is obtained from {@link android.view.View#getViewRoot} and
- * {@link android.view.Window#getViewRoot}. It must be used from the UI thread of
+ * attaching app created SurfaceControl to the SurfaceControl hierarchy used
+ * by the app, and enable SurfaceTransactions to be performed in sync with the
+ * View hierarchy drawing.
+ *
+ * This object is obtained from {@link android.view.View#getRootSurfaceControl} and
+ * {@link android.view.Window#getRootSurfaceControl}. It must be used from the UI thread of
* the object it was obtained from.
*/
@UiThread
-public interface ViewRoot {
+public interface AttachedSurfaceControl {
/**
- * Create a transaction which will reparent {@param child} to the ViewRoot. See
+ * Create a transaction which will reparent {@param child} to the View hierarchy
+ * root SurfaceControl. See
* {@link SurfaceControl.Transaction#reparent}. This transacton must be applied
* or merged in to another transaction by the caller, otherwise it will have
* no effect.
@@ -42,9 +45,9 @@
@Nullable SurfaceControl.Transaction buildReparentTransaction(@NonNull SurfaceControl child);
/**
- * Consume the passed in transaction, and request the ViewRoot to apply it with the
- * next draw. This transaction will be merged with the buffer transaction from the ViewRoot
- * and they will show up on-screen atomically synced.
+ * Consume the passed in transaction, and request the View hierarchy to apply it atomically
+ * with the next draw. This transaction will be merged with the buffer transaction from the
+ * ViewRoot and they will show up on-screen atomically synced.
*
* This will not cause a draw to be scheduled, and if there are no other changes
* to the View hierarchy you may need to call {@link android.view.View#invalidate}
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 7125232..6a2b723 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.view.IRemoteAnimationFinishedCallback;
+import android.view.SurfaceControl;
import android.graphics.GraphicBuffer;
import android.window.PictureInPictureSurfaceTransaction;
import android.window.TaskSnapshot;
@@ -43,9 +44,10 @@
* updated accordingly. This should be called before `finish`
* @param taskId for which the leash should be updated
* @param finishTransaction leash operations for the final transform.
+ * @param overlay the surface control for an overlay being shown above the pip (can be null)
*/
void setFinishTaskTransaction(int taskId,
- in PictureInPictureSurfaceTransaction finishTransaction);
+ in PictureInPictureSurfaceTransaction finishTransaction, in SurfaceControl overlay);
/**
* Notifies to the system that the animation into Recents should end, and all leashes associated
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 6c8753b9..000c685 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -29,6 +29,7 @@
import android.util.AttributeSet;
import android.widget.RelativeLayout;
import android.widget.RemoteViews;
+import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.widget.CachingIconView;
@@ -175,6 +176,28 @@
}
/**
+ * This is used to make the low-priority header show the bolded text of a title.
+ *
+ * @param styleTextAsTitle true if this header's text is to have the style of a title
+ */
+ @RemotableViewMethod
+ public void styleTextAsTitle(boolean styleTextAsTitle) {
+ int styleResId = styleTextAsTitle
+ ? R.style.TextAppearance_DeviceDefault_Notification_Title
+ : R.style.TextAppearance_DeviceDefault_Notification_Info;
+ // Most of the time, we're showing text in the minimized state
+ View headerText = findViewById(R.id.header_text);
+ if (headerText instanceof TextView) {
+ ((TextView) headerText).setTextAppearance(styleResId);
+ }
+ // If there's no summary or text, we show the app name instead of nothing
+ View appNameText = findViewById(R.id.app_name_text);
+ if (appNameText instanceof TextView) {
+ ((TextView) appNameText).setTextAppearance(styleResId);
+ }
+ }
+
+ /**
* Get the current margin end value for the header text.
* Add this to {@link #getTopLineBaseMarginEnd()} to get the total margin of the top line.
*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c1c892c..acc0fc1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -29599,7 +29599,7 @@
return mScrollCaptureInternal;
}
- ViewRoot getViewRoot() {
+ AttachedSurfaceControl getRootSurfaceControl() {
return mViewRootImpl;
}
@@ -30876,7 +30876,9 @@
/**
* Called when the content from {@link View#onCreateViewTranslationRequest} had been translated
- * by the TranslationService.
+ * by the TranslationService. The {@link ViewTranslationResponse} should be saved here so that
+ * the {@link ViewTranslationResponse} can be used to display the translation when the system
+ * calls {@link ViewTranslationCallback#onShowTranslation}.
*
* <p> The default implementation will set the ViewTranslationResponse that can be get from
* {@link View#getViewTranslationResponse}. </p>
@@ -30929,7 +30931,7 @@
* information, e.g. source spec, target spec.
* @param requests fill in with {@link ViewTranslationRequest}s for translation purpose.
*/
- public void dispatchRequestTranslation(@NonNull Map<AutofillId, long[]> viewIds,
+ public void dispatchCreateViewTranslationRequest(@NonNull Map<AutofillId, long[]> viewIds,
@NonNull @DataFormat int[] supportedFormats,
@NonNull TranslationCapability capability,
@NonNull List<ViewTranslationRequest> requests) {
@@ -31043,17 +31045,17 @@
}
/**
- * @return The {@link android.view.ViewRoot} interface for this View. This will only
- * return a non-null value when called between {@link #onAttachedToWindow} and
- * {@link #onDetachedFromWindow}.
- *
- * The ViewRoot itself is not a View, it is just the interface to the windowing-system
- * object that contains the entire view hierarchy. For the root View of a given hierarchy
- * see {@link #getRootView}.
+ * The AttachedSurfaceControl itself is not a View, it is just the interface to the
+ * windowing-system object that contains the entire view hierarchy.
+ * For the root View of a given hierarchy see {@link #getRootView}.
+
+ * @return The {@link android.view.AttachedSurfaceControl} interface for this View.
+ * This will only return a non-null value when called between {@link #onAttachedToWindow}
+ * and {@link #onDetachedFromWindow}.
*/
- public @Nullable ViewRoot getViewRoot() {
+ public @Nullable AttachedSurfaceControl getRootSurfaceControl() {
if (mAttachInfo != null) {
- return mAttachInfo.getViewRoot();
+ return mAttachInfo.getRootSurfaceControl();
}
return null;
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a02281b..679da31 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3581,7 +3581,9 @@
}
structure.setChildCount(childrenCount);
- ArrayList<View> preorderedList = buildOrderedChildList();
+ ArrayList<View> tempPreorderedList = buildOrderedChildList();
+ ArrayList<View> preorderedList =
+ tempPreorderedList != null ? new ArrayList<>(tempPreorderedList) : null;
boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
for (int i = 0; i < childrenCount; i++) {
@@ -9275,21 +9277,23 @@
/**
* {@inheritDoc}
*
- * The implementation calls {@link #dispatchRequestTranslation} for all the child views.
+ * The implementation calls {@link #dispatchCreateViewTranslationRequest} for all the child
+ * views.
*/
@Override
- public void dispatchRequestTranslation(@NonNull Map<AutofillId, long[]> viewIds,
+ public void dispatchCreateViewTranslationRequest(@NonNull Map<AutofillId, long[]> viewIds,
@NonNull @DataFormat int[] supportedFormats,
@Nullable TranslationCapability capability,
@NonNull List<ViewTranslationRequest> requests) {
- super.dispatchRequestTranslation(viewIds, supportedFormats, capability, requests);
+ super.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability, requests);
final int childCount = getChildCount();
if (childCount == 0) {
return;
}
for (int i = 0; i < childCount; ++i) {
final View child = getChildAt(i);
- child.dispatchRequestTranslation(viewIds, supportedFormats, capability, requests);
+ child.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability,
+ requests);
}
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a06f193..9bbd5dd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -224,7 +224,8 @@
*/
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
- View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, ViewRoot {
+ View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
+ AttachedSurfaceControl {
private static final String TAG = "ViewRootImpl";
private static final boolean DBG = false;
private static final boolean LOCAL_LOGV = false;
@@ -1200,8 +1201,7 @@
Looper.myLooper());
if (mAttachInfo.mThreadedRenderer != null) {
- InputMetricsListener listener =
- new InputMetricsListener(mInputEventReceiver);
+ InputMetricsListener listener = new InputMetricsListener();
mHardwareRendererObserver = new HardwareRendererObserver(
listener, listener.data, mHandler, true /*waitForPresentTime*/);
mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver);
@@ -1355,6 +1355,11 @@
}
}
+ /**
+ * Register a callback to be executed when Webview overlay needs to merge a transaction.
+ * This callback will be executed on RenderThread worker thread, and released inside native code
+ * when CanvasContext is destroyed.
+ */
private void addASurfaceTransactionCallback() {
HardwareRenderer.ASurfaceTransactionCallback callback = (nativeTransactionObj,
nativeSurfaceControlObj,
@@ -1408,6 +1413,9 @@
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mHardwareAccelerated =
mAttachInfo.mHardwareAccelerationRequested = true;
+ if (mHardwareRendererObserver != null) {
+ mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver);
+ }
}
}
}
@@ -7499,7 +7507,8 @@
final View prevDragView = mCurrentDragView;
if (what == DragEvent.ACTION_DROP && event.mClipData != null) {
- event.mClipData.prepareToEnterProcess();
+ event.mClipData.prepareToEnterProcess(
+ mView.getContext().getAttributionSource());
}
// Now dispatch the drag/drop event
@@ -7709,6 +7718,7 @@
}
}
if (mAttachInfo.mThreadedRenderer != null) {
+ addASurfaceTransactionCallback();
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
}
} else {
@@ -8110,6 +8120,9 @@
ThreadedRenderer hardwareRenderer = mAttachInfo.mThreadedRenderer;
if (hardwareRenderer != null) {
+ if (mHardwareRendererObserver != null) {
+ hardwareRenderer.removeObserver(mHardwareRendererObserver);
+ }
if (mView != null) {
hardwareRenderer.destroyHardwareResources(mView);
}
@@ -8611,18 +8624,12 @@
super.dispose();
}
}
- WindowInputEventReceiver mInputEventReceiver;
+ private WindowInputEventReceiver mInputEventReceiver;
final class InputMetricsListener
implements HardwareRendererObserver.OnFrameMetricsAvailableListener {
public long[] data = new long[FrameMetrics.Index.FRAME_STATS_COUNT];
- private InputEventReceiver mReceiver;
-
- InputMetricsListener(InputEventReceiver receiver) {
- mReceiver = receiver;
- }
-
@Override
public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
final int inputEventId = (int) data[FrameMetrics.Index.INPUT_EVENT_ID];
@@ -8635,6 +8642,20 @@
// available, we cannot compute end-to-end input latency metrics.
return;
}
+ final long gpuCompletedTime = data[FrameMetrics.Index.GPU_COMPLETED];
+ if (mInputEventReceiver == null) {
+ return;
+ }
+ if (gpuCompletedTime >= presentTime) {
+ final double discrepancyMs = (gpuCompletedTime - presentTime) * 1E-6;
+ final long vsyncId = data[FrameMetrics.Index.FRAME_TIMELINE_VSYNC_ID];
+ Log.w(TAG, "Not reporting timeline because gpuCompletedTime is " + discrepancyMs
+ + "ms ahead of presentTime. FRAME_TIMELINE_VSYNC_ID=" + vsyncId
+ + ", INPUT_EVENT_ID=" + inputEventId);
+ // TODO(b/186664409): figure out why this sometimes happens
+ return;
+ }
+ mInputEventReceiver.reportTimeline(inputEventId, gpuCompletedTime, presentTime);
}
}
HardwareRendererObserver mHardwareRendererObserver;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 52a09701..aa9ea19 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1354,6 +1354,11 @@
public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
}
+ /** @hide */
+ public boolean decorFitsSystemWindows() {
+ return false;
+ }
+
/**
* Specify custom window attributes. <strong>PLEASE NOTE:</strong> the
* layout params you give here should generally be from values previously
@@ -1714,8 +1719,9 @@
* The window background drawable is drawn on top of the blurred region. The blur
* region bounds and rounded corners will mimic those of the background drawable.
* </p><p>
- * For the blur region to be visible, the window has to be translucent. See
- * {@link android.R.styleable#Window_windowIsTranslucent}.
+ * For the blur region to be visible, the window has to be translucent
+ * (see {@link android.R.attr#windowIsTranslucent}) and floating
+ * (see {@link android.R.attr#windowIsFloating}).
* </p><p>
* Note the difference with {@link WindowManager.LayoutParams#setBlurBehindRadius},
* which blurs the whole screen behind the window. Background blur blurs the screen behind
@@ -2722,11 +2728,12 @@
/**
* This will be null before a content view is added, e.g. via
- * {@link #setContentView} or {@link #addContentView}.
+ * {@link #setContentView} or {@link #addContentView}. See
+ * {@link android.view.View#getRootSurfaceControl}.
*
- * @return The {@link android.view.ViewRoot} interface for this Window
+ * @return The {@link android.view.AttachedSurfaceControl} interface for this Window
*/
- public @Nullable ViewRoot getViewRoot() {
+ public @Nullable AttachedSurfaceControl getRootSurfaceControl() {
return null;
}
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c32ab3a..c1e394d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3010,6 +3010,14 @@
public int preferredDisplayModeId;
/**
+ * The max display refresh rate while the window is in focus.
+ *
+ * This value is ignored if {@link #preferredDisplayModeId} is set.
+ * @hide
+ */
+ public float preferredMaxDisplayRefreshRate;
+
+ /**
* An internal annotation for flags that can be specified to {@link #systemUiVisibility}
* and {@link #subtreeSystemUiVisibility}.
*
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index c26b302..9b463bb 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -638,10 +638,11 @@
}
/**
- * Return {@code true} if this input method should be shown in the IME picker.
- * @hide
+ * Returns {@code true} if this input method should be shown in menus for selecting an Input
+ * Method, such as the system Input Method Picker. This is {@code false} if the IME is intended
+ * to be accessed programmatically.
*/
- public boolean showInInputMethodPicker() {
+ public boolean shouldShowInInputMethodPicker() {
return mShowInInputMethodPicker;
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 4298550..7eb8400 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1519,17 +1519,25 @@
}
void setInputChannelLocked(InputChannel channel) {
- if (mCurChannel != channel) {
- if (mCurSender != null) {
- flushPendingEventsLocked();
- mCurSender.dispose();
- mCurSender = null;
- }
- if (mCurChannel != null) {
- mCurChannel.dispose();
- }
- mCurChannel = channel;
+ if (mCurChannel == channel) {
+ return;
}
+ if (mCurChannel != null && channel != null
+ && mCurChannel.getToken() == channel.getToken()) {
+ // channel is a dupe of 'mCurChannel', because they have the same token, and represent
+ // the same connection. Ignore the incoming channel and keep using 'mCurChannel' to
+ // avoid confusing the InputEventReceiver.
+ return;
+ }
+ if (mCurSender != null) {
+ flushPendingEventsLocked();
+ mCurSender.dispose();
+ mCurSender = null;
+ }
+ if (mCurChannel != null) {
+ mCurChannel.dispose();
+ }
+ mCurChannel = channel;
}
/**
diff --git a/core/java/android/view/translation/TranslationContext.java b/core/java/android/view/translation/TranslationContext.java
index 1d3d182..2a5dedd 100644
--- a/core/java/android/view/translation/TranslationContext.java
+++ b/core/java/android/view/translation/TranslationContext.java
@@ -37,9 +37,9 @@
*/
public static final @TranslationFlag int FLAG_TRANSLITERATION = 0x2;
/**
- * This context will enable the {@link Translator} to return dictionary results.
+ * This context will enable the {@link Translator} to return dictionary definitions.
*/
- public static final @TranslationFlag int FLAG_DICTIONARY_DESCRIPTION = 0x4;
+ public static final @TranslationFlag int FLAG_DEFINITIONS = 0x4;
/**
* {@link TranslationSpec} describing the source data to be translated.
@@ -69,7 +69,7 @@
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -86,7 +86,7 @@
@android.annotation.IntDef(flag = true, prefix = "FLAG_", value = {
FLAG_LOW_LATENCY,
FLAG_TRANSLITERATION,
- FLAG_DICTIONARY_DESCRIPTION
+ FLAG_DEFINITIONS
})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
@DataClass.Generated.Member
@@ -106,8 +106,8 @@
return "FLAG_LOW_LATENCY";
case FLAG_TRANSLITERATION:
return "FLAG_TRANSLITERATION";
- case FLAG_DICTIONARY_DESCRIPTION:
- return "FLAG_DICTIONARY_DESCRIPTION";
+ case FLAG_DEFINITIONS:
+ return "FLAG_DEFINITIONS";
default: return Integer.toHexString(value);
}
}
@@ -129,7 +129,7 @@
mTranslationFlags,
FLAG_LOW_LATENCY
| FLAG_TRANSLITERATION
- | FLAG_DICTIONARY_DESCRIPTION);
+ | FLAG_DEFINITIONS);
// onConstructed(); // You can define this method to get a callback
}
@@ -209,7 +209,7 @@
mTranslationFlags,
FLAG_LOW_LATENCY
| FLAG_TRANSLITERATION
- | FLAG_DICTIONARY_DESCRIPTION);
+ | FLAG_DEFINITIONS);
// onConstructed(); // You can define this method to get a callback
}
@@ -295,10 +295,10 @@
}
@DataClass.Generated(
- time = 1616199021789L,
- codegenVersion = "1.0.22",
+ time = 1621034221152L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/TranslationContext.java",
- inputSignatures = "public static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_LOW_LATENCY\npublic static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_TRANSLITERATION\npublic static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_DICTIONARY_DESCRIPTION\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mTranslationFlags\nprivate static int defaultTranslationFlags()\nclass TranslationContext extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_LOW_LATENCY\npublic static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_TRANSLITERATION\npublic static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_DEFINITIONS\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mTranslationFlags\nprivate static int defaultTranslationFlags()\nclass TranslationContext extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/translation/TranslationRequest.java b/core/java/android/view/translation/TranslationRequest.java
index 1dc711b..7082e2b 100644
--- a/core/java/android/view/translation/TranslationRequest.java
+++ b/core/java/android/view/translation/TranslationRequest.java
@@ -98,9 +98,26 @@
return Collections.emptyList();
}
+ abstract static class BaseBuilder {
+ /**
+ * @deprecated use {@link Builder#setTranslationRequestValues(List)}.
+ */
+ @Deprecated
+ public abstract Builder addTranslationRequestValue(
+ @NonNull TranslationRequestValue value);
+
+ /**
+ * @deprecated use {@link Builder#setViewTranslationRequests(List)}.
+ */
+ @Deprecated
+ public abstract Builder addViewTranslationRequest(
+ @NonNull ViewTranslationRequest value);
+
+ }
- // Code below generated by codegen v1.0.22.
+
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -275,7 +292,7 @@
*/
@SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member
- public static final class Builder {
+ public static final class Builder extends BaseBuilder {
private @RequestFlags int mFlags;
private @NonNull List<TranslationRequestValue> mTranslationRequestValues;
@@ -312,6 +329,8 @@
/** @see #setTranslationRequestValues */
@DataClass.Generated.Member
+ @Override
+ @Deprecated
public @NonNull Builder addTranslationRequestValue(@NonNull TranslationRequestValue value) {
if (mTranslationRequestValues == null) setTranslationRequestValues(new ArrayList<>());
mTranslationRequestValues.add(value);
@@ -333,6 +352,8 @@
/** @see #setViewTranslationRequests */
@DataClass.Generated.Member
+ @Override
+ @Deprecated
public @NonNull Builder addViewTranslationRequest(@NonNull ViewTranslationRequest value) {
if (mViewTranslationRequests == null) setViewTranslationRequests(new ArrayList<>());
mViewTranslationRequests.add(value);
@@ -369,10 +390,10 @@
}
@DataClass.Generated(
- time = 1614132376448L,
- codegenVersion = "1.0.22",
+ time = 1620429997487L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/TranslationRequest.java",
- inputSignatures = "public static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_DICTIONARY_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLITERATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_PARTIAL_RESPONSES\nprivate final @android.view.translation.TranslationRequest.RequestFlags int mFlags\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationRequestValue\") java.util.List<android.view.translation.TranslationRequestValue> mTranslationRequestValues\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"viewTranslationRequest\") java.util.List<android.view.translation.ViewTranslationRequest> mViewTranslationRequests\nprivate static int defaultFlags()\nprivate static java.util.List<android.view.translation.TranslationRequestValue> defaultTranslationRequestValues()\nprivate static java.util.List<android.view.translation.ViewTranslationRequest> defaultViewTranslationRequests()\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genBuilder=true)")
+ inputSignatures = "public static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_DICTIONARY_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLITERATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_PARTIAL_RESPONSES\nprivate final @android.view.translation.TranslationRequest.RequestFlags int mFlags\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationRequestValue\") java.util.List<android.view.translation.TranslationRequestValue> mTranslationRequestValues\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"viewTranslationRequest\") java.util.List<android.view.translation.ViewTranslationRequest> mViewTranslationRequests\nprivate static int defaultFlags()\nprivate static java.util.List<android.view.translation.TranslationRequestValue> defaultTranslationRequestValues()\nprivate static java.util.List<android.view.translation.ViewTranslationRequest> defaultViewTranslationRequests()\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addTranslationRequestValue(android.view.translation.TranslationRequestValue)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addViewTranslationRequest(android.view.translation.ViewTranslationRequest)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genBuilder=true)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addTranslationRequestValue(android.view.translation.TranslationRequestValue)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addViewTranslationRequest(android.view.translation.ViewTranslationRequest)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/translation/TranslationRequestValue.java b/core/java/android/view/translation/TranslationRequestValue.java
index 0619618..5178cb1 100644
--- a/core/java/android/view/translation/TranslationRequestValue.java
+++ b/core/java/android/view/translation/TranslationRequestValue.java
@@ -46,22 +46,17 @@
}
/**
- * @return the text value as a {@link CharSequence}.
- *
- * @throws IllegalStateException if the format of this {@link TranslationRequestValue} is not a
- * text value.
+ * @return the text value as a {@link CharSequence} or {@code null} if the value is not of type
+ * text.
*/
- @NonNull
+ @Nullable
public CharSequence getText() {
- if (mText == null) {
- throw new IllegalStateException("Value is not of type text");
- }
return mText;
}
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -111,7 +106,7 @@
TranslationRequestValue that = (TranslationRequestValue) o;
//noinspection PointlessBooleanExpression
return true
- && java.util.Objects.equals(mText, that.mText);
+ && Objects.equals(mText, that.mText);
}
@Override
@@ -121,7 +116,7 @@
// int fieldNameHashCode() { ... }
int _hash = 1;
- _hash = 31 * _hash + java.util.Objects.hashCode(mText);
+ _hash = 31 * _hash + Objects.hashCode(mText);
return _hash;
}
@@ -171,10 +166,10 @@
};
@DataClass.Generated(
- time = 1613687761635L,
- codegenVersion = "1.0.22",
+ time = 1620259864154L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/TranslationRequestValue.java",
- inputSignatures = "private final @android.annotation.Nullable java.lang.CharSequence mText\npublic static @android.annotation.NonNull android.view.translation.TranslationRequestValue forText(java.lang.CharSequence)\npublic @android.annotation.NonNull java.lang.CharSequence getText()\nclass TranslationRequestValue extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genToString=true, genEqualsHashCode=true)")
+ inputSignatures = "private final @android.annotation.Nullable java.lang.CharSequence mText\npublic static @android.annotation.NonNull android.view.translation.TranslationRequestValue forText(java.lang.CharSequence)\npublic @android.annotation.Nullable java.lang.CharSequence getText()\nclass TranslationRequestValue extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genToString=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/translation/TranslationResponseValue.java b/core/java/android/view/translation/TranslationResponseValue.java
index e8e9868..a68ae56 100644
--- a/core/java/android/view/translation/TranslationResponseValue.java
+++ b/core/java/android/view/translation/TranslationResponseValue.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -42,6 +43,17 @@
public static final int STATUS_ERROR = 1;
/**
+ * Name in the result of {@link #getExtras()} to pass dictionary definitions of the text
+ * categorized by parts of speech.
+ *
+ * <p>The dictionary definitions consists of groups of terms keyed by their corresponding parts
+ * of speech. This map-like structure is stored in a {@link Bundle}. The individual parts of
+ * speech can be traversed by {@link Bundle#keySet()} and used to get the corresponding list
+ * of terms as {@link CharSequence}s.</p>
+ */
+ public static final String EXTRA_DEFINITIONS = "android.view.translation.extra.DEFINITIONS";
+
+ /**
* The status code of this {@link TranslationResponseValue}.
*
* <p>If the status code is {@link #STATUS_ERROR}, no values are attached, and all getters will
@@ -56,11 +68,12 @@
private final CharSequence mText;
/**
- * The dictionary description of the translated text.
- * TODO: Describe the result structure.
+ * Extra results associated with the translated text.
+ *
+ * <p>The bundle includes {@link #EXTRA_DEFINITIONS}, obtained by {@link Bundle#getBundle}.</p>
*/
- @Nullable
- private final CharSequence mDictionaryDescription;
+ @NonNull
+ private final Bundle mExtras;
/**
* The transliteration result of the translated text.
@@ -74,15 +87,15 @@
*/
@NonNull
public static TranslationResponseValue forError() {
- return new TranslationResponseValue(STATUS_ERROR, null, null, null);
+ return new TranslationResponseValue(STATUS_ERROR, null, Bundle.EMPTY, null);
}
private static CharSequence defaultText() {
return null;
}
- private static CharSequence defaultDictionaryDescription() {
- return null;
+ private static Bundle defaultExtras() {
+ return Bundle.EMPTY;
}
private static CharSequence defaultTransliteration() {
@@ -96,7 +109,7 @@
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -134,7 +147,7 @@
/* package-private */ TranslationResponseValue(
@Status int statusCode,
@Nullable CharSequence text,
- @Nullable CharSequence dictionaryDescription,
+ @NonNull Bundle extras,
@Nullable CharSequence transliteration) {
this.mStatusCode = statusCode;
@@ -147,7 +160,9 @@
}
this.mText = text;
- this.mDictionaryDescription = dictionaryDescription;
+ this.mExtras = extras;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mExtras);
this.mTransliteration = transliteration;
// onConstructed(); // You can define this method to get a callback
@@ -173,12 +188,13 @@
}
/**
- * The dictionary description of the translated text.
- * TODO: Describe the result structure.
+ * Extra results associated with the translated text.
+ *
+ * <p>The bundle includes {@link #EXTRA_DEFINITIONS}, obtained by {@link Bundle#getBundle}.</p>
*/
@DataClass.Generated.Member
- public @Nullable CharSequence getDictionaryDescription() {
- return mDictionaryDescription;
+ public @NonNull Bundle getExtras() {
+ return mExtras;
}
/**
@@ -199,7 +215,7 @@
return "TranslationResponseValue { " +
"statusCode = " + statusToString(mStatusCode) + ", " +
"text = " + mText + ", " +
- "dictionaryDescription = " + mDictionaryDescription + ", " +
+ "extras = " + mExtras + ", " +
"transliteration = " + mTransliteration +
" }";
}
@@ -219,7 +235,7 @@
return true
&& mStatusCode == that.mStatusCode
&& Objects.equals(mText, that.mText)
- && Objects.equals(mDictionaryDescription, that.mDictionaryDescription)
+ && Objects.equals(mExtras, that.mExtras)
&& Objects.equals(mTransliteration, that.mTransliteration);
}
@@ -232,7 +248,7 @@
int _hash = 1;
_hash = 31 * _hash + mStatusCode;
_hash = 31 * _hash + Objects.hashCode(mText);
- _hash = 31 * _hash + Objects.hashCode(mDictionaryDescription);
+ _hash = 31 * _hash + Objects.hashCode(mExtras);
_hash = 31 * _hash + Objects.hashCode(mTransliteration);
return _hash;
}
@@ -245,12 +261,11 @@
byte flg = 0;
if (mText != null) flg |= 0x2;
- if (mDictionaryDescription != null) flg |= 0x4;
if (mTransliteration != null) flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mStatusCode);
if (mText != null) dest.writeCharSequence(mText);
- if (mDictionaryDescription != null) dest.writeCharSequence(mDictionaryDescription);
+ dest.writeBundle(mExtras);
if (mTransliteration != null) dest.writeCharSequence(mTransliteration);
}
@@ -268,7 +283,7 @@
byte flg = in.readByte();
int statusCode = in.readInt();
CharSequence text = (flg & 0x2) == 0 ? null : (CharSequence) in.readCharSequence();
- CharSequence dictionaryDescription = (flg & 0x4) == 0 ? null : (CharSequence) in.readCharSequence();
+ Bundle extras = in.readBundle();
CharSequence transliteration = (flg & 0x8) == 0 ? null : (CharSequence) in.readCharSequence();
this.mStatusCode = statusCode;
@@ -282,7 +297,9 @@
}
this.mText = text;
- this.mDictionaryDescription = dictionaryDescription;
+ this.mExtras = extras;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mExtras);
this.mTransliteration = transliteration;
// onConstructed(); // You can define this method to get a callback
@@ -311,7 +328,7 @@
private @Status int mStatusCode;
private @Nullable CharSequence mText;
- private @Nullable CharSequence mDictionaryDescription;
+ private @NonNull Bundle mExtras;
private @Nullable CharSequence mTransliteration;
private long mBuilderFieldsSet = 0L;
@@ -351,14 +368,15 @@
}
/**
- * The dictionary description of the translated text.
- * TODO: Describe the result structure.
+ * Extra results associated with the translated text.
+ *
+ * <p>The bundle includes {@link #EXTRA_DEFINITIONS}, obtained by {@link Bundle#getBundle}.</p>
*/
@DataClass.Generated.Member
- public @NonNull Builder setDictionaryDescription(@NonNull CharSequence value) {
+ public @NonNull Builder setExtras(@NonNull Bundle value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
- mDictionaryDescription = value;
+ mExtras = value;
return this;
}
@@ -383,7 +401,7 @@
mText = defaultText();
}
if ((mBuilderFieldsSet & 0x4) == 0) {
- mDictionaryDescription = defaultDictionaryDescription();
+ mExtras = defaultExtras();
}
if ((mBuilderFieldsSet & 0x8) == 0) {
mTransliteration = defaultTransliteration();
@@ -391,7 +409,7 @@
TranslationResponseValue o = new TranslationResponseValue(
mStatusCode,
mText,
- mDictionaryDescription,
+ mExtras,
mTransliteration);
return o;
}
@@ -405,10 +423,10 @@
}
@DataClass.Generated(
- time = 1614983829716L,
- codegenVersion = "1.0.22",
+ time = 1621034223313L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/TranslationResponseValue.java",
- inputSignatures = "public static final int STATUS_SUCCESS\npublic static final int STATUS_ERROR\nprivate final @android.view.translation.TranslationResponseValue.Status int mStatusCode\nprivate final @android.annotation.Nullable java.lang.CharSequence mText\nprivate final @android.annotation.Nullable java.lang.CharSequence mDictionaryDescription\nprivate final @android.annotation.Nullable java.lang.CharSequence mTransliteration\npublic static @android.annotation.NonNull android.view.translation.TranslationResponseValue forError()\nprivate static java.lang.CharSequence defaultText()\nprivate static java.lang.CharSequence defaultDictionaryDescription()\nprivate static java.lang.CharSequence defaultTransliteration()\nclass TranslationResponseValue extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int STATUS_SUCCESS\npublic static final int STATUS_ERROR\npublic static final java.lang.String EXTRA_DEFINITIONS\nprivate final @android.view.translation.TranslationResponseValue.Status int mStatusCode\nprivate final @android.annotation.Nullable java.lang.CharSequence mText\nprivate final @android.annotation.NonNull android.os.Bundle mExtras\nprivate final @android.annotation.Nullable java.lang.CharSequence mTransliteration\npublic static @android.annotation.NonNull android.view.translation.TranslationResponseValue forError()\nprivate static java.lang.CharSequence defaultText()\nprivate static android.os.Bundle defaultExtras()\nprivate static java.lang.CharSequence defaultTransliteration()\nclass TranslationResponseValue extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 8a2b629..f79c329 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -30,6 +30,7 @@
import android.os.HandlerThread;
import android.os.Process;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.IntArray;
import android.util.Log;
import android.util.LongSparseArray;
@@ -77,6 +78,11 @@
private final ArrayMap<Pair<TranslationSpec, TranslationSpec>, Translator> mTranslators;
@NonNull
private final ArrayMap<AutofillId, WeakReference<View>> mViews;
+ /**
+ * Views for which {@link UiTranslationSpec#shouldPadContentForCompat()} is true.
+ */
+ @NonNull
+ private final ArraySet<AutofillId> mViewsToPadContent;
@NonNull
private final HandlerThread mWorkerThread;
@NonNull
@@ -88,6 +94,7 @@
mContext = context;
mViews = new ArrayMap<>();
mTranslators = new ArrayMap<>();
+ mViewsToPadContent = new ArraySet<>();
mWorkerThread =
new HandlerThread("UiTranslationController_" + mActivity.getComponentName(),
@@ -100,19 +107,27 @@
* Update the Ui translation state.
*/
public void updateUiTranslationState(@UiTranslationState int state, TranslationSpec sourceSpec,
- TranslationSpec targetSpec, List<AutofillId> views) {
+ TranslationSpec targetSpec, List<AutofillId> views,
+ UiTranslationSpec uiTranslationSpec) {
if (!mActivity.isResumed() && (state == STATE_UI_TRANSLATION_STARTED
|| state == STATE_UI_TRANSLATION_RESUMED)) {
return;
}
Log.i(TAG, "updateUiTranslationState state: " + stateToString(state)
- + (DEBUG ? ", views: " + views : ""));
+ + (DEBUG ? (", views: " + views + ", spec: " + uiTranslationSpec) : ""));
synchronized (mLock) {
mCurrentState = state;
}
switch (state) {
case STATE_UI_TRANSLATION_STARTED:
+ if (uiTranslationSpec != null && uiTranslationSpec.shouldPadContentForCompat()) {
+ synchronized (mLock) {
+ mViewsToPadContent.addAll(views);
+ // TODO: Cleanup disappeared views from mViews and mViewsToPadContent at
+ // some appropriate place.
+ }
+ }
final Pair<TranslationSpec, TranslationSpec> specs =
new Pair<>(sourceSpec, targetSpec);
if (!mTranslators.containsKey(specs)) {
@@ -177,6 +192,7 @@
pw.print(pfx); pw.print("autofillId: "); pw.println(autofillId);
pw.print(pfx); pw.print("view:"); pw.println(view);
}
+ pw.print(outerPrefix); pw.print("padded views: "); pw.println(mViewsToPadContent);
}
// TODO(b/182433547): we will remove debug rom condition before S release then we change
// change this back to "DEBUG"
@@ -374,8 +390,9 @@
return;
}
- // TODO: Do this for specific views on request only.
- callback.enableContentPadding();
+ if (mViewsToPadContent.contains(autofillId)) {
+ callback.enableContentPadding();
+ }
view.onViewTranslationResponse(response);
callback.onShowTranslation(view);
});
@@ -466,8 +483,8 @@
// traverse the hierarchy to collect ViewTranslationRequests
for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
View rootView = roots.get(rootNum).getView();
- rootView.dispatchRequestTranslation(viewIds, supportedFormats, capability,
- requests);
+ rootView.dispatchCreateViewTranslationRequest(viewIds, supportedFormats,
+ capability, requests);
}
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
UiTranslationController::sendTranslationRequest,
@@ -641,9 +658,7 @@
msg.append("text=").append(value.getText() == null
? "null"
: "string[" + value.getText().length() + "], ");
- msg.append("dict=").append(value.getDictionaryDescription() == null
- ? "null"
- : "string[" + value.getDictionaryDescription().length() + "], ");
+ //TODO: append dictionary results.
msg.append("transliteration=").append(value.getTransliteration() == null
? "null"
: "string[" + value.getTransliteration().length() + "]}, ");
diff --git a/core/java/android/view/translation/UiTranslationSpec.java b/core/java/android/view/translation/UiTranslationSpec.java
index b43dbce..3d1ebfd 100644
--- a/core/java/android/view/translation/UiTranslationSpec.java
+++ b/core/java/android/view/translation/UiTranslationSpec.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.widget.TextView;
import com.android.internal.util.DataClass;
@@ -35,36 +36,38 @@
public final class UiTranslationSpec implements Parcelable {
/**
- * Whether the original content of the view should be directly modified to include padding that
- * makes it the same size as the translated content. Defaults to {@code false}.
+ * Whether the original content of the view should be made to appear as if it is the same size
+ * as the translated content. Defaults to {@code false}.
* <p>
- * For {@link android.widget.TextView}, the system does not directly modify the original text,
- * rather changes the displayed content using a
- * {@link android.text.method.TransformationMethod}.
- * This can cause issues in apps that do not account for TransformationMethods. For example, an
- * app using DynamicLayout may use the calculated text offsets to operate on the original text,
- * but this can be problematic when the layout was calculated on translated text with a
- * different length.
+ * For {@link TextView}, the system does not directly modify the original text, rather
+ * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+ * can cause issues in apps that do not account for length-changing TransformationMethods. For
+ * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+ * original text, but this can cause crashes when the layout was calculated on translated text
+ * with a different length.
* <p>
- * If this is {@code true}, for a TextView the default implementation will append spaces to the
- * text to make the length the same as the translated text.
+ * If this is {@code true}, for a TextView the default implementation appends spaces to the
+ * result of {@link TextView#getText()} to make the length the same as the translated text.
+ * <p>
+ * This only affects apps with target SDK R or lower.
*/
private boolean mShouldPadContentForCompat = false;
/**
- * Whether the original content of the view should be directly modified to include padding that
- * makes it the same size as the translated content.
+ * Whether the original content of the view should be made to appear as if it is the same size
+ * as the translated content.
* <p>
- * For {@link android.widget.TextView}, the system does not directly modify the original text,
- * rather changes the displayed content using a
- * {@link android.text.method.TransformationMethod}.
- * This can cause issues in apps that do not account for TransformationMethods. For example, an
- * app using DynamicLayout may use the calculated text offsets to operate on the original text,
- * but this can be problematic when the layout was calculated on translated text with a
- * different length.
+ * For {@link TextView}, the system does not directly modify the original text, rather
+ * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+ * can cause issues in apps that do not account for length-changing TransformationMethods. For
+ * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+ * original text, but this can cause crashes when the layout was calculated on translated text
+ * with a different length.
* <p>
- * If this is {@code true}, for a TextView the default implementation will append spaces to the
- * text to make the length the same as the translated text.
+ * If this is {@code true}, for a TextView the default implementation appends spaces to the
+ * result of {@link TextView#getText()} to make the length the same as the translated text.
+ * <p>
+ * This only affects apps with target SDK R or lower.
*/
public boolean shouldPadContentForCompat() {
return mShouldPadContentForCompat;
@@ -190,19 +193,20 @@
}
/**
- * Whether the original content of the view should be directly modified to include padding that
- * makes it the same size as the translated content. Defaults to {@code false}.
+ * Whether the original content of the view should be made to appear as if it is the same size
+ * as the translated content. Defaults to {@code false}.
* <p>
- * For {@link android.widget.TextView}, the system does not directly modify the original text,
- * rather changes the displayed content using a
- * {@link android.text.method.TransformationMethod}.
- * This can cause issues in apps that do not account for TransformationMethods. For example, an
- * app using DynamicLayout may use the calculated text offsets to operate on the original text,
- * but this can be problematic when the layout was calculated on translated text with a
- * different length.
+ * For {@link TextView}, the system does not directly modify the original text, rather
+ * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+ * can cause issues in apps that do not account for length-changing TransformationMethods. For
+ * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+ * original text, but this can cause crashes when the layout was calculated on translated text
+ * with a different length.
* <p>
- * If this is {@code true}, for a TextView the default implementation will append spaces to the
- * text to make the length the same as the translated text.
+ * If this is {@code true}, for a TextView the default implementation appends spaces to the
+ * result of {@link TextView#getText()} to make the length the same as the translated text.
+ * <p>
+ * This only affects apps with target SDK R or lower.
*/
@DataClass.Generated.Member
public @NonNull Builder setShouldPadContentForCompat(boolean value) {
@@ -234,7 +238,7 @@
}
@DataClass.Generated(
- time = 1619034161701L,
+ time = 1620790033058L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/UiTranslationSpec.java",
inputSignatures = "private boolean mShouldPadContentForCompat\npublic boolean shouldPadContentForCompat()\nclass UiTranslationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genToString=true)")
diff --git a/core/java/android/view/translation/UiTranslationStateCallback.java b/core/java/android/view/translation/UiTranslationStateCallback.java
index 1bae0ef..9aa089d 100644
--- a/core/java/android/view/translation/UiTranslationStateCallback.java
+++ b/core/java/android/view/translation/UiTranslationStateCallback.java
@@ -31,7 +31,9 @@
* @deprecated use {@link #onStarted(ULocale, ULocale)} instead.
*/
@Deprecated
- void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale);
+ default void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale) {
+ // no-op
+ }
/**
* The system is requesting translation of the UI from {@code sourceLocale} to {@code
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a1d4822..0bbaac0f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -60,11 +60,13 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inspector.InspectableProperty;
import android.view.textclassifier.TextClassifier;
+import android.view.translation.TranslationCapability;
import android.view.translation.TranslationSpec.DataFormat;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
@@ -2869,6 +2871,16 @@
}
@Override
+ public void dispatchCreateViewTranslationRequest(@NonNull Map<AutofillId, long[]> viewIds,
+ @NonNull @DataFormat int[] supportedFormats,
+ @Nullable TranslationCapability capability,
+ @NonNull List<ViewTranslationRequest> requests) {
+ super.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability, requests);
+ mProvider.getViewDelegate().dispatchCreateViewTranslationRequest(viewIds, supportedFormats,
+ capability, requests);
+ }
+
+ @Override
public void onVirtualViewTranslationResponses(
@NonNull LongSparseArray<ViewTranslationResponse> response) {
mProvider.getViewDelegate().onVirtualViewTranslationResponses(response);
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index ad123c1..1b9ff44 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UptimeMillisLong;
import android.app.ActivityThread;
import android.app.Application;
import android.app.ResourcesManager;
@@ -221,14 +220,12 @@
}
/**
- * Returns an array of startup timestamps. For the specification of array
- * see {@link WebViewFactory.Timestamp}.
+ * Get the timestamps at which various WebView startup events occurred in this process.
* This method must be called on the same thread where the
* WebViewChromiumFactoryProvider#create method was invoked.
*/
@NonNull
- @UptimeMillisLong
- public long[] getTimestamps() {
- return WebViewFactory.getTimestamps();
+ public WebViewFactory.StartupTimestamps getStartupTimestamps() {
+ return WebViewFactory.getStartupTimestamps();
}
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 9a38512..cf6807e 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -16,9 +16,9 @@
package android.webkit;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.UptimeMillisLong;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.Application;
@@ -38,8 +38,6 @@
import android.util.Log;
import java.io.File;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
/**
@@ -72,44 +70,6 @@
private static boolean sWebViewDisabled;
private static String sDataDirectorySuffix; // stored here so it can be set without loading WV
- // Indices in sTimestamps array.
- /** @hide */
- @IntDef(value = {
- WEBVIEW_LOAD_START, CREATE_CONTEXT_START, CREATE_CONTEXT_END,
- ADD_ASSETS_START, ADD_ASSETS_END, GET_CLASS_LOADER_START, GET_CLASS_LOADER_END,
- NATIVE_LOAD_START, NATIVE_LOAD_END,
- PROVIDER_CLASS_FOR_NAME_START, PROVIDER_CLASS_FOR_NAME_END})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Timestamp {
- }
-
- /** When the overall WebView provider load began. */
- public static final int WEBVIEW_LOAD_START = 0;
- /** Before creating the WebView APK Context. */
- public static final int CREATE_CONTEXT_START = 1;
- /** After creating the WebView APK Context. */
- public static final int CREATE_CONTEXT_END = 2;
- /** Before adding WebView assets to AssetManager. */
- public static final int ADD_ASSETS_START = 3;
- /** After adding WebView assets to AssetManager. */
- public static final int ADD_ASSETS_END = 4;
- /** Before creating the WebView ClassLoader. */
- public static final int GET_CLASS_LOADER_START = 5;
- /** After creating the WebView ClassLoader. */
- public static final int GET_CLASS_LOADER_END = 6;
- /** Before preloading the WebView native library. */
- public static final int NATIVE_LOAD_START = 7;
- /** After preloading the WebView native library. */
- public static final int NATIVE_LOAD_END = 8;
- /** Before looking up the WebView provider class. */
- public static final int PROVIDER_CLASS_FOR_NAME_START = 9;
- /** After looking up the WebView provider class. */
- public static final int PROVIDER_CLASS_FOR_NAME_END = 10;
- private static final int TIMESTAMPS_SIZE = 11;
-
- // WebView startup timestamps. To access elements use {@link Timestamp}.
- private static long[] sTimestamps = new long[TIMESTAMPS_SIZE];
-
// Error codes for loadWebViewNativeLibraryFromPackage
public static final int LIBLOAD_SUCCESS = 0;
public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1;
@@ -130,6 +90,97 @@
// error for namespace lookup
public static final int LIBLOAD_FAILED_TO_FIND_NAMESPACE = 10;
+ /**
+ * Stores the timestamps at which various WebView startup events occurred in this process.
+ */
+ public static class StartupTimestamps {
+ long mWebViewLoadStart;
+ long mCreateContextStart;
+ long mCreateContextEnd;
+ long mAddAssetsStart;
+ long mAddAssetsEnd;
+ long mGetClassLoaderStart;
+ long mGetClassLoaderEnd;
+ long mNativeLoadStart;
+ long mNativeLoadEnd;
+ long mProviderClassForNameStart;
+ long mProviderClassForNameEnd;
+
+ StartupTimestamps() {}
+
+ /** When the overall WebView provider load began. */
+ @UptimeMillisLong
+ public long getWebViewLoadStart() {
+ return mWebViewLoadStart;
+ }
+
+ /** Before creating the WebView APK Context. */
+ @UptimeMillisLong
+ public long getCreateContextStart() {
+ return mCreateContextStart;
+ }
+
+ /** After creating the WebView APK Context. */
+ @UptimeMillisLong
+ public long getCreateContextEnd() {
+ return mCreateContextEnd;
+ }
+
+ /** Before adding WebView assets to AssetManager. */
+ @UptimeMillisLong
+ public long getAddAssetsStart() {
+ return mAddAssetsStart;
+ }
+
+ /** After adding WebView assets to AssetManager. */
+ @UptimeMillisLong
+ public long getAddAssetsEnd() {
+ return mAddAssetsEnd;
+ }
+
+ /** Before creating the WebView ClassLoader. */
+ @UptimeMillisLong
+ public long getGetClassLoaderStart() {
+ return mGetClassLoaderStart;
+ }
+
+ /** After creating the WebView ClassLoader. */
+ @UptimeMillisLong
+ public long getGetClassLoaderEnd() {
+ return mGetClassLoaderEnd;
+ }
+
+ /** Before preloading the WebView native library. */
+ @UptimeMillisLong
+ public long getNativeLoadStart() {
+ return mNativeLoadStart;
+ }
+
+ /** After preloading the WebView native library. */
+ @UptimeMillisLong
+ public long getNativeLoadEnd() {
+ return mNativeLoadEnd;
+ }
+
+ /** Before looking up the WebView provider class. */
+ @UptimeMillisLong
+ public long getProviderClassForNameStart() {
+ return mProviderClassForNameStart;
+ }
+
+ /** After looking up the WebView provider class. */
+ @UptimeMillisLong
+ public long getProviderClassForNameEnd() {
+ return mProviderClassForNameEnd;
+ }
+ }
+
+ static final StartupTimestamps sTimestamps = new StartupTimestamps();
+
+ @NonNull
+ static StartupTimestamps getStartupTimestamps() {
+ return sTimestamps;
+ }
private static String getWebViewPreparationErrorReason(int error) {
switch (error) {
@@ -273,7 +324,7 @@
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
- sTimestamps[WEBVIEW_LOAD_START] = SystemClock.uptimeMillis();
+ sTimestamps.mWebViewLoadStart = SystemClock.uptimeMillis();
final int uid = android.os.Process.myUid();
if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
|| uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
@@ -413,7 +464,7 @@
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"initialApplication.createApplicationContext");
- sTimestamps[CREATE_CONTEXT_START] = SystemClock.uptimeMillis();
+ sTimestamps.mCreateContextStart = SystemClock.uptimeMillis();
try {
// Construct an app context to load the Java code into the current app.
Context webViewContext = initialApplication.createApplicationContext(
@@ -422,7 +473,7 @@
sPackageInfo = newPackageInfo;
return webViewContext;
} finally {
- sTimestamps[CREATE_CONTEXT_END] = SystemClock.uptimeMillis();
+ sTimestamps.mCreateContextEnd = SystemClock.uptimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (RemoteException | PackageManager.NameNotFoundException e) {
@@ -448,26 +499,26 @@
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
try {
- sTimestamps[ADD_ASSETS_START] = SystemClock.uptimeMillis();
+ sTimestamps.mAddAssetsStart = SystemClock.uptimeMillis();
for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) {
initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath);
}
- sTimestamps[ADD_ASSETS_END] = sTimestamps[GET_CLASS_LOADER_START] =
+ sTimestamps.mAddAssetsEnd = sTimestamps.mGetClassLoaderStart =
SystemClock.uptimeMillis();
ClassLoader clazzLoader = webViewContext.getClassLoader();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
- sTimestamps[GET_CLASS_LOADER_END] = sTimestamps[NATIVE_LOAD_START] =
+ sTimestamps.mGetClassLoaderEnd = sTimestamps.mNativeLoadStart =
SystemClock.uptimeMillis();
WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
getWebViewLibrary(sPackageInfo.applicationInfo));
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
- sTimestamps[NATIVE_LOAD_END] = sTimestamps[PROVIDER_CLASS_FOR_NAME_START] =
+ sTimestamps.mNativeLoadEnd = sTimestamps.mProviderClassForNameStart =
SystemClock.uptimeMillis();
try {
return getWebViewProviderClass(clazzLoader);
} finally {
- sTimestamps[PROVIDER_CLASS_FOR_NAME_END] = SystemClock.uptimeMillis();
+ sTimestamps.mProviderClassForNameEnd = SystemClock.uptimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (ClassNotFoundException e) {
@@ -529,9 +580,4 @@
return IWebViewUpdateService.Stub.asInterface(
ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
}
-
- @NonNull
- static long[] getTimestamps() {
- return sTimestamps;
- }
}
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 8d996ee..f9f823b 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -45,10 +45,12 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.textclassifier.TextClassifier;
+import android.view.translation.TranslationCapability;
import android.view.translation.TranslationSpec.DataFormat;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
@@ -59,6 +61,7 @@
import java.io.BufferedWriter;
import java.io.File;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -377,6 +380,14 @@
LongSparseArray<ViewTranslationResponse> response) {
}
+ default void dispatchCreateViewTranslationRequest(
+ @NonNull @SuppressWarnings("unused") Map<AutofillId, long[]> viewIds,
+ @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats,
+ @Nullable @SuppressWarnings("unused") TranslationCapability capability,
+ @NonNull @SuppressWarnings("unused") List<ViewTranslationRequest> requests) {
+
+ }
+
public AccessibilityNodeProvider getAccessibilityNodeProvider();
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 9eebf06..786e6fc 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -24,7 +24,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.ColorStateList;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.BlendMode;
import android.graphics.Canvas;
@@ -40,8 +39,9 @@
import java.time.Clock;
import java.time.DateTimeException;
+import java.time.Duration;
import java.time.Instant;
-import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.time.ZoneId;
import java.util.Formatter;
import java.util.Locale;
@@ -61,8 +61,8 @@
@Deprecated
public class AnalogClock extends View {
private static final String LOG_TAG = "AnalogClock";
- /** How often the clock should refresh to make the seconds hand advance at ~15 FPS. */
- private static final long SECONDS_TICK_FREQUENCY_MS = 1000 / 15;
+ /** How many times per second that the seconds hand advances. */
+ private static final long SECONDS_HAND_FPS = 30;
private Clock mClock;
@Nullable
@@ -106,7 +106,6 @@
public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- final Resources r = context.getResources();
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.AnalogClock, defStyleAttr, defStyleRes);
saveAttributeDataForStyleable(context, com.android.internal.R.styleable.AnalogClock,
@@ -716,25 +715,34 @@
}
private void onTimeChanged() {
- long nowMillis = mClock.millis();
- LocalDateTime localDateTime = toLocalDateTime(nowMillis, mClock.getZone());
+ Instant now = mClock.instant();
+ onTimeChanged(now.atZone(mClock.getZone()).toLocalTime(), now.toEpochMilli());
+ }
- int hour = localDateTime.getHour();
- int minute = localDateTime.getMinute();
- int second = localDateTime.getSecond();
+ private void onTimeChanged(LocalTime localTime, long nowMillis) {
+ float previousHour = mHour;
+ float previousMinutes = mMinutes;
- mSeconds = second + localDateTime.getNano() / 1_000_000_000f;
- mMinutes = minute + second / 60.0f;
- mHour = hour + mMinutes / 60.0f;
+ float rawSeconds = localTime.getSecond() + localTime.getNano() / 1_000_000_000f;
+ // We round the fraction of the second so that the seconds hand always occupies the same
+ // n positions between two given numbers, where n is the number of ticks per second. This
+ // ensures the second hand advances by a consistent distance despite our handler callbacks
+ // occurring at inconsistent frequencies.
+ mSeconds = Math.round(rawSeconds * SECONDS_HAND_FPS) / (float) SECONDS_HAND_FPS;
+ mMinutes = localTime.getMinute() + mSeconds / 60.0f;
+ mHour = localTime.getHour() + mMinutes / 60.0f;
mChanged = true;
- updateContentDescription(nowMillis);
+ // Update the content description only if the announced hours and minutes have changed.
+ if ((int) previousHour != (int) mHour || (int) previousMinutes != (int) mMinutes) {
+ updateContentDescription(nowMillis);
+ }
}
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
+ if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
createClock();
}
@@ -747,15 +755,32 @@
private final Runnable mSecondsTick = new Runnable() {
@Override
public void run() {
+ removeCallbacks(this);
if (!mVisible || mSecondHand == null) {
return;
}
- onTimeChanged();
+ Instant now = mClock.instant();
+ LocalTime localTime = now.atZone(mClock.getZone()).toLocalTime();
+ // How many milliseconds through the second we currently are.
+ long millisOfSecond = Duration.ofNanos(localTime.getNano()).toMillis();
+ // How many milliseconds there are between tick positions for the seconds hand.
+ double millisPerTick = 1000 / (double) SECONDS_HAND_FPS;
+ // How many milliseconds we are past the last tick position.
+ long millisPastLastTick = Math.round(millisOfSecond % millisPerTick);
+ // How many milliseconds there are until the next tick position.
+ long millisUntilNextTick = Math.round(millisPerTick - millisPastLastTick);
+ // If we are exactly at the tick position, this could be 0 milliseconds due to rounding.
+ // In this case, advance by the full amount of millis to the next position.
+ if (millisUntilNextTick <= 0) {
+ millisUntilNextTick = Math.round(millisPerTick);
+ }
+ // Schedule a callback for when the next tick should occur.
+ postDelayed(this, millisUntilNextTick);
+
+ onTimeChanged(localTime, now.toEpochMilli());
invalidate();
-
- postDelayed(this, SECONDS_TICK_FREQUENCY_MS);
}
};
@@ -782,14 +807,6 @@
setContentDescription(contentDescription);
}
- private static LocalDateTime toLocalDateTime(long timeMillis, ZoneId zoneId) {
- // java.time types like LocalDateTime / Instant can support the full range of "long millis"
- // with room to spare so we do not need to worry about overflow / underflow and the
- // resulting exceptions while the input to this class is a long.
- Instant instant = Instant.ofEpochMilli(timeMillis);
- return LocalDateTime.ofInstant(instant, zoneId);
- }
-
/**
* Tries to parse a {@link ZoneId} from {@code timeZone}, returning null if it is null or there
* is an error parsing.
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 53e145e..ec07209 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -259,6 +259,11 @@
}
mCurrentDate.set(Calendar.YEAR, year);
+ if (mCurrentDate.compareTo(mMinDate) < 0) {
+ mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
+ } else if (mCurrentDate.compareTo(mMaxDate) > 0) {
+ mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
+ }
onDateChanged(true, true);
// Automatically switch to day picker.
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 0cd9b36..1223d72 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -78,17 +78,6 @@
IDisplayAreaOrganizerController getDisplayAreaOrganizerController();
/**
- * Take a screenshot of the requested Window token and place the content of the screenshot into
- * outSurfaceControl. The SurfaceControl will be a child of the token's parent, so it will be
- * a sibling of the token's window
- * @param token The token for the WindowContainer that should get a screenshot taken.
- * @param outSurfaceControl The SurfaceControl where the screenshot will be attached.
- *
- * @return true if the screenshot was successful, false otherwise.
- */
- boolean takeScreenshot(in WindowContainerToken token, out SurfaceControl outSurfaceControl);
-
- /**
* Registers a transition player with Core. There is only one of these at a time and calling
* this will replace the existing one if set.
*/
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 8debe5d..d67fb32 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -17,6 +17,8 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import android.annotation.ColorInt;
import android.annotation.NonNull;
@@ -40,11 +42,13 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowInsetsController;
import android.view.WindowManager;
import android.widget.FrameLayout;
import com.android.internal.R;
import com.android.internal.policy.DecorView;
+import com.android.internal.util.ContrastColorUtil;
import java.time.Duration;
import java.time.Instant;
@@ -69,6 +73,12 @@
private static final String TAG = SplashScreenView.class.getSimpleName();
private static final boolean DEBUG = false;
+ private static final int LIGHT_BARS_MASK =
+ WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
+ | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+ private static final int WINDOW_FLAG_MASK = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
+ | FLAG_TRANSLUCENT_NAVIGATION | FLAG_TRANSLUCENT_STATUS;
+
private boolean mNotCopyable;
private boolean mRevealAnimationSupported = true;
private int mInitBackgroundColor;
@@ -84,10 +94,14 @@
private Activity mHostActivity;
// cache original window and status
private Window mWindow;
- private boolean mDrawBarBackground;
+ private int mAppWindowFlags;
private int mStatusBarColor;
private int mNavigationBarColor;
+ private int mSystemBarsAppearance;
private boolean mHasRemoved;
+ private boolean mNavigationContrastEnforced;
+ private boolean mStatusContrastEnforced;
+ private boolean mDecorFitsSystemWindows;
/**
* Internal builder to create a SplashScreenView object.
@@ -347,54 +361,63 @@
mWindow = null;
}
if (mHostActivity != null) {
- mHostActivity.detachSplashScreenView();
+ mHostActivity.setSplashScreenView(null);
+ mHostActivity = null;
}
mHasRemoved = true;
}
/**
- * Called when this view is attached to an activity.
+ * Called when this view is attached to an activity. This also makes SystemUI colors
+ * transparent so the content of splash screen view can draw fully.
* @hide
*/
- public void attachHostActivity(Activity activity) {
+ public void attachHostActivityAndSetSystemUIColors(Activity activity, Window window) {
+ activity.setSplashScreenView(this);
mHostActivity = activity;
- }
-
- /**
- * Cache the root window.
- * @hide
- */
- public void cacheRootWindow(Window window) {
mWindow = window;
+ final WindowManager.LayoutParams attr = window.getAttributes();
+ mAppWindowFlags = attr.flags;
+ mStatusBarColor = window.getStatusBarColor();
+ mNavigationBarColor = window.getNavigationBarColor();
+ mSystemBarsAppearance = window.getInsetsController().getSystemBarsAppearance();
+ mNavigationContrastEnforced = window.isNavigationBarContrastEnforced();
+ mStatusContrastEnforced = window.isStatusBarContrastEnforced();
+ mDecorFitsSystemWindows = window.decorFitsSystemWindows();
+
+ applySystemBarsContrastColor(window.getInsetsController(), mInitBackgroundColor);
+ // Let app draw the background of bars.
+ window.addFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ // Use specified bar colors instead of window background.
+ window.clearFlags(FLAG_TRANSLUCENT_STATUS | FLAG_TRANSLUCENT_NAVIGATION);
+ window.setStatusBarColor(Color.TRANSPARENT);
+ window.setNavigationBarColor(Color.TRANSPARENT);
+ window.setDecorFitsSystemWindows(false);
+ window.setStatusBarContrastEnforced(false);
+ window.setNavigationBarContrastEnforced(false);
+ }
+
+ /** Called when this view is removed from the host activity. */
+ private void restoreSystemUIColors() {
+ mWindow.setFlags(mAppWindowFlags, WINDOW_FLAG_MASK);
+ mWindow.setStatusBarColor(mStatusBarColor);
+ mWindow.setNavigationBarColor(mNavigationBarColor);
+ mWindow.getInsetsController().setSystemBarsAppearance(mSystemBarsAppearance,
+ LIGHT_BARS_MASK);
+ mWindow.setDecorFitsSystemWindows(mDecorFitsSystemWindows);
+ mWindow.setStatusBarContrastEnforced(mStatusContrastEnforced);
+ mWindow.setNavigationBarContrastEnforced(mNavigationContrastEnforced);
}
/**
- * Called after SplashScreenView has added on the root window.
+ * Makes the icon color of system bars contrast.
* @hide
*/
- public void makeSystemUIColorsTransparent() {
- if (mWindow != null) {
- final WindowManager.LayoutParams attr = mWindow.getAttributes();
- mDrawBarBackground = (attr.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
- mWindow.addFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- mStatusBarColor = mWindow.getStatusBarColor();
- mNavigationBarColor = mWindow.getNavigationBarDividerColor();
- mWindow.setStatusBarColor(Color.TRANSPARENT);
- mWindow.setNavigationBarColor(Color.TRANSPARENT);
- }
- setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
- }
-
- private void restoreSystemUIColors() {
- if (mWindow != null) {
- if (!mDrawBarBackground) {
- mWindow.clearFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- }
- mWindow.setStatusBarColor(mStatusBarColor);
- mWindow.setNavigationBarColor(mNavigationBarColor);
- }
+ public static void applySystemBarsContrastColor(WindowInsetsController windowInsetsController,
+ int backgroundColor) {
+ final int lightBarAppearance = ContrastColorUtil.isColorLight(backgroundColor)
+ ? LIGHT_BARS_MASK : 0;
+ windowInsetsController.setSystemBarsAppearance(lightBarAppearance, LIGHT_BARS_MASK);
}
/**
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index bcd0e8d..544d422 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -25,7 +25,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Singleton;
-import android.view.SurfaceControl;
/**
* Base class for organizing specific types of windows like Tasks and DisplayAreas
@@ -112,28 +111,6 @@
}
/**
- * Take a screenshot for a specified Window
- * @param token The token for the WindowContainer that should get a screenshot taken.
- * @return A SurfaceControl where the screenshot will be attached, or null if failed.
- *
- * @hide
- */
- @Nullable
- @RequiresPermission(android.Manifest.permission.READ_FRAME_BUFFER)
- public SurfaceControl takeScreenshot(@NonNull WindowContainerToken token) {
- try {
- SurfaceControl surfaceControl = new SurfaceControl();
- if (getWindowOrganizerController().takeScreenshot(token, surfaceControl)) {
- return surfaceControl;
- } else {
- return null;
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Register an ITransitionPlayer to handle transition animations.
* @hide
*/
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 49a3e29..14fd4c2 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -86,6 +86,11 @@
public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "ReduceBrightColors");
+ // The component name for the sub setting of Accessibility button in Accessibility settings
+ public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "AccessibilityButton");
+
+
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 281702e..3cf4621 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -92,7 +92,7 @@
void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages);
void setUserRestrictions(in Bundle restrictions, IBinder token, int userHandle);
- void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in String[] exceptionPackages);
+ void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in Map<String, String[]> excludedPackageTags);
void removeUser(int userHandle);
void startWatchingActive(in int[] ops, IAppOpsActiveCallback callback);
@@ -113,7 +113,7 @@
void stopWatchingAsyncNoted(String packageName, IAppOpsAsyncNotedCallback callback);
List<AsyncNotedAppOp> extractAsyncOps(String packageName);
- int checkOperationRaw(int code, int uid, String packageName);
+ int checkOperationRaw(int code, int uid, String packageName, @nullable String attributionTag);
void reloadNonHistoricalState();
diff --git a/core/java/com/android/internal/app/procstats/OWNERS b/core/java/com/android/internal/app/procstats/OWNERS
new file mode 100644
index 0000000..72c0a9e
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
index 1c7eca8..87df5eb 100644
--- a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
+++ b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
@@ -30,7 +30,5 @@
interface IFontManager {
FontConfig getFontConfig();
- int updateFontFile(in FontUpdateRequest request, int baseVersion);
-
int updateFontFamily(in List<FontUpdateRequest> request, int baseVersion);
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 0441594..f029e6a 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -28,6 +28,7 @@
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_APP_LAUNCH_FROM_WIDGET;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
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__LOCKSCREEN_PASSWORD_APPEAR;
@@ -144,6 +145,7 @@
public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24;
public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25;
public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26;
+ public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27;
private static final int NO_STATSD_LOGGING = -1;
@@ -179,6 +181,7 @@
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET,
};
private static volatile InteractionJankMonitor sInstance;
@@ -225,6 +228,7 @@
CUJ_LOCKSCREEN_TRANSITION_TO_AOD,
CUJ_LAUNCHER_OPEN_ALL_APPS,
CUJ_LAUNCHER_ALL_APPS_SCROLL,
+ CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -537,6 +541,8 @@
return "CUJ_LAUNCHER_OPEN_ALL_APPS";
case CUJ_LAUNCHER_ALL_APPS_SCROLL:
return "CUJ_LAUNCHER_ALL_APPS_SCROLL";
+ case CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET:
+ return "LAUNCHER_APP_LAUNCH_FROM_WIDGET";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/os/BatteryChargeCalculator.java b/core/java/com/android/internal/os/BatteryChargeCalculator.java
index 0690d1f..8178529 100644
--- a/core/java/com/android/internal/os/BatteryChargeCalculator.java
+++ b/core/java/com/android/internal/os/BatteryChargeCalculator.java
@@ -63,10 +63,15 @@
builder.setChargeTimeRemainingMs(chargeTimeRemainingMs / 1000);
}
+ long dischargeMah = batteryStats.getUahDischarge(BatteryStats.STATS_SINCE_CHARGED) / 1000;
+ if (dischargeMah == 0) {
+ dischargeMah = (long) ((dischargedPowerLowerBoundMah + dischargedPowerUpperBoundMah) / 2
+ + 0.5);
+ }
+
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .setConsumedPower(
- (dischargedPowerLowerBoundMah + dischargedPowerUpperBoundMah) / 2);
+ .setConsumedPower(dischargeMah);
}
@Override
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index db67bab..0938c85 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -610,18 +610,32 @@
}
public interface Clocks {
- public long elapsedRealtime();
- public long uptimeMillis();
+ /** Elapsed Realtime, see SystemClock.elapsedRealtime() */
+ long elapsedRealtime();
+
+ /** Uptime, see SystemClock.uptimeMillis() */
+ long uptimeMillis();
+
+ /** Wall-clock time as per System.currentTimeMillis() */
+ long currentTimeMillis();
}
public static class SystemClocks implements Clocks {
+
+ @Override
public long elapsedRealtime() {
return SystemClock.elapsedRealtime();
}
+ @Override
public long uptimeMillis() {
return SystemClock.uptimeMillis();
}
+
+ @Override
+ public long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
}
public interface ExternalStatsSync {
@@ -1163,7 +1177,7 @@
public BatteryStatsImpl(Clocks clocks) {
init(clocks);
- mStartClockTimeMs = System.currentTimeMillis();
+ mStartClockTimeMs = clocks.currentTimeMillis();
mStatsFile = null;
mCheckinFile = null;
mDailyFile = null;
@@ -3694,7 +3708,7 @@
if (dataSize == 0) {
// The history is currently empty; we need it to start with a time stamp.
- cur.currentTime = System.currentTimeMillis();
+ cur.currentTime = mClocks.currentTimeMillis();
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur);
}
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
@@ -3914,7 +3928,7 @@
}
public void noteCurrentTimeChangedLocked() {
- final long currentTime = System.currentTimeMillis();
+ final long currentTime = mClocks.currentTimeMillis();
final long elapsedRealtime = mClocks.elapsedRealtime();
final long uptime = mClocks.uptimeMillis();
noteCurrentTimeChangedLocked(currentTime, elapsedRealtime, uptime);
@@ -4237,7 +4251,7 @@
if (mPretendScreenOff != pretendScreenOff) {
mPretendScreenOff = pretendScreenOff;
noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis(), System.currentTimeMillis());
+ mClocks.elapsedRealtime(), mClocks.uptimeMillis(), mClocks.currentTimeMillis());
}
}
@@ -4827,7 +4841,7 @@
@GuardedBy("this")
public void noteScreenStateLocked(int state) {
noteScreenStateLocked(state, mClocks.elapsedRealtime(), mClocks.uptimeMillis(),
- System.currentTimeMillis());
+ mClocks.currentTimeMillis());
}
@GuardedBy("this")
@@ -5064,31 +5078,46 @@
}
public void notePowerSaveModeLocked(boolean enabled) {
- notePowerSaveModeLocked(enabled, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ notePowerSaveModeLocked(enabled, mClocks.elapsedRealtime(), mClocks.uptimeMillis(), false);
}
- public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs) {
+ /**
+ * Handles power save mode state changes.
+ */
+ public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs,
+ boolean forceLog) {
if (mPowerSaveModeEnabled != enabled) {
int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0;
- mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
- mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
+ mModStepMode |= (mCurStepMode & STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
+ mCurStepMode = (mCurStepMode & ~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
mPowerSaveModeEnabled = enabled;
if (enabled) {
mHistoryCur.states2 |= HistoryItem.STATE2_POWER_SAVE_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode enabled to: "
- + Integer.toHexString(mHistoryCur.states2));
+ if (DEBUG_HISTORY) {
+ Slog.v(TAG, "Power save mode enabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ }
mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtimeMs);
} else {
mHistoryCur.states2 &= ~HistoryItem.STATE2_POWER_SAVE_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode disabled to: "
- + Integer.toHexString(mHistoryCur.states2));
+ if (DEBUG_HISTORY) {
+ Slog.v(TAG, "Power save mode disabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ }
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtimeMs);
}
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
enabled
- ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
- : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+ ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+ : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+ } else if (forceLog) {
+ // Log an initial value for BATTERY_SAVER_MODE_STATE_CHANGED in order to
+ // allow the atom to read all future state changes.
+ FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
+ enabled
+ ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+ : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
@@ -6972,7 +7001,7 @@
}
@Override public long getStartClockTime() {
- final long currentTimeMs = System.currentTimeMillis();
+ final long currentTimeMs = mClocks.currentTimeMillis();
if ((currentTimeMs > MILLISECONDS_IN_YEAR
&& mStartClockTimeMs < (currentTimeMs - MILLISECONDS_IN_YEAR))
|| (mStartClockTimeMs > currentTimeMs)) {
@@ -10735,7 +10764,7 @@
public void updateDailyDeadlineLocked() {
// Get the current time.
- long currentTimeMs = mDailyStartTimeMs = System.currentTimeMillis();
+ long currentTimeMs = mDailyStartTimeMs = mClocks.currentTimeMillis();
Calendar calDeadline = Calendar.getInstance();
calDeadline.setTimeInMillis(currentTimeMs);
@@ -10763,7 +10792,7 @@
public void recordDailyStatsLocked() {
DailyItem item = new DailyItem();
item.mStartTime = mDailyStartTimeMs;
- item.mEndTime = System.currentTimeMillis();
+ item.mEndTime = mClocks.currentTimeMillis();
boolean hasData = false;
if (mDailyDischargeStepTracker.mNumStepDurations > 0) {
hasData = true;
@@ -11128,7 +11157,7 @@
}
void initTimes(long uptimeUs, long realtimeUs) {
- mStartClockTimeMs = System.currentTimeMillis();
+ mStartClockTimeMs = mClocks.currentTimeMillis();
mOnBatteryTimeBase.init(uptimeUs, realtimeUs);
mOnBatteryScreenOffTimeBase.init(uptimeUs, realtimeUs);
mRealtimeUs = 0;
@@ -11194,7 +11223,7 @@
final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000;
mStartCount = 0;
initTimes(uptimeUs, elapsedRealtimeUs);
- mScreenOnTimer.reset(false, uptimeUs);
+ mScreenOnTimer.reset(false, elapsedRealtimeUs);
mScreenDozeTimer.reset(false, elapsedRealtimeUs);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].reset(false, elapsedRealtimeUs);
@@ -13091,13 +13120,11 @@
if (Process.isIsolated(uid)) {
// This could happen if the isolated uid mapping was removed before that process
// was actually killed.
- mCpuUidUserSysTimeReader.removeUid(uid);
if (DEBUG) Slog.d(TAG, "Got readings for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
- mCpuUidUserSysTimeReader.removeUid(uid);
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
@@ -13202,19 +13229,16 @@
mWakeLockAllocationsUs = null;
final long startTimeMs = mClocks.uptimeMillis();
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final List<Integer> uidsToRemove = new ArrayList<>();
// If power is being accumulated for attribution, data needs to be read immediately.
final boolean forceRead = powerAccumulator != null;
mCpuUidFreqTimeReader.readDelta(forceRead, (uid, cpuFreqTimeMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
- uidsToRemove.add(uid);
if (DEBUG) Slog.d(TAG, "Got freq readings for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
- uidsToRemove.add(uid);
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
@@ -13278,9 +13302,6 @@
}
}
});
- for (int uid : uidsToRemove) {
- mCpuUidFreqTimeReader.removeUid(uid);
- }
final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
@@ -13332,25 +13353,19 @@
public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
final long startTimeMs = mClocks.uptimeMillis();
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final List<Integer> uidsToRemove = new ArrayList<>();
mCpuUidActiveTimeReader.readDelta(false, (uid, cpuActiveTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
- uidsToRemove.add(uid);
if (DEBUG) Slog.w(TAG, "Got active times for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
- uidsToRemove.add(uid);
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery);
});
- for (int uid : uidsToRemove) {
- mCpuUidActiveTimeReader.removeUid(uid);
- }
final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
@@ -13371,19 +13386,16 @@
@Nullable CpuDeltaPowerAccumulator powerAccumulator) {
final long startTimeMs = mClocks.uptimeMillis();
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final List<Integer> uidsToRemove = new ArrayList<>();
// If power is being accumulated for attribution, data needs to be read immediately.
final boolean forceRead = powerAccumulator != null;
mCpuUidClusterTimeReader.readDelta(forceRead, (uid, cpuClusterTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
- uidsToRemove.add(uid);
if (DEBUG) Slog.w(TAG, "Got cluster times for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
- uidsToRemove.add(uid);
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
@@ -13393,9 +13405,6 @@
powerAccumulator.addCpuClusterDurationsMs(u, cpuClusterTimesMs);
}
});
- for (int uid : uidsToRemove) {
- mCpuUidClusterTimeReader.removeUid(uid);
- }
final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
@@ -13562,7 +13571,7 @@
private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
boolean reset) {
mRecordingHistory = true;
- mHistoryCur.currentTime = System.currentTimeMillis();
+ mHistoryCur.currentTime = mClocks.currentTimeMillis();
addHistoryBufferLocked(elapsedRealtimeMs,
reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
mHistoryCur);
@@ -13604,7 +13613,7 @@
final int chargeFullUah, final long chargeTimeToFullSeconds) {
setBatteryStateLocked(status, health, plugType, level, temp, voltageMv, chargeUah,
chargeFullUah, chargeTimeToFullSeconds,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis(), System.currentTimeMillis());
+ mClocks.elapsedRealtime(), mClocks.uptimeMillis(), mClocks.currentTimeMillis());
}
public void setBatteryStateLocked(final int status, final int health, final int plugType,
@@ -14389,7 +14398,7 @@
}
public void shutdownLocked() {
- recordShutdownLocked(System.currentTimeMillis(), mClocks.elapsedRealtime());
+ recordShutdownLocked(mClocks.currentTimeMillis(), mClocks.elapsedRealtime());
writeSyncLocked();
mShuttingDown = true;
}
@@ -14938,7 +14947,7 @@
startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
}
- recordDailyStatsIfNeededLocked(false, System.currentTimeMillis());
+ recordDailyStatsIfNeededLocked(false, mClocks.currentTimeMillis());
}
public int describeContents() {
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 4989559..d4d6125 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -108,9 +108,9 @@
ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size());
synchronized (mStats) {
mStats.prepareForDumpLocked();
-
+ final long currentTimeMillis = currentTimeMillis();
for (int i = 0; i < queries.size(); i++) {
- results.add(getBatteryUsageStats(queries.get(i)));
+ results.add(getBatteryUsageStats(queries.get(i), currentTimeMillis));
}
}
return results;
@@ -121,6 +121,11 @@
*/
@VisibleForTesting
public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+ return getBatteryUsageStats(query, currentTimeMillis());
+ }
+
+ private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
+ long currentTimeMs) {
final long realtimeUs = elapsedRealtime() * 1000;
final long uptimeUs = uptimeMillis() * 1000;
@@ -129,7 +134,10 @@
final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder(
mStats.getCustomEnergyConsumerNames(), includePowerModels);
+ // TODO(b/188068523): use a monotonic clock to ensure resilience of order and duration
+ // of stats sessions to wall-clock adjustments
batteryUsageStatsBuilder.setStatsStartTimestamp(mStats.getStartClockTime());
+ batteryUsageStatsBuilder.setStatsEndTimestamp(currentTimeMs);
SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
for (int i = uidStats.size() - 1; i >= 0; i--) {
@@ -217,4 +225,12 @@
return SystemClock.uptimeMillis();
}
}
+
+ private long currentTimeMillis() {
+ if (mStats instanceof BatteryStatsImpl) {
+ return ((BatteryStatsImpl) mStats).mClocks.currentTimeMillis();
+ } else {
+ return System.currentTimeMillis();
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index c297a1f..57e1bf7 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -1127,11 +1127,10 @@
private final int mProcessSource;
public SettingsObserver(Context context, BinderCallsStats binderCallsStats,
- int processSource, int userHandle) {
+ int processSource) {
super(BackgroundThread.getHandler());
mContext = context;
- context.getContentResolver().registerContentObserver(mUri, false, this,
- userHandle);
+ context.getContentResolver().registerContentObserver(mUri, false, this);
mBinderCallsStats = binderCallsStats;
mProcessSource = processSource;
// Always kick once to ensure that we match current state
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 6e99bbb..c322258 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -51,7 +51,7 @@
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
- if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) {
+ if (!batteryStats.hasBluetoothActivityReporting()) {
return;
}
@@ -69,8 +69,8 @@
final ControllerActivityCounter activityCounter =
batteryStats.getBluetoothControllerActivity();
final long systemDurationMs = calculateDuration(activityCounter);
- final double systemPowerMah =
- calculatePowerMah(powerModel, measuredChargeUC, activityCounter);
+ final double systemPowerMah = calculatePowerMah(powerModel, measuredChargeUC,
+ activityCounter, query.shouldForceUsePowerProfileModel());
// Subtract what the apps used, but clamp to 0.
final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs);
@@ -100,7 +100,8 @@
final ControllerActivityCounter activityCounter =
app.getBatteryStatsUid().getBluetoothControllerActivity();
final long durationMs = calculateDuration(activityCounter);
- final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter);
+ final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter,
+ query.shouldForceUsePowerProfileModel());
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah, powerModel);
@@ -132,7 +133,7 @@
batteryStats.getBluetoothControllerActivity();
final long systemDurationMs = calculateDuration(activityCounter);
final double systemPowerMah =
- calculatePowerMah(powerModel, measuredChargeUC, activityCounter);
+ calculatePowerMah(powerModel, measuredChargeUC, activityCounter, false);
// Subtract what the apps used, but clamp to 0.
final double powerMah = Math.max(0, systemPowerMah - total.powerMah);
@@ -165,7 +166,8 @@
final int powerModel = getPowerModel(measuredChargeUC);
final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity();
final long durationMs = calculateDuration(activityCounter);
- final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter);
+ final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter,
+ false);
app.bluetoothRunningTimeMs = durationMs;
app.bluetoothPowerMah = powerMah;
@@ -188,7 +190,7 @@
/** Returns bluetooth power usage based on the best data available. */
private double calculatePowerMah(@BatteryConsumer.PowerModel int powerModel,
- long measuredChargeUC, ControllerActivityCounter counter) {
+ long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower) {
if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
return uCtoMah(measuredChargeUC);
}
@@ -197,12 +199,17 @@
return 0;
}
- final double powerMah =
- counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
- / (double) (1000 * 60 * 60);
+ if (!ignoreReportedPower) {
+ final double powerMah =
+ counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
+ / (double) (1000 * 60 * 60);
+ if (powerMah != 0) {
+ return powerMah;
+ }
+ }
- if (powerMah != 0) {
- return powerMah;
+ if (!mHasBluetoothPowerController) {
+ return 0;
}
final long idleTimeMs =
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index e670178..50df166 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -477,18 +477,17 @@
}
copyToCurTimes();
boolean notify = false;
- boolean valid = true;
for (int i = 0; i < mFreqCount; i++) {
// Unit is 10ms.
mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
if (mDeltaTimes[i] < 0) {
Slog.e(mTag, "Negative delta from freq time for uid: " + uid
+ ", delta: " + mDeltaTimes[i]);
- valid = false;
+ return;
}
notify |= mDeltaTimes[i] > 0;
}
- if (notify && valid) {
+ if (notify) {
System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
if (cb != null) {
cb.onUidCpuTime(uid, mDeltaTimes);
@@ -826,11 +825,11 @@
if (mDeltaTime[i] < 0) {
Slog.e(mTag, "Negative delta from cluster time for uid: " + uid
+ ", delta: " + mDeltaTime[i]);
- valid = false;
+ return;
}
notify |= mDeltaTime[i] > 0;
}
- if (notify && valid) {
+ if (notify) {
System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
if (cb != null) {
cb.onUidCpuTime(uid, mDeltaTime);
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
index d594107..e0ef129 100644
--- a/core/java/com/android/internal/os/WakelockPowerCalculator.java
+++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java
@@ -75,22 +75,29 @@
// this remainder to the OS, if possible.
calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs,
BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs);
+ final double remainingPowerMah = result.powerMah;
if (osBatteryConsumer != null) {
osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
result.durationMs)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah);
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, remainingPowerMah);
}
- final long wakeTimeMillis =
- calculateWakeTimeMillis(batteryStats, rawRealtimeUs, rawUptimeUs);
- final double powerMah = mPowerEstimator.calculatePower(wakeTimeMillis);
+ long wakeTimeMs = calculateWakeTimeMillis(batteryStats, rawRealtimeUs, rawUptimeUs);
+ if (wakeTimeMs < 0) {
+ wakeTimeMs = 0;
+ }
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, wakeTimeMillis)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, powerMah);
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
+ wakeTimeMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
+ appPowerMah + remainingPowerMah);
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, appPowerMah);
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
+ totalAppDurationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
+ appPowerMah);
}
@Override
@@ -167,9 +174,16 @@
}
result.durationMs = osDurationMs + wakeTimeMillis;
result.powerMah = osPowerMah + power;
+ } else {
+ result.durationMs = 0;
+ result.powerMah = 0;
}
}
+ /**
+ * Return on-battery/screen-off time. May be negative if the screen-on time exceeds
+ * the on-battery time.
+ */
private long calculateWakeTimeMillis(BatteryStats batteryStats, long rawRealtimeUs,
long rawUptimeUs) {
final long batteryUptimeUs = batteryStats.getBatteryUptime(rawUptimeUs);
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index a6dc4e0..1a23cc1 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -70,12 +70,12 @@
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.AndroidRuntimeException;
+import android.view.AttachedSurfaceControl;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
-import android.view.ViewRoot;
import android.view.ContextThemeWrapper;
import android.view.CrossWindowBlurListeners;
import android.view.Gravity;
@@ -3931,6 +3931,11 @@
applyDecorFitsSystemWindows();
}
+ @Override
+ public boolean decorFitsSystemWindows() {
+ return mDecorFitsSystemWindows;
+ }
+
private void applyDecorFitsSystemWindows() {
ViewRootImpl impl = getViewRootImplOrNull();
if (impl != null) {
@@ -3984,7 +3989,7 @@
}
@Override
- public ViewRoot getViewRoot() {
+ public AttachedSurfaceControl getRootSurfaceControl() {
return getViewRootImplOrNull();
}
}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index 42fb3f4..72b57ab 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -33,6 +33,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
/**
* Tracks the measured charge consumption of various subsystems according to their
@@ -96,8 +97,10 @@
* supportedStandardBuckets must be of size {@link #NUMBER_STANDARD_POWER_BUCKETS}.
* numCustomBuckets >= 0 is the number of (non-standard) custom power buckets on the device.
*/
- public MeasuredEnergyStats(boolean[] supportedStandardBuckets, String[] customBucketNames) {
- final int numTotalBuckets = NUMBER_STANDARD_POWER_BUCKETS + customBucketNames.length;
+ public MeasuredEnergyStats(@NonNull boolean[] supportedStandardBuckets,
+ @Nullable String[] customBucketNames) {
+ mCustomBucketNames = customBucketNames == null ? new String[0] : customBucketNames;
+ final int numTotalBuckets = NUMBER_STANDARD_POWER_BUCKETS + mCustomBucketNames.length;
mAccumulatedChargeMicroCoulomb = new long[numTotalBuckets];
// Initialize to all zeros where supported, otherwise POWER_DATA_UNAVAILABLE.
// All custom buckets are, by definition, supported, so their values stay at 0.
@@ -106,7 +109,6 @@
mAccumulatedChargeMicroCoulomb[stdBucket] = POWER_DATA_UNAVAILABLE;
}
}
- mCustomBucketNames = customBucketNames;
}
/**
@@ -431,14 +433,22 @@
/** Check if the supported power buckets are precisely those given. */
public boolean isSupportEqualTo(
- @NonNull boolean[] queriedStandardBuckets, String[] customBucketNames) {
+ @NonNull boolean[] queriedStandardBuckets, @Nullable String[] customBucketNames) {
+ if (customBucketNames == null) {
+ //In practice customBucketNames should never be null, but sanitize it just to be sure.
+ customBucketNames = new String[0];
+ }
final int numBuckets = getNumberOfIndices();
- // TODO(b/178504428): Detect whether custom buckets have changed qualitatively, not just
- // quantitatively, and treat as mismatch if so.
- if (numBuckets != NUMBER_STANDARD_POWER_BUCKETS + customBucketNames.length) {
+ final int numCustomBuckets = customBucketNames == null ? 0 : customBucketNames.length;
+ if (numBuckets != NUMBER_STANDARD_POWER_BUCKETS + numCustomBuckets) {
return false;
}
+
+ if (!Arrays.equals(mCustomBucketNames, customBucketNames)) {
+ return false;
+ }
+
for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_POWER_BUCKETS; stdBucket++) {
if (isStandardBucketSupported(stdBucket) != queriedStandardBuckets[stdBucket]) {
return false;
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
deleted file mode 100644
index d2d8220..0000000
--- a/core/java/com/android/internal/util/Preconditions.java
+++ /dev/null
@@ -1,771 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.text.TextUtils;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Objects;
-
-/**
- * Simple static methods to be called at the start of your own methods to verify
- * correct arguments and state.
- */
-public class Preconditions {
-
- /**
- * Ensures that an expression checking an argument is true.
- *
- * @param expression the expression to check
- * @throws IllegalArgumentException if {@code expression} is false
- */
- @UnsupportedAppUsage
- public static void checkArgument(boolean expression) {
- if (!expression) {
- throw new IllegalArgumentException();
- }
- }
-
- /**
- * Ensures that an expression checking an argument is true.
- *
- * @param expression the expression to check
- * @param errorMessage the exception message to use if the check fails; will
- * be converted to a string using {@link String#valueOf(Object)}
- * @throws IllegalArgumentException if {@code expression} is false
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static void checkArgument(boolean expression, final Object errorMessage) {
- if (!expression) {
- throw new IllegalArgumentException(String.valueOf(errorMessage));
- }
- }
-
- /**
- * Ensures that an expression checking an argument is true.
- *
- * @param expression the expression to check
- * @param messageTemplate a printf-style message template to use if the check fails; will
- * be converted to a string using {@link String#format(String, Object...)}
- * @param messageArgs arguments for {@code messageTemplate}
- * @throws IllegalArgumentException if {@code expression} is false
- */
- public static void checkArgument(
- final boolean expression,
- final @NonNull String messageTemplate,
- final Object... messageArgs) {
- if (!expression) {
- throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
- }
- }
-
- /**
- * Ensures that an string reference passed as a parameter to the calling
- * method is not empty.
- *
- * @param string an string reference
- * @return the string reference that was validated
- * @throws IllegalArgumentException if {@code string} is empty
- */
- public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string) {
- if (TextUtils.isEmpty(string)) {
- throw new IllegalArgumentException();
- }
- return string;
- }
-
- /**
- * Ensures that an string reference passed as a parameter to the calling
- * method is not empty.
- *
- * @param string an string reference
- * @param errorMessage the exception message to use if the check fails; will
- * be converted to a string using {@link String#valueOf(Object)}
- * @return the string reference that was validated
- * @throws IllegalArgumentException if {@code string} is empty
- */
- public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string,
- final Object errorMessage) {
- if (TextUtils.isEmpty(string)) {
- throw new IllegalArgumentException(String.valueOf(errorMessage));
- }
- return string;
- }
-
- /**
- * Ensures that an string reference passed as a parameter to the calling method is not empty.
- *
- * @param string an string reference
- * @param messageTemplate a printf-style message template to use if the check fails; will be
- * converted to a string using {@link String#format(String, Object...)}
- * @param messageArgs arguments for {@code messageTemplate}
- * @return the string reference that was validated
- * @throws IllegalArgumentException if {@code string} is empty
- */
- public static @NonNull <T extends CharSequence> T checkStringNotEmpty(
- final T string,
- final @NonNull String messageTemplate,
- final Object... messageArgs) {
- if (TextUtils.isEmpty(string)) {
- throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
- }
- return string;
- }
-
- /**
- * Ensures that an object reference passed as a parameter to the calling
- * method is not null.
- *
- * @param reference an object reference
- * @return the non-null reference that was validated
- * @throws NullPointerException if {@code reference} is null
- * @deprecated - use {@link java.util.Objects.requireNonNull} instead.
- */
- @Deprecated
- @UnsupportedAppUsage
- public static @NonNull <T> T checkNotNull(final T reference) {
- if (reference == null) {
- throw new NullPointerException();
- }
- return reference;
- }
-
- /**
- * Ensures that an object reference passed as a parameter to the calling
- * method is not null.
- *
- * @param reference an object reference
- * @param errorMessage the exception message to use if the check fails; will
- * be converted to a string using {@link String#valueOf(Object)}
- * @return the non-null reference that was validated
- * @throws NullPointerException if {@code reference} is null
- * @deprecated - use {@link java.util.Objects#requireNonNull} instead.
- */
- @Deprecated
- @UnsupportedAppUsage
- public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) {
- if (reference == null) {
- throw new NullPointerException(String.valueOf(errorMessage));
- }
- return reference;
- }
-
- /**
- * Ensures that an object reference passed as a parameter to the calling
- * method is not null.
- *
- * @param messageTemplate a printf-style message template to use if the check fails; will
- * be converted to a string using {@link String#format(String, Object...)}
- * @param messageArgs arguments for {@code messageTemplate}
- * @throws NullPointerException if {@code reference} is null
- */
- public static @NonNull <T> T checkNotNull(
- final T reference,
- final @NonNull String messageTemplate,
- final Object... messageArgs) {
- if (reference == null) {
- throw new NullPointerException(String.format(messageTemplate, messageArgs));
- }
- return reference;
- }
-
- /**
- * Ensures the truth of an expression involving the state of the calling
- * instance, but not involving any parameters to the calling method.
- *
- * @param expression a boolean expression
- * @throws IllegalStateException if {@code expression} is false
- */
- @UnsupportedAppUsage
- public static void checkState(final boolean expression) {
- checkState(expression, null);
- }
-
- /**
- * Ensures the truth of an expression involving the state of the calling
- * instance, but not involving any parameters to the calling method.
- *
- * @param expression a boolean expression
- * @param errorMessage the exception message to use if the check fails; will
- * be converted to a string using {@link String#valueOf(Object)}
- * @throws IllegalStateException if {@code expression} is false
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static void checkState(final boolean expression, String errorMessage) {
- if (!expression) {
- throw new IllegalStateException(errorMessage);
- }
- }
-
- /**
- * Ensures the truth of an expression involving the state of the calling
- * instance, but not involving any parameters to the calling method.
- *
- * @param expression a boolean expression
- * @param messageTemplate a printf-style message template to use if the check fails; will
- * be converted to a string using {@link String#format(String, Object...)}
- * @param messageArgs arguments for {@code messageTemplate}
- * @throws IllegalStateException if {@code expression} is false
- */
- public static void checkState(
- final boolean expression,
- final @NonNull String messageTemplate,
- final Object... messageArgs) {
- if (!expression) {
- throw new IllegalStateException(String.format(messageTemplate, messageArgs));
- }
- }
-
- /**
- * Ensures the truth of an expression involving whether the calling identity is authorized to
- * call the calling method.
- *
- * @param expression a boolean expression
- * @throws SecurityException if {@code expression} is false
- */
- public static void checkCallAuthorization(final boolean expression) {
- if (!expression) {
- throw new SecurityException("Calling identity is not authorized");
- }
- }
-
- /**
- * Ensures the truth of an expression involving whether the calling identity is authorized to
- * call the calling method.
- *
- * @param expression a boolean expression
- * @param message the message of the security exception to be thrown
- * @throws SecurityException if {@code expression} is false
- */
- public static void checkCallAuthorization(final boolean expression, final String message) {
- if (!expression) {
- throw new SecurityException(message);
- }
- }
-
- /**
- * Ensures the truth of an expression involving whether the calling identity is authorized to
- * call the calling method.
- *
- * @param expression a boolean expression
- * @param messageTemplate a printf-style message template to use if the check fails; will
- * be converted to a string using {@link String#format(String, Object...)}
- * @param messageArgs arguments for {@code messageTemplate}
- * @throws SecurityException if {@code expression} is false
- */
- public static void checkCallAuthorization(
- final boolean expression,
- final @NonNull String messageTemplate,
- final Object... messageArgs) {
- if (!expression) {
- throw new SecurityException(String.format(messageTemplate, messageArgs));
- }
- }
-
- /**
- * Ensures the truth of an expression involving whether the calling user is authorized to
- * call the calling method.
- *
- * @param expression a boolean expression
- * @throws SecurityException if {@code expression} is false
- */
- public static void checkCallingUser(final boolean expression) {
- if (!expression) {
- throw new SecurityException("Calling user is not authorized");
- }
- }
-
- /**
- * Check the requested flags, throwing if any requested flags are outside
- * the allowed set.
- *
- * @return the validated requested flags.
- */
- public static int checkFlagsArgument(final int requestedFlags, final int allowedFlags) {
- if ((requestedFlags & allowedFlags) != requestedFlags) {
- throw new IllegalArgumentException("Requested flags 0x"
- + Integer.toHexString(requestedFlags) + ", but only 0x"
- + Integer.toHexString(allowedFlags) + " are allowed");
- }
-
- return requestedFlags;
- }
-
- /**
- * Ensures that that the argument numeric value is non-negative (greater than or equal to 0).
- *
- * @param value a numeric int value
- * @param errorMessage the exception message to use if the check fails
- * @return the validated numeric value
- * @throws IllegalArgumentException if {@code value} was negative
- */
- public static @IntRange(from = 0) int checkArgumentNonnegative(final int value,
- final String errorMessage) {
- if (value < 0) {
- throw new IllegalArgumentException(errorMessage);
- }
-
- return value;
- }
-
- /**
- * Ensures that that the argument numeric value is non-negative (greater than or equal to 0).
- *
- * @param value a numeric int value
- *
- * @return the validated numeric value
- * @throws IllegalArgumentException if {@code value} was negative
- */
- public static @IntRange(from = 0) int checkArgumentNonnegative(final int value) {
- if (value < 0) {
- throw new IllegalArgumentException();
- }
-
- return value;
- }
-
- /**
- * Ensures that that the argument numeric value is non-negative (greater than or equal to 0).
- *
- * @param value a numeric long value
- * @return the validated numeric value
- * @throws IllegalArgumentException if {@code value} was negative
- */
- public static long checkArgumentNonnegative(final long value) {
- if (value < 0) {
- throw new IllegalArgumentException();
- }
-
- return value;
- }
-
- /**
- * Ensures that that the argument numeric value is non-negative (greater than or equal to 0).
- *
- * @param value a numeric long value
- * @param errorMessage the exception message to use if the check fails
- * @return the validated numeric value
- * @throws IllegalArgumentException if {@code value} was negative
- */
- public static long checkArgumentNonnegative(final long value, final String errorMessage) {
- if (value < 0) {
- throw new IllegalArgumentException(errorMessage);
- }
-
- return value;
- }
-
- /**
- * Ensures that that the argument numeric value is positive (greater than 0).
- *
- * @param value a numeric int value
- * @param errorMessage the exception message to use if the check fails
- * @return the validated numeric value
- * @throws IllegalArgumentException if {@code value} was not positive
- */
- public static int checkArgumentPositive(final int value, final String errorMessage) {
- if (value <= 0) {
- throw new IllegalArgumentException(errorMessage);
- }
-
- return value;
- }
-
- /**
- * Ensures that the argument floating point value is non-negative (greater than or equal to 0).
- * @param value a floating point value
- * @param errorMessage the exteption message to use if the check fails
- * @return the validated numeric value
- * @throws IllegalArgumentException if {@code value} was negative
- */
- public static float checkArgumentNonNegative(final float value, final String errorMessage) {
- if (value < 0) {
- throw new IllegalArgumentException(errorMessage);
- }
-
- return value;
- }
-
- /**
- * Ensures that the argument floating point value is positive (greater than 0).
- * @param value a floating point value
- * @param errorMessage the exteption message to use if the check fails
- * @return the validated numeric value
- * @throws IllegalArgumentException if {@code value} was not positive
- */
- public static float checkArgumentPositive(final float value, final String errorMessage) {
- if (value <= 0) {
- throw new IllegalArgumentException(errorMessage);
- }
-
- return value;
- }
-
- /**
- * Ensures that the argument floating point value is a finite number.
- *
- * <p>A finite number is defined to be both representable (that is, not NaN) and
- * not infinite (that is neither positive or negative infinity).</p>
- *
- * @param value a floating point value
- * @param valueName the name of the argument to use if the check fails
- *
- * @return the validated floating point value
- *
- * @throws IllegalArgumentException if {@code value} was not finite
- */
- public static float checkArgumentFinite(final float value, final String valueName) {
- if (Float.isNaN(value)) {
- throw new IllegalArgumentException(valueName + " must not be NaN");
- } else if (Float.isInfinite(value)) {
- throw new IllegalArgumentException(valueName + " must not be infinite");
- }
-
- return value;
- }
-
- /**
- * Ensures that the argument floating point value is within the inclusive range.
- *
- * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
- * will always be out of range.</p>
- *
- * @param value a floating point value
- * @param lower the lower endpoint of the inclusive range
- * @param upper the upper endpoint of the inclusive range
- * @param valueName the name of the argument to use if the check fails
- *
- * @return the validated floating point value
- *
- * @throws IllegalArgumentException if {@code value} was not within the range
- */
- public static float checkArgumentInRange(float value, float lower, float upper,
- String valueName) {
- if (Float.isNaN(value)) {
- throw new IllegalArgumentException(valueName + " must not be NaN");
- } else if (value < lower) {
- throw new IllegalArgumentException(
- String.format(
- "%s is out of range of [%f, %f] (too low)", valueName, lower, upper));
- } else if (value > upper) {
- throw new IllegalArgumentException(
- String.format(
- "%s is out of range of [%f, %f] (too high)", valueName, lower, upper));
- }
-
- return value;
- }
-
- /**
- * Ensures that the argument floating point value is within the inclusive range.
- *
- * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
- * will always be out of range.</p>
- *
- * @param value a floating point value
- * @param lower the lower endpoint of the inclusive range
- * @param upper the upper endpoint of the inclusive range
- * @param valueName the name of the argument to use if the check fails
- *
- * @return the validated floating point value
- *
- * @throws IllegalArgumentException if {@code value} was not within the range
- */
- public static double checkArgumentInRange(double value, double lower, double upper,
- String valueName) {
- if (Double.isNaN(value)) {
- throw new IllegalArgumentException(valueName + " must not be NaN");
- } else if (value < lower) {
- throw new IllegalArgumentException(
- String.format(
- "%s is out of range of [%f, %f] (too low)", valueName, lower, upper));
- } else if (value > upper) {
- throw new IllegalArgumentException(
- String.format(
- "%s is out of range of [%f, %f] (too high)", valueName, lower, upper));
- }
-
- return value;
- }
-
- /**
- * Ensures that the argument int value is within the inclusive range.
- *
- * @param value a int value
- * @param lower the lower endpoint of the inclusive range
- * @param upper the upper endpoint of the inclusive range
- * @param valueName the name of the argument to use if the check fails
- *
- * @return the validated int value
- *
- * @throws IllegalArgumentException if {@code value} was not within the range
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static int checkArgumentInRange(int value, int lower, int upper,
- String valueName) {
- if (value < lower) {
- throw new IllegalArgumentException(
- String.format(
- "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
- } else if (value > upper) {
- throw new IllegalArgumentException(
- String.format(
- "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
- }
-
- return value;
- }
-
- /**
- * Ensures that the argument long value is within the inclusive range.
- *
- * @param value a long value
- * @param lower the lower endpoint of the inclusive range
- * @param upper the upper endpoint of the inclusive range
- * @param valueName the name of the argument to use if the check fails
- *
- * @return the validated long value
- *
- * @throws IllegalArgumentException if {@code value} was not within the range
- */
- public static long checkArgumentInRange(long value, long lower, long upper,
- String valueName) {
- if (value < lower) {
- throw new IllegalArgumentException(
- String.format(
- "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
- } else if (value > upper) {
- throw new IllegalArgumentException(
- String.format(
- "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
- }
-
- return value;
- }
-
- /**
- * Ensures that the array is not {@code null}, and none of its elements are {@code null}.
- *
- * @param value an array of boxed objects
- * @param valueName the name of the argument to use if the check fails
- *
- * @return the validated array
- *
- * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
- */
- public static <T> T[] checkArrayElementsNotNull(final T[] value, final String valueName) {
- if (value == null) {
- throw new NullPointerException(valueName + " must not be null");
- }
-
- for (int i = 0; i < value.length; ++i) {
- if (value[i] == null) {
- throw new NullPointerException(
- String.format("%s[%d] must not be null", valueName, i));
- }
- }
-
- return value;
- }
-
- /**
- * Ensures that the {@link Collection} is not {@code null}, and none of its elements are
- * {@code null}.
- *
- * @param value a {@link Collection} of boxed objects
- * @param valueName the name of the argument to use if the check fails
- *
- * @return the validated {@link Collection}
- *
- * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
- */
- public static @NonNull <C extends Collection<T>, T> C checkCollectionElementsNotNull(
- final C value, final String valueName) {
- if (value == null) {
- throw new NullPointerException(valueName + " must not be null");
- }
-
- long ctr = 0;
- for (T elem : value) {
- if (elem == null) {
- throw new NullPointerException(
- String.format("%s[%d] must not be null", valueName, ctr));
- }
- ++ctr;
- }
-
- return value;
- }
-
- /**
- * Ensures that the {@link Collection} is not {@code null}, and contains at least one element.
- *
- * @param value a {@link Collection} of boxed elements.
- * @param valueName the name of the argument to use if the check fails.
-
- * @return the validated {@link Collection}
- *
- * @throws NullPointerException if the {@code value} was {@code null}
- * @throws IllegalArgumentException if the {@code value} was empty
- */
- public static <T> Collection<T> checkCollectionNotEmpty(final Collection<T> value,
- final String valueName) {
- if (value == null) {
- throw new NullPointerException(valueName + " must not be null");
- }
- if (value.isEmpty()) {
- throw new IllegalArgumentException(valueName + " is empty");
- }
- return value;
- }
-
- /**
- * Ensures that the given byte array is not {@code null}, and contains at least one element.
- *
- * @param value an array of elements.
- * @param valueName the name of the argument to use if the check fails.
-
- * @return the validated array
- *
- * @throws NullPointerException if the {@code value} was {@code null}
- * @throws IllegalArgumentException if the {@code value} was empty
- */
- @NonNull
- public static byte[] checkByteArrayNotEmpty(final byte[] value, final String valueName) {
- if (value == null) {
- throw new NullPointerException(valueName + " must not be null");
- }
- if (value.length == 0) {
- throw new IllegalArgumentException(valueName + " is empty");
- }
- return value;
- }
-
- /**
- * Ensures that argument {@code value} is one of {@code supportedValues}.
- *
- * @param supportedValues an array of string values
- * @param value a string value
- *
- * @return the validated value
- *
- * @throws NullPointerException if either {@code value} or {@code supportedValues} is null
- * @throws IllegalArgumentException if the {@code value} is not in {@code supportedValues}
- */
- @NonNull
- public static String checkArgumentIsSupported(final String[] supportedValues,
- final String value) {
- checkNotNull(value);
- checkNotNull(supportedValues);
-
- if (!contains(supportedValues, value)) {
- throw new IllegalArgumentException(value + "is not supported "
- + Arrays.toString(supportedValues));
- }
- return value;
- }
-
- private static boolean contains(String[] values, String value) {
- if (values == null) {
- return false;
- }
- for (int i = 0; i < values.length; ++i) {
- if (Objects.equals(value, values[i])) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Ensures that all elements in the argument floating point array are within the inclusive range
- *
- * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
- * will always be out of range.</p>
- *
- * @param value a floating point array of values
- * @param lower the lower endpoint of the inclusive range
- * @param upper the upper endpoint of the inclusive range
- * @param valueName the name of the argument to use if the check fails
- *
- * @return the validated floating point value
- *
- * @throws IllegalArgumentException if any of the elements in {@code value} were out of range
- * @throws NullPointerException if the {@code value} was {@code null}
- */
- public static float[] checkArrayElementsInRange(float[] value, float lower, float upper,
- String valueName) {
- checkNotNull(value, "%s must not be null", valueName);
-
- for (int i = 0; i < value.length; ++i) {
- float v = value[i];
-
- if (Float.isNaN(v)) {
- throw new IllegalArgumentException(valueName + "[" + i + "] must not be NaN");
- } else if (v < lower) {
- throw new IllegalArgumentException(
- String.format("%s[%d] is out of range of [%f, %f] (too low)",
- valueName, i, lower, upper));
- } else if (v > upper) {
- throw new IllegalArgumentException(
- String.format("%s[%d] is out of range of [%f, %f] (too high)",
- valueName, i, lower, upper));
- }
- }
-
- return value;
- }
-
- /**
- * Ensures that all elements in the argument integer array are within the inclusive range
- *
- * @param value an integer array of values
- * @param lower the lower endpoint of the inclusive range
- * @param upper the upper endpoint of the inclusive range
- * @param valueName the name of the argument to use if the check fails
- *
- * @return the validated integer array
- *
- * @throws IllegalArgumentException if any of the elements in {@code value} were out of range
- * @throws NullPointerException if the {@code value} was {@code null}
- */
- public static int[] checkArrayElementsInRange(int[] value, int lower, int upper,
- String valueName) {
- checkNotNull(value, "%s must not be null", valueName);
-
- for (int i = 0; i < value.length; ++i) {
- int v = value[i];
-
- if (v < lower) {
- throw new IllegalArgumentException(
- String.format("%s[%d] is out of range of [%d, %d] (too low)",
- valueName, i, lower, upper));
- } else if (v > upper) {
- throw new IllegalArgumentException(
- String.format("%s[%d] is out of range of [%d, %d] (too high)",
- valueName, i, lower, upper));
- }
- }
-
- return value;
- }
-}
diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
index 058a921..4460e4a 100644
--- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
+++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
@@ -27,9 +27,7 @@
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.RemotableViewMethod;
-import android.view.ViewGroup;
import android.widget.Button;
-import android.widget.LinearLayout;
import android.widget.RemoteViews;
import com.android.internal.R;
@@ -42,8 +40,6 @@
@RemoteViews.RemoteView
public class EmphasizedNotificationButton extends Button {
private final RippleDrawable mRipple;
- private final int mStrokeWidth;
- private final int mStrokeColor;
private boolean mPriority;
public EmphasizedNotificationButton(Context context) {
@@ -63,9 +59,6 @@
super(context, attrs, defStyleAttr, defStyleRes);
DrawableWrapper background = (DrawableWrapper) getBackground().mutate();
mRipple = (RippleDrawable) background.getDrawable();
- mStrokeWidth = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.emphasized_button_stroke_width);
- mStrokeColor = getContext().getColor(com.android.internal.R.color.material_grey_300);
mRipple.mutate();
}
@@ -82,13 +75,6 @@
invalidate();
}
- @RemotableViewMethod
- public void setHasStroke(boolean hasStroke) {
- GradientDrawable inner = (GradientDrawable) mRipple.getDrawable(0);
- inner.setStroke(hasStroke ? mStrokeWidth : 0, mStrokeColor);
- invalidate();
- }
-
/**
* Sets an image icon which will have its size constrained and will be set to the same color as
* the text. Must be called after {@link #setTextColor(int)} for the latter to work.
@@ -121,18 +107,13 @@
}
/**
- * Changes the LayoutParams.width to WRAP_CONTENT, with the argument representing if this view
- * is a priority over its peers (which affects weight).
+ * Sets whether this view is a priority over its peers (which affects width).
+ * Specifically, this is used by {@link NotificationActionListLayout} to give this view width
+ * priority ahead of user-defined buttons when allocating horizontal space.
*/
@RemotableViewMethod
- public void setWrapModePriority(boolean priority) {
+ public void setIsPriority(boolean priority) {
mPriority = priority;
- ViewGroup.LayoutParams layoutParams = getLayoutParams();
- layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
- if (layoutParams instanceof LinearLayout.LayoutParams) {
- ((LinearLayout.LayoutParams) layoutParams).weight = 0;
- }
- setLayoutParams(layoutParams);
}
/**
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 8e6497b..a4d6a60 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -17,11 +17,11 @@
package com.android.internal.widget;
import android.annotation.DimenRes;
+import android.app.Notification;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
-import android.util.Pair;
import android.view.Gravity;
import android.view.RemotableViewMethod;
import android.view.View;
@@ -43,10 +43,9 @@
private final int mGravity;
private int mTotalWidth = 0;
private int mExtraStartPadding = 0;
- private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>();
+ private ArrayList<TextViewInfo> mMeasureOrderTextViews = new ArrayList<>();
private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
private boolean mEmphasizedMode;
- private boolean mPrioritizedWrapMode;
private int mDefaultPaddingBottom;
private int mDefaultPaddingTop;
private int mEmphasizedHeight;
@@ -70,16 +69,18 @@
ta.recycle();
}
+ private static boolean isPriority(View actionView) {
+ return actionView instanceof EmphasizedNotificationButton
+ && ((EmphasizedNotificationButton) actionView).isPriority();
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mEmphasizedMode && !mPrioritizedWrapMode) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- return;
- }
final int N = getChildCount();
int textViews = 0;
int otherViews = 0;
int notGoneChildren = 0;
+ int priorityChildren = 0;
for (int i = 0; i < N; i++) {
View c = getChildAt(i);
@@ -90,6 +91,9 @@
}
if (c.getVisibility() != GONE) {
notGoneChildren++;
+ if (isPriority(c)) {
+ priorityChildren++;
+ }
}
}
@@ -103,9 +107,9 @@
if (!needRebuild) {
final int size = mMeasureOrderTextViews.size();
for (int i = 0; i < size; i++) {
- Pair<Integer, TextView> pair = mMeasureOrderTextViews.get(i);
- if (pair.first != pair.second.getText().length()) {
+ if (mMeasureOrderTextViews.get(i).needsRebuild()) {
needRebuild = true;
+ break;
}
}
}
@@ -122,14 +126,19 @@
int usedWidth = 0;
int measuredChildren = 0;
+ int measuredPriorityChildren = 0;
for (int i = 0; i < N; i++) {
// Measure shortest children first. To avoid measuring twice, we approximate by looking
// at the text length.
- View c;
+ final boolean isPriority;
+ final View c;
if (i < otherSize) {
c = mMeasureOrderOther.get(i);
+ isPriority = false;
} else {
- c = mMeasureOrderTextViews.get(i - otherSize).second;
+ TextViewInfo info = mMeasureOrderTextViews.get(i - otherSize);
+ c = info.mTextView;
+ isPriority = info.mIsPriority;
}
if (c.getVisibility() == GONE) {
continue;
@@ -143,7 +152,18 @@
// measure in the order of (approx.) size, a large view can still take more than its
// share if the others are small.
int availableWidth = innerWidth - usedWidth;
- int maxWidthForChild = availableWidth / (notGoneChildren - measuredChildren);
+ int unmeasuredChildren = notGoneChildren - measuredChildren;
+ int maxWidthForChild = availableWidth / unmeasuredChildren;
+ if (isPriority) {
+ // Priority children get a larger maximum share of the total space:
+ // maximum priority share = (nPriority + 1) / (MAX + 1)
+ int unmeasuredPriorityChildren = priorityChildren - measuredPriorityChildren;
+ int unmeasuredOtherChildren = unmeasuredChildren - unmeasuredPriorityChildren;
+ int widthReservedForOtherChildren = innerWidth * unmeasuredOtherChildren
+ / (Notification.MAX_ACTION_BUTTONS + 1);
+ int widthAvailableForPriority = availableWidth - widthReservedForOtherChildren;
+ maxWidthForChild = widthAvailableForPriority / unmeasuredPriorityChildren;
+ }
usedWidthForChild = innerWidth - maxWidthForChild;
}
@@ -153,6 +173,9 @@
usedWidth += c.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
measuredChildren++;
+ if (isPriority) {
+ measuredPriorityChildren++;
+ }
}
int collapsibleIndent = mCollapsibleIndentDimen == 0 ? 0
@@ -175,13 +198,8 @@
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View c = getChildAt(i);
- if (c instanceof EmphasizedNotificationButton
- && ((EmphasizedNotificationButton) c).isPriority()) {
- // add with 0 length to ensure that this view is measured before others.
- mMeasureOrderTextViews.add(Pair.create(0, (TextView) c));
- } else if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
- mMeasureOrderTextViews.add(Pair.create(((TextView) c).getText().length(),
- (TextView)c));
+ if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
+ mMeasureOrderTextViews.add(new TextViewInfo((TextView) c));
} else {
mMeasureOrderOther.add(c);
}
@@ -213,10 +231,6 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (mEmphasizedMode && !mPrioritizedWrapMode) {
- super.onLayout(changed, left, top, right, bottom);
- return;
- }
final boolean isLayoutRtl = isLayoutRtl();
final int paddingTop = mPaddingTop;
final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
@@ -293,16 +307,6 @@
}
/**
- * When used with emphasizedMode, changes the button sizing behavior to prioritize certain
- * buttons (which are system generated) to not scrunch, and leave the remaining space for
- * custom actions.
- */
- @RemotableViewMethod
- public void setPrioritizedWrapMode(boolean prioritizedWrapMode) {
- mPrioritizedWrapMode = prioritizedWrapMode;
- }
-
- /**
* When buttons are in wrap mode, this is a padding that will be applied at the start of the
* layout of the actions, but only when those actions would fit with the entire padding
* visible. Otherwise, this padding will be omitted entirely.
@@ -353,6 +357,28 @@
return 0;
}
- public static final Comparator<Pair<Integer, TextView>> MEASURE_ORDER_COMPARATOR
- = (a, b) -> a.first.compareTo(b.first);
+ public static final Comparator<TextViewInfo> MEASURE_ORDER_COMPARATOR = (a, b) -> {
+ int priorityComparison = -Boolean.compare(a.mIsPriority, b.mIsPriority);
+ return priorityComparison != 0
+ ? priorityComparison
+ : Integer.compare(a.mTextLength, b.mTextLength);
+ };
+
+ private static final class TextViewInfo {
+ final boolean mIsPriority;
+ final int mTextLength;
+ final TextView mTextView;
+
+ TextViewInfo(TextView textView) {
+ this.mIsPriority = isPriority(textView);
+ this.mTextLength = textView.getText().length();
+ this.mTextView = textView;
+ }
+
+ boolean needsRebuild() {
+ return mTextView.getText().length() != mTextLength
+ || isPriority(mTextView) != mIsPriority;
+ }
+ }
+
}
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 315ca2e..5c9999d 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -270,7 +270,28 @@
metadataSrc->unlock(metaBuffer);
return;
}
- metadataDst->update(entry.tag, entry.data.u8, entry.count);
+ switch (entry.type) {
+ case TYPE_BYTE:
+ metadataDst->update(entry.tag, entry.data.u8, entry.count);
+ break;
+ case TYPE_INT32:
+ metadataDst->update(entry.tag, entry.data.i32, entry.count);
+ break;
+ case TYPE_FLOAT:
+ metadataDst->update(entry.tag, entry.data.f, entry.count);
+ break;
+ case TYPE_INT64:
+ metadataDst->update(entry.tag, entry.data.i64, entry.count);
+ break;
+ case TYPE_DOUBLE:
+ metadataDst->update(entry.tag, entry.data.d, entry.count);
+ break;
+ case TYPE_RATIONAL:
+ metadataDst->update(entry.tag, entry.data.r, entry.count);
+ break;
+ default:
+ ALOGE("%s: Unsupported tag type: %d!", __func__, entry.type);
+ }
}
metadataSrc->unlock(metaBuffer);
}
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 44597cc..a699f91 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -241,13 +241,13 @@
}
// Some other error. Give up
- ALOGW("Failed to send outbound event on channel '%s'. status=%d",
- getInputChannelName().c_str(), status);
+ ALOGW("Failed to send outbound event on channel '%s'. status=%s(%d)",
+ getInputChannelName().c_str(), statusToString(status).c_str(), status);
if (status != DEAD_OBJECT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
std::string message =
- android::base::StringPrintf("Failed to send outbound event. status=%d",
- status);
+ android::base::StringPrintf("Failed to send outbound event. status=%s(%d)",
+ statusToString(status).c_str(), status);
jniThrowRuntimeException(env, message.c_str());
mMessageQueue->raiseAndClearException(env, "finishInputEvent");
}
@@ -319,8 +319,8 @@
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
if (status != OK && status != WOULD_BLOCK) {
- ALOGE("channel '%s' ~ Failed to consume input event. status=%d",
- getInputChannelName().c_str(), status);
+ ALOGE("channel '%s' ~ Failed to consume input event. status=%s(%d)",
+ getInputChannelName().c_str(), statusToString(status).c_str(), status);
return status;
}
@@ -502,9 +502,9 @@
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
if (status) {
- std::string message =
- android::base::StringPrintf("Failed to initialize input event receiver. status=%d",
- status);
+ std::string message = android::base::
+ StringPrintf("Failed to initialize input event receiver. status=%s(%d)",
+ statusToString(status).c_str(), status);
jniThrowRuntimeException(env, message.c_str());
return 0;
}
@@ -531,7 +531,7 @@
if (status != DEAD_OBJECT) {
std::string message =
android::base::StringPrintf("Failed to finish input event. status=%s(%d)",
- strerror(-status), status);
+ statusToString(status).c_str(), status);
jniThrowRuntimeException(env, message.c_str());
}
}
@@ -564,8 +564,8 @@
&consumedBatch);
if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
std::string message =
- android::base::StringPrintf("Failed to consume batched input event. status=%d",
- status);
+ android::base::StringPrintf("Failed to consume batched input event. status=%s(%d)",
+ statusToString(status).c_str(), status);
jniThrowRuntimeException(env, message.c_str());
return JNI_FALSE;
}
diff --git a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
index 980e12d..83e2f2b 100644
--- a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
+++ b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
@@ -31,7 +31,7 @@
}
static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) {
- return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd));
+ return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFd(env, javaFd));
}
static const JNINativeMethod gNetworkUtilMethods[] = {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 8df113d..4a1a272 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -826,7 +826,7 @@
PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
- bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, true);
+ bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false);
if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 24fef48..5fe96ed 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -402,7 +402,7 @@
socklen_t cred_size = sizeof credentials;
if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1
|| cred_size != sizeof credentials) {
- fail_fn_1("ForkMany failed to get initial credentials, %s", strerror(errno));
+ fail_fn_1(CREATE_ERROR("ForkMany failed to get initial credentials, %s", strerror(errno)));
}
bool first_time = true;
@@ -453,7 +453,7 @@
close(session_socket);
int new_fd = accept(zygote_socket_fd, nullptr, nullptr);
if (new_fd == -1) {
- fail_fn_z("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno));
+ fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno)));
}
if (new_fd != session_socket) {
// Move new_fd back to the old value, so that we don't have to change Java-level data
diff --git a/core/proto/android/server/biometrics.proto b/core/proto/android/server/biometrics.proto
index c918dbb..ac9e3e0 100644
--- a/core/proto/android/server/biometrics.proto
+++ b/core/proto/android/server/biometrics.proto
@@ -113,11 +113,16 @@
IRIS = 3;
}
+ enum ModalityFlag {
+ FINGERPRINT_UDFPS = 0;
+ }
+
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
// Unique sensorId
optional int32 sensor_id = 1;
+ // The type of the sensor.
optional Modality modality = 2;
// The current strength (see {@link BiometricManager.Authenticators}) of this sensor, taking any
@@ -137,6 +142,9 @@
// True if a HAT is required (field above) AND a challenge needs to be generated by the
// biometric TEE (or equivalent), and wrapped within the HAT.
optional bool reset_lockout_requires_challenge = 7;
+
+ // Detailed information about the sensor's modality, if available.
+ repeated ModalityFlag modality_flags = 8;
}
// State of a specific user for a specific sensor.
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index b157146..fa1e9d4 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -221,6 +221,9 @@
optional string name = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
repeated DisplayAreaChildProto children = 3 [deprecated=true];
optional bool is_task_display_area = 4;
+ optional bool is_root_display_area = 5;
+ optional int32 feature_id = 6;
+ optional bool is_organized = 7;
}
/* represents a generic child of a DisplayArea */
diff --git a/core/res/Android.bp b/core/res/Android.bp
index b988b5a..6063062 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -100,8 +100,7 @@
+ "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && "
+ "mkdir -p $$RES_DIR/values && "
+ "cp $${INPUTS[1]} $$RES_DIR/values && "
- + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR && "
- + "cp $(out) ."
+ + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR"
}
android_app {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ffb8d1d..3d892b5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -593,8 +593,6 @@
<protected-broadcast android:name="android.intent.action.DYNAMIC_SENSOR_CHANGED" />
- <protected-broadcast android:name="android.intent.action.ACTION_RADIO_OFF" />
-
<protected-broadcast android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
<protected-broadcast android:name="android.accounts.action.ACCOUNT_REMOVED" />
<protected-broadcast android:name="android.accounts.action.VISIBLE_ACCOUNTS_CHANGED" />
@@ -5768,6 +5766,21 @@
android:protectionLevel="normal" />
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"/>
+ <!-- Allows an application to take screenshots of layers that normally would be blacked out when
+ a screenshot is taken. Specifically, layers that have the flag
+ {@link android.view.SurfaceControl#SECURE} will be screenshot if the caller requests to
+ capture secure layers. Normally those layers will be rendered black.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to query over global data in AppSearch.
+ @hide -->
+ <permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA"
+ android:protectionLevel="internal|role" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 9d739b9..7a8da36 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -1,6 +1,7 @@
adamp@google.com
alanv@google.com
asc@google.com
+cinek@google.com
dsandler@android.com
dsandler@google.com
dupin@google.com
@@ -8,6 +9,7 @@
hackbod@google.com
ilyamaty@google.com
jaggies@google.com
+jdemeulenaere@google.com
jsharkey@android.com
jsharkey@google.com
juliacr@google.com
@@ -17,7 +19,9 @@
narayan@google.com
ogunwale@google.com
patb@google.com
+shanh@google.com
svetoslavganov@android.com
svetoslavganov@google.com
toddke@google.com
+tsuji@google.com
yamasani@google.com
diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml
index ad68054..63707ab 100644
--- a/core/res/res/drawable/btn_notification_emphasized.xml
+++ b/core/res/res/drawable/btn_notification_emphasized.xml
@@ -24,13 +24,11 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="@dimen/notification_action_button_radius" />
- <padding android:left="@dimen/button_padding_horizontal_material"
+ <padding android:left="12dp"
android:top="@dimen/button_padding_vertical_material"
- android:right="@dimen/button_padding_horizontal_material"
+ android:right="12dp"
android:bottom="@dimen/button_padding_vertical_material" />
<solid android:color="@color/white" />
- <stroke android:width="@dimen/emphasized_button_stroke_width"
- android:color="@color/material_grey_300"/>
</shape>
</item>
</ripple>
diff --git a/core/res/res/drawable/work_widget_mask_view_background.xml b/core/res/res/drawable/work_widget_mask_view_background.xml
new file mode 100644
index 0000000..ce9c514
--- /dev/null
+++ b/core/res/res/drawable/work_widget_mask_view_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="?android:attr/colorSurfaceVariant" />
+ <corners android:radius="@dimen/system_app_widget_background_radius" />
+</shape>
diff --git a/core/res/res/layout/notification_material_action_emphasized.xml b/core/res/res/layout/notification_material_action_emphasized.xml
index cd1f1ab..ea84185 100644
--- a/core/res/res/layout/notification_material_action_emphasized.xml
+++ b/core/res/res/layout/notification_material_action_emphasized.xml
@@ -18,10 +18,9 @@
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/NotificationEmphasizedAction"
android:id="@+id/action0"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="12dp"
- android:layout_weight="1"
android:drawablePadding="6dp"
android:gravity="center"
android:textColor="@color/notification_default_color"
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index e7174cc..86c5d13 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -18,14 +18,14 @@
android:id="@+id/work_widget_mask_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?android:attr/colorSurfaceVariant"
+ android:background="@drawable/work_widget_mask_view_background"
android:importantForAccessibility="noHideDescendants"
android:clickable="true">
<com.android.internal.widget.DisableImageView
android:id="@+id/work_widget_app_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
android:layout_gravity="center"
android:clickable="false" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 73adeaa..c9fa462 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Vingerafdrukikoon"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"bestuur gesigslothardeware"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"bestuur Gesigslothardeware"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Laat program toe om metodes te benut om gesigtemplate vir gebruik by te voeg en uit te vee."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"gebruik gesigslothardeware"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Laat die program toe om gesigslothardeware vir stawing te gebruik"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"gebruik Gesigslothardeware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Laat die program toe om Gesigslothardeware vir stawing te gebruik"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Gesigslot"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Skryf jou gesig weer in"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Skryf asseblief jou gesig weer in om herkenning te verbeter"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Stel gesigslot op"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Stel Gesigslot op"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ontsluit jou foon deur daarna te kyk"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer maniere op om te ontsluit"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tik om \'n vingerafdruk by te voeg"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Kan nie gesig verifieer nie. Hardeware nie beskikbaar nie."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Probeer gesigslot weer."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Probeer Gesigslot weer."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Kan nie nuwe gesigdata berg nie. Vee eers \'n ou een uit."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Gesighandeling is gekanselleer."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Gebruiker het gesigslot gekanselleer."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Gebruiker het Gesigslot gekanselleer."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Te veel pogings. Probeer later weer."</string>
<string name="face_error_lockout_permanent" msgid="8277853602168960343">"Te veel pogings. Gesigslot is gedeaktiveer."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Kan nie gesig verifieer nie. Probeer weer."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Jy het nie gesigslot opgestel nie."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Jy het nie Gesigslot opgestel nie."</string>
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Gesigslot word nie op hierdie toestel gesteun nie."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor is tydelik gedeaktiveer."</string>
<string name="face_name_template" msgid="3877037340223318119">"Gesig <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Gebruik gesigslot"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gebruik gesig- of skermslot"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Gebruik Gesigslot"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gebruik Gesigslot of Skermslot"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Gebruik jou gesig om voort te gaan"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Gebruik jou gesig of skermslot om voort te gaan"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probeer weer"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Probeer weer"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Ontsluit vir alle kenmerke en data"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maksimum gesigontsluit-pogings oorskry"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maksimum Gesigslot-pogings oorskry"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Geen SIM-kaart nie"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Geen SIM-kaart in tablet nie."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Geen SIM-kaart in jou Android TV-toestel nie."</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gebruik kortpad"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Kleuromkering"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurkorreksie"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Eenhandmodus"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra donker"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is afgeskakel"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index f8ff934..4940b80 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"እንደገና ሞክር"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"እንደገና ሞክር"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"ለሁሉም ባህሪያት እና ውሂብ ያስከፍቱ"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"የመጨረሻውን የገጽ ክፈት ሙከራዎችን አልፏል"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"የመጨረሻውን በመልክ መክፈት ሙከራዎችን አልፏል"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"ምንም ሲም ካርድ የለም"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"በጡባዊ ውስጥ ምንም SIM ካርድ የለም።"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"በእርስዎ Android TV መሣሪያ ላይ ምንም ሲም ካርድ የለም።"</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"የመክፈቻ አካባቢውን አስፋፋ።"</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"በማንሸራተት ክፈት።"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"በስርዓተ-ጥለት መክፈት።"</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"በፊት መክፈት።"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"በመልክ መክፈት።"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"በፒን መክፈት።"</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"የሲም ፒን ክፈት።"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"የሲም PUK ክፈት።"</string>
@@ -1869,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"በእርስዎ አስተዳዳሪ ተዘምኗል"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"በእርስዎ አስተዳዳሪ ተሰርዟል"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"እሺ"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"የባትሪ ኃይል ቆጣቢ ጠቆር ያለ ገጽታን ያበራል እና የጀርባ እንቅስቃሴን፣ አንዳንድ ምስላዊ ተጽዕኖዎችን፣ የተወሰኑ ባህሪያትን እና አንዳንድ አውታረ መረብ ግንኙነቶችን ይገድባል ወይም ያጠፋል።\n\n"<annotation id="url">"የበለጠ ለመረዳት"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"ባትሪ ቆጣቢ ጠቆር ያለ ገጽታን ያበራል እና የጀርባ እንቅስቃሴን፣ አንዳንድ ዕይታዊ ውጤቶችን፣ አንዳንድ ባህሪዎችን፣ እና አንዳንድ የአውታረ መረብ ግንኙነቶችን ይገድባል ወይም ያጠፋል።"</string>
<string name="data_saver_description" msgid="4995164271550590517">"የውሂብ አጠቃቀም እንዲቀንስ ለማገዝ ውሂብ ቆጣቢ አንዳንድ መተግበሪያዎች ከበስተጀርባ ሆነው ውሂብ እንዳይልኩ ወይም እንዳይቀበሉ ይከለክላቸዋል። በአሁኑ ጊዜ እየተጠቀሙበት ያለ መተግበሪያ ውሂብ ሊደርስ ይችላል፣ ነገር ግን ባነሰ ተደጋጋሚነት ሊሆን ይችላል። ይሄ ማለት ለምሳሌ ምስሎችን መታ እስኪያደርጓቸው ድረስ ላይታዩ ይችላሉ ማለት ነው።"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ውሂብ ቆጣቢ ይጥፋ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"አብራ"</string>
@@ -1975,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> አሁን ላይ አይገኝም። በ<xliff:g id="APP_NAME_1">%2$s</xliff:g> የሚተዳደር ነው።"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"የበለጠ ለመረዳት"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"መተግበሪያን ላፍታ እንዳይቆም አድርግ"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"የሥራ መተግበሪያዎች ይብሩ?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"የእርስዎን የሥራ መተግበሪያዎች እና ማሳወቂያዎች መዳረሻ ያግኙ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"አብራ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"መተግበሪያ አይገኝም"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> አሁን አይገኝም።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index c2be67c..6d44d15 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -621,14 +621,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"رمز بصمة الإصبع"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"إدارة أجهزة \"فتح القفل بالوجه\""</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"إدارة أجهزة ميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"السماح للتطبيق باستدعاء طرق لإضافة نماذج من الوجوه وحذفها"</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"استخدام أجهزة \"فتح القفل بالوجه\""</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"السماح للتطبيق باستخدام أجهزة \"فتح القفل بالوجه\" لإجراء المصادقة"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"فتح القفل بالوجه"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"استخدام أجهزة ميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"السماح للتطبيق باستخدام أجهزة ميزة \"فتح الجهاز بالتعرف على الوجه\" لإجراء المصادقة"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"فتح الجهاز بالتعرف على الوجه"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"إعادة تسجيل وجهك"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"لتحسين قدرة الجهاز على معرفة وجهك، يُرجى إعادة تسجيل الوجه."</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"إعداد ميزة \"فتح القفل بالوجه\""</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"إعداد ميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"يمكنك فتح قفل هاتفك بمجرّد النظر إلى الشاشة."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"إعداد المزيد من الطرق لفتح قفل الجهاز"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"انقر لإضافة بصمة إصبع."</string>
@@ -655,19 +655,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"يتعذّر التحقُّق من الوجه. الجهاز غير مُتاح."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"حاول استخدام \"فتح القفل بالوجه\" مرة أخرى."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"جرِّب استخدام ميزة \"فتح الجهاز بالتعرف على الوجه\" مرة أخرى."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"يتعذَّر تخزين بيانات الوجه الجديد. احذف الوجه القديم أولاً."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"تمّ إلغاء عملية مصادقة الوجه."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"ألغى المستخدم \"فتح القفل بالوجه\"."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"ألغى المستخدم ميزة \"فتح الجهاز بالتعرف على الوجه\"."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"تمّ إجراء محاولات كثيرة. أعِد المحاولة لاحقًا."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"تم إجراء عدد كبير جدًا من المحاولات. وتم إيقاف \"فتح القفل بالوجه\"."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"تم إجراء محاولات كثيرة، ولذا تم إيقاف ميزة \"فتح الجهاز بالتعرف على الوجه\"."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"يتعذّر التحقق من الوجه. حاول مرة أخرى."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"لم يسبق لك إعداد \"فتح القفل بالوجه\"."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"\"فتح القفل بالوجه\" غير متوفر على هذا الجهاز."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"لم يسبق لك إعداد ميزة \"فتح الجهاز بالتعرف على الوجه\"."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"ميزة \"فتح الجهاز بالتعرف على الوجه\" غير متوفرة بهذا الجهاز."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"تم إيقاف جهاز الاستشعار مؤقتًا."</string>
<string name="face_name_template" msgid="3877037340223318119">"الوجه <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"استخدام \"فتح القفل بالوجه\""</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"استخدام ميزة \"فتح القفل بالوجه\" أو ميزة \"قفل الشاشة\""</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"فتح الجهاز بالتعرف على الوجه"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"استخدام ميزة \"فتح الجهاز بالتعرف على الوجه\" أو ميزة \"قفل الشاشة\""</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"استخدِم الوجه للمتابعة"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"استخدام ميزة \"فتح القفل بالوجه\" أو ميزة \"قفل الشاشة\" للمتابعة"</string>
<string-array name="face_error_vendor">
@@ -899,7 +899,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"أعد المحاولة"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"أعد المحاولة"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"فتح قفل جميع الميزات والبيانات"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"تم تجاوز الحد الأقصى لعدد محاولات تأمين الجهاز بالوجه"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"تم تجاوز الحد الأقصى لعدد محاولات فتح الجهاز بالتعرف على الوجه"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"ليست هناك شريحة SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"ليس هناك شريحة SIM في الجهاز اللوحي."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"لا تتوفر شريحة SIM في جهاز Android TV."</string>
@@ -969,7 +969,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"توسيع منطقة فتح القفل."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"فتح القفل باستخدام التمرير."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"فتح القفل باستخدام النقش."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"تأمين الجهاز بالوجه."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"استخدام ميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"فتح القفل باستخدام رمز PIN."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"فتح قفل رقم التعريف الشخصي لشريحة SIM."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"فتح قفل مفتاح PUK لشريحة SIM."</string>
@@ -1034,8 +1034,7 @@
<string name="text_copied" msgid="2531420577879738860">"تم نسخ النص إلى الحافظة."</string>
<string name="copied" msgid="4675902854553014676">"تم النسخ."</string>
<string name="pasted_from_app" msgid="5627698450808256545">"تم لصق محتوى في <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> من <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>."</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"لصَق تطبيق <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> محتوىً من الحافظة."</string>
<string name="pasted_text" msgid="4298871641549173733">"لصق تطبيق <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> النص الذي نسخته."</string>
<string name="pasted_image" msgid="4729097394781491022">"لصق تطبيق <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> الصورة التي نسختها."</string>
<string name="pasted_content" msgid="646276353060777131">"لصق تطبيق <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> المحتوى الذي نسخته."</string>
@@ -1340,14 +1339,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"جارٍ تحضير <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"بدء التطبيقات."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"جارٍ إعادة التشغيل."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"هل تريد إيقاف الشاشة؟"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"أثناء إعداد بصمة الإصبع، ضغطت على زر التشغيل.\n\nيؤدي هذا الإجراء عادةً إلى إيقاف الشاشة."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"إيقاف"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"إلغاء"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> يعمل"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"انقر للعودة إلى اللعبة"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"اختيار اللعبة"</string>
@@ -1806,8 +1801,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استخدام الاختصار"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"قلب الألوان"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"تصحيح الألوان"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"وضع \"التصفح بيد واحدة\""</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"زيادة تعتيم الشاشة"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم تفعيل <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم إيقاف <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -1967,10 +1961,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"تم التحديث بواسطة المشرف"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"تم الحذف بواسطة المشرف"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"حسنًا"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"يؤدي استخدام ميزة \"توفير شحن البطارية\" إلى تفعيل وضع \"المظهر الداكن\" وتقييد أو إيقاف الأنشطة في الخلفية وبعض التأثيرات المرئية وميزات معيّنة وبعض اتصالات الشبكات.\n\n"<annotation id="url">"مزيد من المعلومات"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"يؤدي استخدام ميزة \"توفير شحن البطارية\" إلى تفعيل وضع \"المظهر الداكن\" وتقييد أو إيقاف الأنشطة في الخلفية وبعض التأثيرات المرئية وميزات معيّنة وبعض اتصالات الشبكات."</string>
<string name="data_saver_description" msgid="4995164271550590517">"للمساعدة في خفض استخدام البيانات، تمنع ميزة \"توفير البيانات\" بعض التطبيقات من إرسال البيانات وتلقّيها في الخلفية. يمكن للتطبيقات المتاحة لديك الآن استخدام البيانات، ولكن لا يمكنها الإكثار من ذلك. وهذا يعني أن الصور مثلاً لا تظهر حتى تنقر عليها."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"هل تريد تفعيل توفير البيانات؟"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"تفعيل"</string>
@@ -2109,10 +2101,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"التطبيق <xliff:g id="APP_NAME_0">%1$s</xliff:g> غير متاح الآن، وهو مُدار بواسطة <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"مزيد من المعلومات"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"استئناف تشغيل التطبيق"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"هل تريد تفعيل تطبيقات العمل؟"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"الوصول إلى تطبيقات العمل وإشعاراتها"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"تفعيل"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"التطبيق غير متاح"</string>
<string name="app_blocked_message" msgid="542972921087873023">"تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> غير متاح الآن."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 8101c28..569b628 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"মুখাৱয়বৰদ্বাৰা আনলক হার্ডৱেৰ পৰিচালনা কৰক"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"ফেচ আনলক হার্ডৱেৰ পৰিচালনা কৰক"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"মুখমণ্ডলৰ টেম্প্লেট যোগ কৰাৰ বা মচাৰ পদ্ধতি কামত লগাবলৈ আহ্বান কৰিবলৈ এপটোক অনুমতি দিয়ে।"</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"মুখাৱয়বৰদ্বাৰা আনলক হার্ডৱেৰ ব্যৱহাৰ কৰক"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণৰ বাবে এপ্ক মুখাৱয়বৰদ্বাৰা আনলক কৰা হাৰ্ডৱেৰ ব্যৱহাৰ কৰিবলৈ দিয়ে"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"মুখাৱয়বৰদ্বাৰা আনলক কৰা সুবিধা"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ফেচ আনলক হার্ডৱেৰ ব্যৱহাৰ কৰক"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণৰ বাবে এপ্ক ফেচ আনলক কৰা হাৰ্ডৱেৰ ব্যৱহাৰ কৰিবলৈ দিয়ে"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ফেচ আনলক কৰা সুবিধা"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"আপোনাৰ মুখমণ্ডল পুনৰ পঞ্জীয়ণ কৰক"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"চিনাক্তকৰণৰ সুবিধাটো উন্নত কৰিবলৈ, অনুগ্ৰহ কৰি আপোনাৰ মুখমণ্ডল পুনৰ পঞ্জীয়ন কৰক"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"মুখাৱয়বৰদ্বাৰা আনলক কৰা সুবিধাটো ছেট আপ কৰক"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"ফেচ আনলক সুবিধাটো ছেট আপ কৰক"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"আপোনাৰ ফ’নটোলৈ চাই সেইটো আনলক কৰক"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক কৰাৰ অধিক উপায় ছেট আপ কৰক"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"এটা ফিংগাৰপ্ৰিণ্ট যোগ দিবলৈ টিপক"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"মুখমণ্ডল সত্যাপন কৰিব পৰা নগ’ল। হাৰ্ডৱেৰ নাই।"</string>
- <string name="face_error_timeout" msgid="522924647742024699">"পুনৰ মুখাৱয়বৰদ্বাৰা আনলক কৰি চাওক।"</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"পুনৰ ফেচ আনলক কৰি চাওক।"</string>
<string name="face_error_no_space" msgid="5649264057026021723">"নতুন মুখমণ্ডলৰ ডেটা জমা কৰিব পৰা নাই। প্ৰথমে পুৰণি এখন মচক।"</string>
<string name="face_error_canceled" msgid="2164434737103802131">"মুখমণ্ডলৰ প্ৰক্ৰিয়া বাতিল কৰা হ’ল।"</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"ব্যৱহাৰকাৰীয়ে মুখাৱয়বৰদ্বাৰা আনলক কৰাটো বাতিল কৰিছে।"</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"ব্যৱহাৰকাৰীয়ে ফেচ আনলক কৰাটো বাতিল কৰিছে।"</string>
<string name="face_error_lockout" msgid="7864408714994529437">"অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।"</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"অতি বেছি প্ৰয়াস। মুখাৱয়বৰদ্বাৰা আনলক কৰাটো অক্ষম কৰা হৈছে।"</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"অতি বেছি প্ৰয়াস। ফেচ আনলক কৰাটো অক্ষম কৰা হৈছে।"</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"মুখমণ্ডল সত্যাপন কৰিব পৰা নগ’ল। আকৌ চেষ্টা কৰক।"</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"আপুনি মুখাৱয়বৰদ্বাৰা আনলক কৰাটো ছেট আপ কৰা নাই।"</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"এই ডিভাইচটোত মুখাৱয়বৰদ্বাৰা আনলক কৰা সুবিধাটো নচলে।"</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"আপুনি ফেচ আনলক ছেট আপ কৰা নাই।"</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"এই ডিভাইচটোত ফেচ আনলক কৰা সুবিধাটো নচলে।"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে।"</string>
<string name="face_name_template" msgid="3877037340223318119">"মুখমণ্ডল <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"মুখাৱয়বৰে আনলক কৰা ব্যৱহাৰ কৰক"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"মুখাৱয়বৰে আনলক কৰা অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ফেচ আনলক ব্যৱহাৰ কৰক"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ফেচ আনলক অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"অব্যাহত ৰাখিবলৈ নিজৰ মুখাৱয়ব ব্যৱহাৰ কৰক"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"অব্যাহত ৰাখিবলৈ আপোনাৰ মুখাৱয়ব অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"আকৌ চেষ্টা কৰক"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"আকৌ চেষ্টা কৰক"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"সকলো সুবিধা আৰু ডেটাৰ বাবে আনলক কৰক"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"গৰাকীৰ মুখাৱয়বৰ দ্বাৰা আনলক কৰা সর্বধিক সীমা অতিক্ৰম কৰা হ’ল"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"গৰাকীৰ ফেচ আনলক কৰা সৰ্বাধিক সীমা অতিক্ৰম কৰা হ’ল"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"কোনো ছিম কাৰ্ড নাই"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"টে\'বলেটত ছিম কার্ড নাই।"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"আপোনাৰ Android TV ডিভাইচটোত কোনো ছিম কার্ড নাই।"</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"আনলক ক্ষেত্ৰ বিস্তাৰ কৰক।"</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"শ্লাইডৰদ্বাৰা আনলক।"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"আৰ্হিৰদ্বাৰা আনলক।"</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"মুখাৱয়বৰদ্বাৰা আনলক।"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"ফেচ আনলক।"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"পিনৰদ্বাৰা আনলক।"</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"ছিম পিন আনলক।"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"ছিম পিইউকে আনলক।"</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"ক্লিপব\'র্ডলৈ বাৰ্তা প্ৰতিলিপি কৰা হ’ল।"</string>
<string name="copied" msgid="4675902854553014676">"প্ৰতিলিপি কৰা হ’ল"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>ৰ পৰা <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> পে’ষ্ট কৰা হৈছে"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>এ আপোনাৰ ক্লিপব’ৰ্ডৰ পৰা পে’ষ্ট কৰিছে"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>এ আপুনি প্ৰতিলিপি কৰা সমল পে’ষ্ট কৰিছে"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>এ আপুনি প্ৰতিলিপি কৰা এটা প্ৰতিচ্ছবি পে’ষ্ট কৰিছে"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>এ আপুনি প্ৰতিলিপি কৰা সমল পে’ষ্ট কৰিছে"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>সাজু কৰি থকা হৈছে।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"আৰম্ভ হৈ থকা এপসমূহ।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"বুট কাৰ্য সমাপ্ত কৰিছে।"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"স্ক্ৰীন অফ কৰিবনে?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"আপোনাৰ ফিংগাৰপ্ৰিণ্ট ছেট আপ কৰাৰ সময়ত, আপুনি পাৱাৰ বুটামটো টিপিছে।\n\nএইটোৱে সচৰাচৰ আপোনাৰ স্ক্ৰীনখন অফ কৰে।"</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"অফ কৰক"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"বাতিল কৰক"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> চলি আছে"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"গেইমলৈ উভতি যাওক"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"গেইম বাছনি কৰক"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"আপোনাৰ প্ৰশাসকে আপেডট কৰিছে"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"আপোনাৰ প্ৰশাসকে মচিছে"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ঠিক আছে"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"বেটাৰী সঞ্চয়কাৰীয়ে গাঢ় ৰঙৰ থীম অন কৰে আৰু নেপথ্যৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল ইফেক্ট, নিৰ্দিষ্ট কিছুমান সুবিধা আৰু নেটৱৰ্কৰ সংযোগ সীমিত অথবা অফ কৰে।\n\n"<annotation id="url">"অধিক জানক"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"বেটাৰী সঞ্চয়কাৰীয়ে গাঢ় ৰঙৰ থীম অন কৰে আৰু নেপথ্যৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল ইফেক্ট, নিৰ্দিষ্ট কিছুমান সুবিধা আৰু নেটৱৰ্কৰ সংযোগ অফ কৰে অথবা সীমাবদ্ধ কৰে।"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ডেটা ব্য়ৱহাৰৰ হ্ৰাস কৰিবলৈ ডেটা সঞ্চয়কাৰীয়ে কিছুমান এপক নেপথ্য়ত ডেটা প্ৰেৰণ বা সংগ্ৰহ কৰাত বাধা প্ৰদান কৰে। আপুনি বৰ্তমান ব্য়ৱহাৰ কৰি থকা এটা এপে ডেটা এক্সেছ কৰিব পাৰে, কিন্তু সঘনাই এক্সেছ কৰিব নোৱাৰিব পাৰে। ইয়াৰ অৰ্থ উদাহৰণস্বৰূপে এয়া হ\'ব পাৰে যে, আপুনি নিটিপা পর্যন্ত প্ৰতিচ্ছবিসমূহ দেখুওৱা নহ’ব।"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ডেটা সঞ্চয়কাৰী অন কৰিবনে?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"অন কৰক"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"এই মুহূৰ্তত <xliff:g id="APP_NAME_0">%1$s</xliff:g> উপলব্ধ নহয়। ইয়াক <xliff:g id="APP_NAME_1">%2$s</xliff:g>এ পৰিচালনা কৰে।"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"অধিক জানক"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"এপ্ আনপজ কৰক"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"কাম সম্পৰ্কীয় এপ্ অন কৰিবনে?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"আপোনাৰ কাম সম্পৰ্কীয় এপ্ আৰু জাননীৰ এক্সেছ পাওক"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"অন কৰক"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"এপ্টো উপলব্ধ নহয়"</string>
<string name="app_blocked_message" msgid="542972921087873023">"এই মুহূৰ্তত <xliff:g id="APP_NAME">%1$s</xliff:g> উপলব্ধ নহয়।"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index e8abac1..6552e82 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -609,11 +609,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Barmaq izi ikonası"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"üz kilidi avadanlığını idarə edin"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"Üz ilə kiliddən açma avadanlığını idarə edin"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Proqramdan istifadə üçün barmaq izi şablonlarını əlavə etmək və silmək məqsədilə üsullara müraciət etməyə imkan verir."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"üz kilidi avadanlığından istifadə edin"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"İdentifikasiya üçün tətbiqin üz kilidi avadanlığından istifadə etməsinə icazə verir"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Üz kilidi"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"üz ilə kiliddən açma işlədin"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"İdentifikasiya üçün tətbiqin Üz ilə Kiliddən Açmasına icazə verir"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Üz ilə Kiliddən Açma"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Üzünüzü yenidən qeydiyyatdan keçirin"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Tanınmanı təkmilləşdirmək üçün üzünüzü yenidən qeydiyyatdan keçirin"</string>
<string name="face_setup_notification_title" msgid="550617822603450009">"Üz ilə kiliddən çıxarmanı ayarlayın"</string>
@@ -643,15 +643,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Üz doğrulanmadı. Avadanlıq əlçatan deyil."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Üz kilidini yenidən sınayın."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Üz ilə Kiliddən Açmanı yenidən sınayın."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Yeni üz datası saxlanmadı. Əvvəlcə köhnə olanı silin."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Üz əməliyyatı ləğv edildi."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"İstifadəçi üz kilidini ləğv edib."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"İstifadəçi Üz ilə Kiliddən Açmanı ləğv edib."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Həddindən çox cəhd. Sonraya saxlayın."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Həddindən çox cəhd. Üz kilidi deaktiv edildi."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Həddindən çox cəhd. Üz ilə Kiliddən Açma deaktiv edildi."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Üz doğrulanmadı. Yenidən cəhd edin."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Üz kilidi quraşdırmamısınız."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Üz kilidi bu cihazda dəstəklənmir."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Üz ilə Kiliddən Açmanı quraşdırmamısınız."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Üz ilə Kiliddən Açma bu cihazda dəstəklənmir."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor müvəqqəti deaktivdir."</string>
<string name="face_name_template" msgid="3877037340223318119">"Üz <xliff:g id="FACEID">%d</xliff:g>"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"Üz ilə kiliddən çıxarmadan istifadə edin"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Bir də cəhd edin"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Bir daha cəhd et"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Bütün funksiyalar və data üçün kiliddən çıxarın"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Sifət kilidi cəhdləriniz bitdi"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Üz ilə Kiliddən Açma cəhdləriniz bitdi"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM kart yoxdur."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Planşetdə SIM kart yoxdur."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Android TV cihazında SIM kart yoxdur."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Kilidi açma sahəsini genişləndir."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Sürüşdürmə kilidi."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Kild açma modeli."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Sifət Kilidi"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Üz ilə Kiliddən Açma"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Pin kilid açması."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Sim Pin kilidini açın."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Sim Puk kilidini açın."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Mətn panoya kopyalandı."</string>
<string name="copied" msgid="4675902854553014676">"Kopyalandı"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> tətbiqindən əlavə edilib"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> mübadilə buferinizdən əlavə edilib"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız mətni əlavə etdi"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız şəkli əlavə etdi"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız kontenti əlavə etdi"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> proqramının hazırlanması."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Tətbiqlər başladılır."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Yükləmə başa çatır."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ekran deaktiv edilsin?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Barmaq izinizi ayarlayarkən Qidalanma düyməsinə basdınız.\n\nBu, adətən ekranınızı deaktiv edir."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Deaktiv edin"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Ləğv edin"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> çalışır"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Oyuna qayıtmaq üçün klikləyin"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Oyun seçin"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Qısayol İstifadə edin"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Rəng İnversiyası"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Rəng korreksiyası"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Bir əlli rejim"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Əlavə qaraltma"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiv edildi."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basılaraq saxlanıb. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edilib."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Admin tərəfindən yeniləndi"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Admin tərəfindən silindi"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Enerjiyə Qənaət rejimi Tünd temanı aktivləşdirir, habelə arxa fon fəaliyyətini, bəzi vizual effektləri, müəyyən xüsusiyyətləri və bəzi şəbəkə bağlantılarını məhdudlaşdırır, yaxud söndürür.\n\n"<annotation id="url">"Ətraflı məlumat"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Enerjiyə Qənaət rejimi Tünd temanı aktivləşdirir, habelə arxa fon fəaliyyətini, bəzi vizual effektləri, müəyyən xüsusiyyətləri və bəzi şəbəkə bağlantılarını məhdudlaşdırır, yaxud söndürür."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Mobil interneti qənaətlə işlətmək məqsədilə Data Qanaəti bəzi tətbiqlərin fonda data göndərməsinin və qəbulunun qarşısını alır. Hazırda işlətdiyiniz tətbiq nisbətən az müntəzəmliklə data istifadə edə bilər. Örnək olaraq bu, o deməkdir ki, şəkil fayllarına toxunmadıqca onlar açılmayacaq."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Trafikə qənaət edilsin?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivləşdirin"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> hazırda əlçatan deyil. Bunu <xliff:g id="APP_NAME_1">%2$s</xliff:g> idarə edir."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Ətraflı məlumat"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Tətbiqi davam etdirin"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"İş tətbiqləri aktiv edilsin?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"İş tətbiqlərinizə və bildirişlərinizə giriş əldə edin"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivləşdirin"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Tətbiq əlçatan deyil"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hazırda əlçatan deyil."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 2681451..91ac488 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1892,10 +1892,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Potvrdi"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte, određene funkcije i mrežne veze.\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Ušteda baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte, određene funkcije i neke mrežne veze."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjila potrošnja podataka, Ušteda podataka sprečava neke aplikacije da šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može da pristupa podacima, ali će to činiti ređe. Na primer, slike se neće prikazivati dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Želite da uključite Uštedu podataka?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string>
@@ -2007,10 +2005,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> trenutno nije dostupna. <xliff:g id="APP_NAME_1">%2$s</xliff:g> upravlja dostupnošću."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Saznajte više"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Opozovi pauziranje aplikacije"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Uključiti poslovne aplikacije?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Pristupajte poslovnim aplikacijama i obaveštenjima"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 9f7fc35..24243e4 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -893,7 +893,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Паўтарыце спробу"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Паўтарыце спробу"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Разблакіраваць для ўсіх функцый і даных"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Перавышана максімальная колькасць спроб разблакоўкі праз Фэйскантроль"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Перавышана максімальная колькасць спроб разблакоўкі праз распазнаванне твару"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Няма SIM-карты"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Няма SIM-карты ў планшэце."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"У вашай прыладзе Android TV няма SIM-карты."</string>
@@ -1028,8 +1028,7 @@
<string name="text_copied" msgid="2531420577879738860">"Тэкст скапіраваны ў буфер абмену."</string>
<string name="copied" msgid="4675902854553014676">"Скапіравана"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Праграма \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\" была ўстаўлена з праграмы \"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>\""</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Праграма (\"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\") уставіла даныя з буфера абмену"</string>
<string name="pasted_text" msgid="4298871641549173733">"Скапіраваны вамі тэкст устаўлены праграмай \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\""</string>
<string name="pasted_image" msgid="4729097394781491022">"Скапіраваны вамі відарыс устаўлены праграмай \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\""</string>
<string name="pasted_content" msgid="646276353060777131">"Скапіраванае вамі змесціва ўстаўлена праграмай \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\""</string>
@@ -1300,14 +1299,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Падрыхтоўка <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск прыкладанняў."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завяршэнне загрузкі."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Выключыць экран?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Падчас наладкі адбітка пальца вы націскалі кнопку сілкавання.\n\nЗвычайна гэта дзеянне выключае экран."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Выключыць"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Скасаваць"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Прыкладанне \"<xliff:g id="APP">%1$s</xliff:g>\" запушчанае"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Націсніце, каб вярнуцца да гульні"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Выберыце гульню"</string>
@@ -1762,8 +1757,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Выкарыстоўваць камбінацыю хуткага доступу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія колераў"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Карэкцыя колераў"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Рэжым кіравання адной рукой"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дадатковае памяншэнне яркасці"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Клавішы гучнасці ўтрымліваліся націснутымі. Уключана служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Клавішы гучнасці ўтрымліваліся націснутымі. Служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" выключана."</string>
@@ -1921,10 +1915,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Абноўлены вашым адміністратарам"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Выдалены вашым адміністратарам"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"У рэжыме эканоміі зараду ўключаецца цёмная тэма і выключаюцца ці абмяжоўваюцца дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты, пэўныя функцыі і падключэнні да сетак.\n\n"<annotation id="url">"Даведацца больш"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"У рэжыме эканоміі зараду ўключаецца цёмная тэма і выключаюцца ці абмяжоўваюцца дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты, пэўныя функцыі і падключэнні да сетак."</string>
<string name="data_saver_description" msgid="4995164271550590517">"У рэжыме \"Эканомія трафіка\" фонавая перадача для некаторых праграмам адключана. Праграма, якую вы зараз выкарыстоўваеце, можа атрымліваць доступ да даных, але радзей, чым звычайна. Напрыклад, відарысы могуць не загружацца, пакуль вы не націсніце на іх."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Уключыць Эканомію трафіка?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Уключыць"</string>
@@ -2045,10 +2037,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Праграма \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" цяпер недаступная. Яна кіруецца праграмай \"<xliff:g id="APP_NAME_1">%2$s</xliff:g>\"."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Даведацца больш"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Скасаваць прыпыненне для праграмы"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Уключыць працоўныя праграмы?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Атрымаць доступ да працоўных праграм і апавяшчэнняў"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Уключыць"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Праграма недаступная"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" цяпер недаступная."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 619dcb7..d82f189 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Текстът е копиран в буферната памет."</string>
<string name="copied" msgid="4675902854553014676">"Копирано"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> постави данни от <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> постави данни от буферната памет"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> постави копиран от вас текст"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> постави копирано от вас изображение"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> постави копирано от вас съдържание"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> се подготвя."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Приложенията се стартират."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Зареждането завършва."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Да се изключи ли екранът?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"При настройването на отпечатъка си натиснахте бутона за захранване.\n\nТова действие обикновено изключва екрана."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Изключване"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Отказ"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> се изпълнява"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Докоснете, за да се върнете към играта"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Избиране на игра"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Актуализирано от администратора ви"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Изтрито от администратора ви"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Режимът за запазване на батерията включва тъмната тема и ограничава или изключва активността на заден план, някои визуални ефекти, определени функции и някои връзки с мрежата.\n\n"<annotation id="url">"Научете повече"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Режимът за запазване на батерията включва тъмната тема и ограничава или изключва активността на заден план, някои визуални ефекти, определени функции и някои връзки с мрежата."</string>
<string name="data_saver_description" msgid="4995164271550590517">"С цел намаляване на преноса на данни функцията за икономия на данни не позволява на някои приложения да изпращат или получават данни на заден план. Понастоящем използвано от вас приложение може да използва данни, но по-рядко. Това например може да означава, че изображенията не се показват, докато не ги докоснете."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Включване на „Икономия на данни“?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Включване"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"В момента няма достъп до <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Това се управлява от <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Научете повече"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Отмяна на паузата за приложението"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Включване на служ. приложения?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Получете достъп до служебните си приложения и известия"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Включване"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Приложението не е достъпно"</string>
<string name="app_blocked_message" msgid="542972921087873023">"В момента няма достъп до <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 9dedb88..b1be0ca 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"আঙ্গুলের ছাপ আইকন"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"মুখের সাহায্যে আনলক করার হার্ডওয়্যার ম্যানেজ করা"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"ফেস আনলক হার্ডওয়্যার ম্যানেজ করা"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"ব্যবহার করার জন্য ফেস টেম্পলেট যোগ করা এবং মোছার পদ্ধতি গ্রহণ করতে অ্যাপটিকে অনুমতি দেয়৷"</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"মুখের সাহায্যে আনলক করার হার্ডওয়্যার ব্যবহার করা"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"অ্যাপকে যাচাইকরণের জন্য মুখের সাহায্যে আনলক করার হার্ডওয়্যার ব্যবহার করতে দেয়"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"মুখের সাহায্যে আনলক"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ফেস আনলক হার্ডওয়্যার ব্যবহার করা"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"অ্যাপকে যাচাইকরণের জন্য ফেস আনলক হার্ডওয়্যার ব্যবহার করতে দেয়"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ফেস আনলক"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"আপনার ফেস আবার এনরোল করুন"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"শনাক্তকরণের উন্নতি করতে আপনার ফেস আবার এনরোল করুন"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"মুখের সাহায্যে আনলক করার ফিচার সেট-আপ করুন"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"ফেস আনলক ফিচার সেট-আপ করুন"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"আপনার ফোনের দিকে তাকিয়ে এটিকে আনলক করুন"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক করার জন্য বিভিন্ন উপায়ে সেট আপ করুন"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"একটি আঙ্গুলের ছাপ যোগ করতে ট্যাপ করুন"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"ফেস যাচাই করা যায়নি। হার্ডওয়্যার উপলভ্য নেই।"</string>
- <string name="face_error_timeout" msgid="522924647742024699">"আবার মুখের সাহায্যে আনলক করার চেষ্টা করুন।"</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"আবার ফেস আনলকের মাধ্যমে চেষ্টা করুন।"</string>
<string name="face_error_no_space" msgid="5649264057026021723">"নতুন ফেস ডেটা স্টোর করা যায়নি। প্রথমে পুরনোটি মুছে ফেলুন।"</string>
<string name="face_error_canceled" msgid="2164434737103802131">"ফেস অপারেশন বাতিল করা হয়েছে৷"</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"ব্যবহারকারী মুখের সাহায্যে আনলক বাতিল করে দিয়েছেন।"</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"ব্যবহারকারী ফেস আনলক বাতিল করে দিয়েছেন।"</string>
<string name="face_error_lockout" msgid="7864408714994529437">"অনেকবার চেষ্টা করা হয়েছে। পরে আবার চেষ্টা করুন।"</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"অনেকবার চেষ্টা করেছেন। মুখের সাহায্যে আনলক করার সুবিধা বন্ধ করা হয়েছে।"</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"অনেকবার চেষ্টা করেছেন। ফেস আনলক বন্ধ করা হয়েছে।"</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"আপনার মুখ যাচাই করা যাচ্ছে না। আবার চেষ্টা করুন।"</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"এখনও মুখের সাহায্যে আনলক করার সুবিধা সেট-আপ করেননি।"</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"এই ডিভাইসে মুখের সাহায্যে আনলক করার সুবিধাটি কাজ করে না।"</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"এখনও ফেস আনলক সেট-আপ করেননি।"</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"এই ডিভাইসে ফেস আনলক সুবিধাটি কাজ করে না।"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"সেন্সর অস্থায়ীভাবে বন্ধ করা আছে।"</string>
<string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g> ফেস"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"মুখের সাহায্যে আনলক ব্যবহার করুন"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"মুখ অথবা স্ক্রিন লক ব্যবহার করুন"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ফেস আনলক ব্যবহার করুন"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ফেস অথবা স্ক্রিন লক ব্যবহার করুন"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"চালিয়ে যেতে আপনার মুখ ব্যবহার করুন"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"চালিয়ে যেতে আপনার ফেস বা স্ক্রিন লক ব্যবহার করুন"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"আবার চেষ্টা করুন"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"আবার চেষ্টা করুন"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"সমস্ত বৈশিষ্ট্য এবং ডেটার জন্য আনলক করুন"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"মুখের সাহায্যে আনলক করার প্রচেষ্টা যতবার করা যায় তার সীমা পেরিয়ে গেছে"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"ফেস আনলক ফিচারের সাহায্যে আনলকের চেষ্টা সর্বোচ্চ সীমা পেরিয়ে গেছে"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"কোনো সিম কার্ড নেই"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"ট্যাবলেটের মধ্যে কোনো সিম কার্ড নেই৷"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"আপনার Android TV ডিভাইসে কোনও সিম কার্ড নেই।"</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"আনলক এলাকা প্রসারিত করুন৷"</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"স্লাইড দিয়ে আনলক৷"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"প্যাটার্ন দিয়ে আনলক৷"</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"মুখের সাহায্যে আনলক৷"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"ফেস আনলক৷"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"পিন দিয়ে আনলক৷"</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"সিম পিন আনলক।"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"সিম পিইউকে আনলক।"</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শর্টকাট ব্যবহার করুন"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"রঙ উল্টানো"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"রঙ সংশোধন"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"এক হাতে ব্যবহার করার মোড"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিরিক্ত কম আলো"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করা হয়েছে।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"আপনার প্রশাসক আপডেট করেছেন"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"আপনার প্রশাসক মুছে দিয়েছেন"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ঠিক আছে"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ব্যাটারি সেভার ডার্ক থিম চালু করে এবং ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট, নির্দিষ্ট ফিচার ও কয়েকটি নেটওয়ার্ক কানেকশনের ব্যবহার সীমিত করে বা বন্ধ করে দেয়।\n\n"<annotation id="url">"আরও জানুন"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"ব্যাটারি সেভার ডার্ক থিম চালু করে এবং ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট, নির্দিষ্ট ফিচার ও কয়েকটি নেটওয়ার্ক কানেকশনের ব্যবহার সীমিত করে বা বন্ধ করে দেয়।"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ডেটার ব্যবহার কমাতে সহায়তা করার জন্য, ডেটা সেভার ব্যাকগ্রাউন্ডে কিছু অ্যাপ্লিকেশনকে ডেটা পাঠাতে বা গ্রহণ করতে বাধা দেয়৷ আপনি বর্তমানে এমন একটি অ্যাপ্লিকেশন ব্যবহার করছেন যেটি ডেটা অ্যাক্সেস করতে পারে, তবে সেটি কমই করে৷ এর ফলে যা হতে পারে, উদাহরণস্বরূপ, আপনি ছবির উপর ট্যাপ না করা পর্যন্ত সেগুলি দেখানো হবে না৷"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ডেটা সেভার চালু করবেন?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"চালু করুন"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> এখন উপলভ্য নয়। এই অ্যাপটিকে <xliff:g id="APP_NAME_1">%2$s</xliff:g> অ্যাপ ম্যানেজ করে।"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"আরও জানুন"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"অ্যাপ আবার চালু করুন"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"অফিস অ্যাপ চালু করবেন?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"আপনার অফিস অ্যাপ এবং বিজ্ঞপ্তিতে অ্যাক্সেস পান"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"চালু করুন"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"অ্যাপ পাওয়া যাচ্ছে না"</string>
<string name="app_blocked_message" msgid="542972921087873023">"এই মুহূর্তে <xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপ পাওয়া যাচ্ছে না।"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index c918d8c..35f5e4b 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1892,10 +1892,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je vaš administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je vaš administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Uredu"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnost u pozadini, određene vizuelne efekte i funkcije te neke mrežne veze.\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnost u pozadini, određene vizuelne efekte i funkcije i neke mrežne veze."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Radi smanjenja prijenosa podataka, Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali će to činiti rjeđe. Naprimjer, to može značiti da se slike ne prikazuju dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Uključiti Uštedu podataka?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string>
@@ -2007,10 +2005,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> trenutno nije dostupna. Ovim upravlja aplikacija <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Saznajte više"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Ponovo aktiviraj aplikaciju"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Uključiti poslovne aplikacije?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Pristupite poslovnim aplikacijama i obavještenjima"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 402402f..2b16b36 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -609,10 +609,10 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona d\'empremta digital"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"gestiona el maquinari de desbloqueig facial"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"gestiona el maquinari de Desbloqueig facial"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Permet que l\'aplicació afegeixi i suprimeixi plantilles de cares que es puguin fer servir."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"utilitza el maquinari de desbloqueig facial"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Permet que l\'aplicació faci servir el maquinari de desbloqueig facial per a l\'autenticació"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"utilitza el maquinari de Desbloqueig facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Permet que l\'aplicació faci servir el maquinari de Desbloqueig facial per a l\'autenticació"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Desbloqueig facial"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Torna a registrar la cara"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Per millorar el reconeixement, torna a registrar la cara"</string>
@@ -648,7 +648,7 @@
<string name="face_error_canceled" msgid="2164434737103802131">"S\'ha cancel·lat el reconeixement facial."</string>
<string name="face_error_user_canceled" msgid="8553045452825849843">"L\'usuari ha cancel·lat el desbloqueig facial."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Massa intents. Torna-ho a provar més tard."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Massa intents. S\'ha desactivat el desbloqueig facial."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Massa intents. S\'ha desactivat Desbloqueig facial."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"No es pot verificar la cara. Torna-ho a provar."</string>
<string name="face_error_not_enrolled" msgid="7369928733504691611">"No has configurat el desbloqueig facial"</string>
<string name="face_error_hw_not_present" msgid="1070600921591729944">"El desbloqueig facial no és compatible amb el dispositiu."</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Torna-ho a provar"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Torna-ho a provar"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Desbl. per accedir a totes les funcions i dades"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"S\'ha superat el nombre màxim d\'intents de desbloqueig facial"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"S\'ha superat el nombre màxim d\'intents de Desbloqueig facial"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"No hi ha cap SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"No hi ha cap SIM a la tauleta."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"No hi ha cap targeta SIM al dispositiu Android TV."</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilitza la drecera"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversió de colors"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correcció de color"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode d\'una mà"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuació extra"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S\'han mantingut premudes les tecles de volum. S\'ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S\'han mantingut premudes les tecles de volum. S\'ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Actualitzat per l\'administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Suprimit per l\'administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"D\'acord"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions a la xarxa.\n\n"<annotation id="url">"Més informació"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions a la xarxa."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Per reduir l\'ús de dades, la funció Economitzador de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a les dades, però menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activar l\'Economitzador de dades?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activa"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> no està disponible en aquests moments. Aquesta opció es gestiona a <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Més informació"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Reactiva l\'aplicació"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Activar aplicacions de treball?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Accedeix a les teves aplicacions de treball i a les notificacions"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activa"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"L\'aplicació no està disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Ara mateix, <xliff:g id="APP_NAME">%1$s</xliff:g> no està disponible."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 17ddb0e..0ae3e7a 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -656,7 +656,7 @@
<string name="face_error_lockout" msgid="7864408714994529437">"Příliš mnoho pokusů. Zkuste to později."</string>
<string name="face_error_lockout_permanent" msgid="8277853602168960343">"Příliš mnoho pokusů. Odemknutí obličejem bylo deaktivováno."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Obličej se nepodařilo ověřit. Zkuste to znovu."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Ověření obličejem nemáte nastavené."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Odemknutí obličejem nemáte nastavené."</string>
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Odemknutí obličejem na tomto zařízení není podporováno."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Senzor je dočasně deaktivován."</string>
<string name="face_name_template" msgid="3877037340223318119">"Obličej <xliff:g id="FACEID">%d</xliff:g>"</string>
@@ -1915,10 +1915,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Aktualizováno administrátorem"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Smazáno administrátorem"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Spořič baterie zapíná tmavý motiv a omezuje či vypíná aktivitu na pozadí, některé vizuální efekty, některé funkce a připojení k některým sítím.\n\n"<annotation id="url">"Další informace"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Spořič baterie zapíná tmavý motiv a omezuje či vypíná aktivitu na pozadí, některé vizuální efekty, některé funkce a připojení k některým sítím."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Z důvodu snížení využití dat brání spořič dat některým aplikacím v odesílání nebo příjmu dat na pozadí. Aplikace, kterou právě používáte, data přenášet může, ale může tak činit méně často. V důsledku toho se například obrázky nemusejí zobrazit, dokud na ně neklepnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Chcete zapnout Spořič dat?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Zapnout"</string>
@@ -2039,10 +2037,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikace <xliff:g id="APP_NAME_0">%1$s</xliff:g> momentálně není dostupná. Tato předvolba se spravuje v aplikaci <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Další informace"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Zrušit pozastavení aplikace"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Zapnout pracovní aplikace?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Získejte přístup ke svým pracovním aplikacím a oznámením"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Zapnout"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikace není k dispozici"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> v tuto chvíli není k dispozici."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index d4aced53..432a7c7 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Prøv igen"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Prøv igen"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Lås op for at se alle funktioner og data"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Det maksimale antal forsøg på at bruge Ansigtslås er overskredet"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Det maksimale antal forsøg på at bruge ansigtslås er overskredet"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Intet SIM-kort"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Der er ikke noget SIM-kort i tabletcomputeren."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Der er intet SIM-kort i din Android TV-enhed."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Udvid oplåsningsområdet."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Lås op ved at stryge."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Lås op med mønster."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Lås op med ansigt."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Ansigtslås."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Lås op med pinkode."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Lås op ved hjælp af pinkoden til SIM-kortet."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Lås op ved hjælp af PUK-koden til SIM-kortet."</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Brug genvej"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ombytning af farver"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Farvekorrigering"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhåndstilstand"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra dæmpet belysning"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er aktiveret."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er deaktiveret."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index bd01896..bc8dafb 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerabdruck-Symbol"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"Face Unlock-Hardware verwalten"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"Hardware für Gesichtsentsperrung verwalten"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Ermöglicht der App, Gesichtsvorlagen hinzuzufügen oder zu entfernen."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"Face Unlock-Hardware verwenden"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Ermöglicht der App, zu Authentifizierungszwecken Face Unlock-Hardware zu verwenden"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face Unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"Hardware für Gesichtsentsperrung verwenden"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Ermöglicht der App, zur Authentifizierung Hardware für Gesichtsentsperrung zu verwenden"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Gesichtsentsperrung"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Gesicht neu scannen lassen"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Für bessere Erkennung Gesicht neu scannen lassen"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Face Unlock einrichten"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Gesichtsentsperrung einrichten"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Entsperre dein Smartphone, indem du es ansiehst"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weitere Möglichkeiten zum Entsperren einrichten"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tippe, um einen Fingerabdruck hinzuzufügen"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Gesicht nicht erkannt. Hardware nicht verfügbar."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Face Unlock noch einmal versuchen."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Gesichtsentsperrung noch einmal versuchen."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Kein Speicherplatz frei. Bitte erst ein Gesicht löschen."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Gesichtserkennung abgebrochen."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Face Unlock vom Nutzer abgebrochen."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Gesichtsentsperrung vom Nutzer abgebrochen."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Zu viele Versuche. Face Unlock wurde deaktiviert."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Zu viele Versuche. Gesichtsentsperrung wurde deaktiviert."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Gesichtsprüfung nicht möglich. Noch mal versuchen."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Face Unlock ist nicht eingerichtet."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Face Unlock wird auf diesem Gerät nicht unterstützt."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Gesichtsentsperrung ist nicht eingerichtet."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Gesichtsentsperrung wird auf diesem Gerät nicht unterstützt."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Der Sensor ist vorübergehend deaktiviert."</string>
<string name="face_name_template" msgid="3877037340223318119">"Gesicht <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Face Unlock verwenden"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Face Unlock oder Displaysperre verwenden"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Gesichtsentsperrung verwenden"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gesichtsentsperrung oder Displaysperre verwenden"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Gesichtserkennung verwenden, um fortzufahren"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Verwende die Gesichtserkennung oder deine Display-Entsperrmethode, um fortzufahren"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Erneut versuchen"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Erneut versuchen"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Entsperren, um alle Funktionen und Daten zu nutzen"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Die maximal zulässige Anzahl an Face Unlock-Versuchen wurde überschritten."</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Die maximal zulässige Anzahl an Versuchen zur Gesichtsentsperrung wurde überschritten."</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Keine SIM-Karte"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Keine SIM-Karte im Tablet"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Keine SIM-Karte in deinem Android TV-Gerät."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Entsperr-Bereich maximieren"</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Entsperrung mit Fingerbewegung"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Entsperrung mit Muster"</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face Unlock"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Gesichtsentsperrung"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Entsperrung mit PIN"</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM durch PIN-Eingabe entsperren."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM durch PUK-Eingabe entsperren."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Text in Zwischenablage kopiert."</string>
<string name="copied" msgid="4675902854553014676">"Kopiert"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat etwas von <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> eingefügt"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aus der Zwischenablage eingefügt"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat einen von dir kopierten Text eingefügt"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat ein von dir kopiertes Bild eingefügt"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat den von dir kopierten Inhalt eingefügt"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> wird vorbereitet"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Apps werden gestartet..."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Start wird abgeschlossen..."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Display ausschalten?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Beim Einrichten deines Fingerabdrucks hast du die Ein-/Aus-Taste gedrückt.\n\nDamit wird üblicherweise das Display ausgeschaltet."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Ausschalten"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Abbrechen"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> läuft"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tippe, um zum Spiel zurückzukehren"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Spiel wählen"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Der Energiesparmodus aktiviert das dunkle Design und schränkt Hintergrundaktivitäten, einige Funktionen und optische Effekte und manche Netzwerkverbindungen ein oder deaktiviert sie.\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Der Energiesparmodus aktiviert das dunkle Design und schränkt Hintergrundaktivitäten, einige Funktionen und optische Effekte und manche Netzwerkverbindungen ein oder deaktiviert sie."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ist momentan nicht verfügbar. Dies wird über die App \"<xliff:g id="APP_NAME_1">%2$s</xliff:g>\" verwaltet."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Weitere Informationen"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"App-Pausierung aufheben"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Geschäftliche Apps aktivieren?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Du erhältst Zugriff auf deine geschäftlichen Apps und Benachrichtigungen"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivieren"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App ist nicht verfügbar"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist derzeit nicht verfügbar."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 1f11664..d3715d3 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"διαχείριση εξοπλισμού Face Unlock"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"διαχείριση εξοπλισμού για ξεκλείδωμα με το πρόσωπο"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Επιτρέπει στην εφαρμογή να επικαλείται μεθόδους προσθήκης/διαγραφής προτύπων για χρήση."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"χρήση εξοπλισμού Face Unlock"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί εξοπλισμό Face Unlock για έλεγχο ταυτότητας"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face Unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"χρήση εξοπλισμού για ξεκλείδωμα με το πρόσωπο"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Επιτρέπει στην εφαρμογή έλεγχο ταυτότητας με χρήση εξοπλισμού για ξεκλείδωμα με το πρόσωπο"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Ξεκλείδωμα με το πρόσωπο"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Εγγράψτε ξανά το πρόσωπό σας"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Για να βελτιώσετε την αναγνώριση, εγγράψτε ξανά το πρόσωπό σας"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Ρυθμίστε το Face Unlock"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Ρυθμίστε το ξεκλείδωμα με το πρόσωπο"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ξεκλειδώστε το τηλέφωνό σας απλώς κοιτώντας το"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ρυθμίστε περισσότερους τρόπους ξεκλειδώματος"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Πατήστε για να προσθέσετε δακτυλικό αποτύπωμα"</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Αδύν. επαλήθ. προσώπου. Μη διαθέσιμος εξοπλισμός."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Δοκιμάστε ξανά το Face Unlock."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Δοκιμάστε ξανά για ξεκλείδωμα με το πρόσωπο."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Η αποθήκ. νέων δεδομ. προσώπ. είναι αδύν. Διαγρ. ένα παλιό."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Η ενέργεια προσώπου ακυρώθηκε."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Το Face Unlock ακυρώθηκε από τον χρήστη."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Το ξεκλείδωμα με το πρόσωπο ακυρώθηκε από τον χρήστη."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Πάρα πολλές προσπάθειες. Δοκιμάστε ξανά αργότερα."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Υπερβολικά πολλές προσπάθειες. Το Face Unlock απενεργοποιήθηκε."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Υπερβολικά πολλές προσπάθειες. Το ξεκλείδωμα με το πρόσωπο απενεργοποιήθηκε."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Αδύνατη επαλήθευση του προσώπου. Επανάληψη."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Δεν έχετε ρυθμίσει το Face Unlock."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Το Face Unlock δεν υποστηρίζεται σε αυτήν τη συσκευή."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Δεν έχετε ρυθμίσει το ξεκλείδωμα με το πρόσωπο."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Το Ξεκλείδωμα με το πρόσωπο δεν υποστηρίζεται σε αυτήν τη συσκευή."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά."</string>
<string name="face_name_template" msgid="3877037340223318119">"Πρόσωπο <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Χρήση Face Unlock"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Χρήση ξεκλειδώματος με το πρόσωπο"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Χρήση προσώπου ή κλειδώματος οθόνης"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Χρησιμοποιήστε το πρόσωπό σας για να συνεχίσετε"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Χρησιμοποιήστε το πρόσωπό σας ή το κλείδωμα οθόνης για συνέχεια"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Προσπαθήστε ξανά"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Προσπαθήστε ξανά"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Ξεκλείδωμα για όλες τις λειτουργίες και δεδομένα"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Έγινε υπέρβαση του μέγιστου αριθμού προσπαθειών Face Unlock"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Έγινε υπέρβαση του μέγιστου αριθμού προσπαθειών για Ξεκλείδωμα με το πρόσωπο"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Δεν υπάρχει κάρτα SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Δεν υπάρχει κάρτα SIM στο tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Δεν υπάρχει κάρτα SIM στη συσκευή σας Android TV."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Ανάπτυξη περιοχής ξεκλειδώματος."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Ξεκλείδωμα ολίσθησης."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Ξεκλείδωμα μοτίβου."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face unlock."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Ξεκλείδωμα με το πρόσωπο."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Ξεκλείδωμα κωδικού ασφαλείας"</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Ξεκλείδωμα αριθμού PIN κάρτας SIM."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Ξεκλείδωμα αριθμού PUK κάρτας SIM."</string>
@@ -1869,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ενημερώθηκε από τον διαχειριστή σας"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Διαγράφηκε από τον διαχειριστή σας"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Η Εξοικονόμηση μπαταρίας ενεργοποιεί το Σκούρο θέμα και περιορίζει ή απενεργοποιεί τη δραστηριότητα στο παρασκήνιο, ορισμένα οπτικά εφέ, συγκεκριμένες λειτουργίες και ορισμένες συνδέσεις δικτύου.\n\n"<annotation id="url">"Μάθετε περισσότερα"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Η Εξοικονόμηση μπαταρίας ενεργοποιεί το Σκούρο θέμα και περιορίζει ή απενεργοποιεί τη δραστηριότητα στο παρασκήνιο, ορισμένα οπτικά εφέ, συγκεκριμένες λειτουργίες και ορισμένες συνδέσεις δικτύου."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Προκειμένου να μειωθεί η χρήση δεδομένων, η Εξοικονόμηση δεδομένων αποτρέπει την αποστολή ή λήψη δεδομένων από ορισμένες εφαρμογές στο παρασκήνιο. Μια εφαρμογή που χρησιμοποιείτε αυτήν τη στιγμή μπορεί να χρησιμοποιήσει δεδομένα αλλά με μικρότερη συχνότητα. Για παράδειγμα, οι εικόνες μπορεί να μην εμφανίζονται μέχρι να τις πατήσετε."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ενεργ.Εξοικονόμησης δεδομένων;"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ενεργοποίηση"</string>
@@ -1975,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Η εφαρμογή <xliff:g id="APP_NAME_0">%1$s</xliff:g> δεν είναι διαθέσιμη αυτήν τη στιγμή. Η διαχείριση πραγματοποιείται από την εφαρμογή <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Μάθετε περισσότερα"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Κατάργηση παύσης εφαρμογής"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Ενεργοπ. εφαρμογών εργασιών;"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Αποκτήστε πρόσβαση στις εφαρμογές εργασιών και τις ειδοποιήσεις"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Ενεργοποίηση"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Η εφαρμογή δεν είναι διαθέσιμη"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν είναι διαθέσιμη αυτήν τη στιγμή."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 2246e95..953fa17 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"manage face unlock hardware"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"manage Face Unlock hardware"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Allows the app to invoke methods to add and delete facial templates for use."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"use face unlock hardware"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Allows the app to use face unlock hardware for authentication"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"use Face Unlock hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Allows the app to use Face Unlock hardware for authentication"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Re-enrol your face"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"To improve recognition, please re-enrol your face"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Set up face unlock"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Can’t verify face. Hardware not available."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Try face unlock again."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Try Face Unlock again."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Can’t store new face data. Delete an old one first."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Face operation cancelled."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Face unlock cancelled by user."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Face Unlock cancelled by user."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Too many attempts. Try again later."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Too many attempts. Face unlock disabled."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Too many attempts. Face Unlock disabled."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Can’t verify face. Try again."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"You haven’t set up face unlock."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Face unlock is not supported on this device."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"You haven’t set up Face Unlock."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Face Unlock is not supported on this device."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor temporarily disabled."</string>
<string name="face_name_template" msgid="3877037340223318119">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Use face unlock"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Use Face Unlock"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Use face or screen lock"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Use your face to continue"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Use your face or screen lock to continue"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Unlock for all features and data"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum face unlock attempts exceeded"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum Face Unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"No SIM card in tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"No SIM card in your Android TV device."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Expand unlock area."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Slide unlock."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Pattern unlock."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face unlock."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face Unlock."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Pin unlock."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM PIN unlock."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM PUK unlock."</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 33c6d71..b3b1ff5 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"manage face unlock hardware"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"manage Face Unlock hardware"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Allows the app to invoke methods to add and delete facial templates for use."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"use face unlock hardware"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Allows the app to use face unlock hardware for authentication"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"use Face Unlock hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Allows the app to use Face Unlock hardware for authentication"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Re-enrol your face"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"To improve recognition, please re-enrol your face"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Set up face unlock"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Can’t verify face. Hardware not available."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Try face unlock again."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Try Face Unlock again."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Can’t store new face data. Delete an old one first."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Face operation cancelled."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Face unlock cancelled by user."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Face Unlock cancelled by user."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Too many attempts. Try again later."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Too many attempts. Face unlock disabled."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Too many attempts. Face Unlock disabled."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Can’t verify face. Try again."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"You haven’t set up face unlock."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Face unlock is not supported on this device."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"You haven’t set up Face Unlock."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Face Unlock is not supported on this device."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor temporarily disabled."</string>
<string name="face_name_template" msgid="3877037340223318119">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Use face unlock"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Use Face Unlock"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Use face or screen lock"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Use your face to continue"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Use your face or screen lock to continue"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Unlock for all features and data"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum face unlock attempts exceeded"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum Face Unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"No SIM card in tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"No SIM card in your Android TV device."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Expand unlock area."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Slide unlock."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Pattern unlock."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face unlock."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face Unlock."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Pin unlock."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM PIN unlock."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM PUK unlock."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index a08ad1d..0c60a02 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"manage face unlock hardware"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"manage Face Unlock hardware"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Allows the app to invoke methods to add and delete facial templates for use."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"use face unlock hardware"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Allows the app to use face unlock hardware for authentication"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"use Face Unlock hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Allows the app to use Face Unlock hardware for authentication"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Re-enrol your face"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"To improve recognition, please re-enrol your face"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Set up face unlock"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Can’t verify face. Hardware not available."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Try face unlock again."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Try Face Unlock again."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Can’t store new face data. Delete an old one first."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Face operation cancelled."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Face unlock cancelled by user."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Face Unlock cancelled by user."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Too many attempts. Try again later."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Too many attempts. Face unlock disabled."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Too many attempts. Face Unlock disabled."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Can’t verify face. Try again."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"You haven’t set up face unlock."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Face unlock is not supported on this device."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"You haven’t set up Face Unlock."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Face Unlock is not supported on this device."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor temporarily disabled."</string>
<string name="face_name_template" msgid="3877037340223318119">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Use face unlock"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Use Face Unlock"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Use face or screen lock"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Use your face to continue"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Use your face or screen lock to continue"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Unlock for all features and data"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum face unlock attempts exceeded"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum Face Unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"No SIM card in tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"No SIM card in your Android TV device."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Expand unlock area."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Slide unlock."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Pattern unlock."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face unlock."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face Unlock."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Pin unlock."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM PIN unlock."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM PUK unlock."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 44eba89..3830047 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"manage face unlock hardware"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"manage Face Unlock hardware"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Allows the app to invoke methods to add and delete facial templates for use."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"use face unlock hardware"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Allows the app to use face unlock hardware for authentication"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"use Face Unlock hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Allows the app to use Face Unlock hardware for authentication"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Re-enrol your face"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"To improve recognition, please re-enrol your face"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Set up face unlock"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Can’t verify face. Hardware not available."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Try face unlock again."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Try Face Unlock again."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Can’t store new face data. Delete an old one first."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Face operation cancelled."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Face unlock cancelled by user."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Face Unlock cancelled by user."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Too many attempts. Try again later."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Too many attempts. Face unlock disabled."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Too many attempts. Face Unlock disabled."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Can’t verify face. Try again."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"You haven’t set up face unlock."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Face unlock is not supported on this device."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"You haven’t set up Face Unlock."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Face Unlock is not supported on this device."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor temporarily disabled."</string>
<string name="face_name_template" msgid="3877037340223318119">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Use face unlock"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Use Face Unlock"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Use face or screen lock"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Use your face to continue"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Use your face or screen lock to continue"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Unlock for all features and data"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum face unlock attempts exceeded"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maximum Face Unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"No SIM card in tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"No SIM card in your Android TV device."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Expand unlock area."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Slide unlock."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Pattern unlock."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face unlock."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face Unlock."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Pin unlock."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM PIN unlock."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM PUK unlock."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 624a678..15b1ec8 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -655,7 +655,7 @@
<string name="face_error_security_update_required" msgid="5076017208528750161">"Se inhabilitó temporalmente el sensor."</string>
<string name="face_name_template" msgid="3877037340223318119">"Rostro <xliff:g id="FACEID">%d</xliff:g>"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"Usar desbloqueo facial"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usar bloqueo facial o de pantalla"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usar desbloqueo facial o de pantalla"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Usa el rostro para continuar"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Usa tu rostro o bloqueo de pantalla para continuar"</string>
<string-array name="face_error_vendor">
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 00a7522..5b147b4 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icono de huella digital"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"gestionar el hardware de desbloqueo facial"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"gestionar el hardware de Desbloqueo facial"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Permite que la app use métodos para añadir y suprimir plantillas de caras para su uso."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"Utilizar hardware de desbloqueo facial"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Permite que la aplicación utilice el hardware de desbloqueo facial para autenticarte"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"utilizar hardware de Desbloqueo facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Permite que la aplicación utilice el hardware de Desbloqueo facial para autenticarte"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Desbloqueo facial"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Volver a registrar la cara"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Para mejorar el reconocimiento, vuelve a registrar tu cara"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Configura el desbloqueo facial"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Configura Desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloqueo"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca para añadir una huella digital"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"No se puede verificar. Hardware no disponible."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Vuelve a probar el desbloqueo facial."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Vuelve a probar Desbloqueo facial."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Para guardar nuevos datos faciales, borra otros antiguos."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Se ha cancelado el reconocimiento facial."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"El usuario ha cancelado el desbloqueo facial."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"El usuario ha cancelado Desbloqueo facial."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Demasiados intentos. Inténtalo de nuevo más tarde."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Muchos intentos. Se ha inhabilitado el desbloqueo facial."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Muchos intentos. Se ha inhabilitado Desbloqueo facial."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"No se ha verificado tu cara. Vuelve a intentarlo."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"No has configurado el desbloqueo facial."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"El desbloqueo facial no está disponible en este dispositivo."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"No has configurado Desbloqueo facial."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Desbloqueo facial no está disponible en este dispositivo."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"El sensor está inhabilitado en estos momentos."</string>
<string name="face_name_template" msgid="3877037340223318119">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Usar desbloqueo facial"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usar desbloqueo facial o bloqueo de pantalla"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Usar Desbloqueo facial"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usar Desbloqueo facial o Bloqueo de pantalla"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Usa tu cara para continuar"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Usa tu cara o tu bloqueo de pantalla para continuar"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Vuelve a intentarlo"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Vuelve a intentarlo"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Desbloquear para todos los datos y funciones"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Se ha superado el número máximo de intentos de desbloqueo facial."</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Se ha superado el número máximo de intentos de Desbloqueo facial."</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Falta la tarjeta SIM."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"No se ha insertado ninguna tarjeta SIM en el tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"No hay ninguna tarjeta SIM en tu dispositivo Android TV."</string>
@@ -1271,12 +1271,12 @@
<string name="new_app_action" msgid="547772182913269801">"Abrir <xliff:g id="NEW_APP">%1$s</xliff:g>"</string>
<string name="new_app_description" msgid="1958903080400806644">"<xliff:g id="OLD_APP">%1$s</xliff:g> se cerrará sin guardar"</string>
<string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> ha superado el límite de memoria"</string>
- <string name="dump_heap_ready_notification" msgid="2302452262927390268">"El volcado de pila <xliff:g id="PROC">%1$s</xliff:g> está listo"</string>
- <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Se ha recopilado un volcado de pila. Toca para compartir."</string>
- <string name="dump_heap_title" msgid="4367128917229233901">"¿Compartir volcado de pila?"</string>
- <string name="dump_heap_text" msgid="1692649033835719336">"El proceso <xliff:g id="PROC">%1$s</xliff:g> ha superado su límite de memoria de <xliff:g id="SIZE">%2$s</xliff:g>. Hay un volcado de pila disponible que puedes compartir con su desarrollador (ten cuidado, ya que puede incluir información personal a la que tenga acceso la aplicación)."</string>
- <string name="dump_heap_system_text" msgid="6805155514925350849">"El proceso <xliff:g id="PROC">%1$s</xliff:g> ha superado su límite de memoria de <xliff:g id="SIZE">%2$s</xliff:g>. Hay un volcado de pila disponible que puedes compartir. Ten cuidado, ya que puede incluir información personal sensible a la que el proceso puede acceder, como texto que hayas introducido."</string>
- <string name="dump_heap_ready_text" msgid="5849618132123045516">"Hay un volcado de pila del proceso <xliff:g id="PROC">%1$s</xliff:g> que puedes compartir. Ten cuidado, ya que puede incluir información personal sensible a la que el proceso puede acceder, como texto que hayas introducido."</string>
+ <string name="dump_heap_ready_notification" msgid="2302452262927390268">"El volcado de montículo <xliff:g id="PROC">%1$s</xliff:g> está listo"</string>
+ <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Se ha recopilado un volcado de montículo. Toca para compartir."</string>
+ <string name="dump_heap_title" msgid="4367128917229233901">"¿Compartir volcado de montículo?"</string>
+ <string name="dump_heap_text" msgid="1692649033835719336">"El proceso <xliff:g id="PROC">%1$s</xliff:g> ha superado su límite de memoria de <xliff:g id="SIZE">%2$s</xliff:g>. Hay un volcado de montículo disponible que puedes compartir con su desarrollador (ten cuidado, ya que puede incluir información personal a la que tenga acceso la aplicación)."</string>
+ <string name="dump_heap_system_text" msgid="6805155514925350849">"El proceso <xliff:g id="PROC">%1$s</xliff:g> ha superado su límite de memoria de <xliff:g id="SIZE">%2$s</xliff:g>. Hay un volcado de montículo disponible que puedes compartir. Ten cuidado, ya que puede incluir información personal sensible a la que el proceso puede acceder, como texto que hayas introducido."</string>
+ <string name="dump_heap_ready_text" msgid="5849618132123045516">"Hay un volcado de montículo del proceso <xliff:g id="PROC">%1$s</xliff:g> que puedes compartir. Ten cuidado, ya que puede incluir información personal sensible a la que el proceso puede acceder, como texto que hayas introducido."</string>
<string name="sendText" msgid="493003724401350724">"Selecciona una acción para el texto"</string>
<string name="volume_ringtone" msgid="134784084629229029">"Volumen del timbre"</string>
<string name="volume_music" msgid="7727274216734955095">"Volumen de multimedia"</string>
@@ -1869,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado por el administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado por el administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"El modo Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red.\n\n"<annotation id="url">"Más información"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"El modo Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano, lo que puede reducir el uso de datos. Una aplicación que estés usando de forma activa puede acceder a los datos, aunque con menos frecuencia. Esto significa que es posible que, por ejemplo, algunas imágenes no se muestren hasta que las toques."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"¿Activar Ahorro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
@@ -1975,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> no está disponible en este momento. Esta opción se administra en <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Más información"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Anular pausa de aplicación"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"¿Activar apps de trabajo?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Accede a tus aplicaciones y notificaciones de trabajo"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activar"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"La aplicación no está disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"En estos momentos, <xliff:g id="APP_NAME">%1$s</xliff:g> no está disponible."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index dc56e12..d74cca6 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Sõrmejälje ikoon"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"hallata Face Unlocki riistvara"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"hallata näoga avamise riistvara"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Lubab rakendusel tühistada meetodid kasutatavate näomallide lisamiseks ja kustutamiseks."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"kasutada Face Unlocki riistvara"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Võimaldab rakendusel autentimiseks kasutada Face Unlocki riistvara"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face Unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"kasutada näoga avamise riistvara"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Võimaldab rakendusel autentimiseks kasutada näoga avamise riistvara"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Näoga avamine"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Registreerige oma nägu uuesti"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Tuvastamise parandamiseks registreerige oma nägu uuesti"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Seadistage Face Unlock"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Seadistage näoga avamine"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Avage telefon seda vaadates"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Seadistage rohkem viise avamiseks"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Puudutage sõrmejälje lisamiseks"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Nägu ei saa kinnitada. Riistvara pole saadaval."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Proovige Face Unlocki uuesti."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Proovige näoga avamist uuesti."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Uue näo andmeid ei saa salvestada. Kustutage enne vanad."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Näotuvastuse toiming tühistati."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Kasutaja tühistas Face Unlocki."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Kasutaja tühistas näoga avamise."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Liiga palju katseid. Proovige hiljem uuesti."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Liiga palju katseid. Face Unlock on keelatud."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Liiga palju katseid. Näoga avamine on keelatud."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Nägu ei saa kinnitada. Proovige uuesti."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Face Unlocki ei ole seadistatud."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Seade ei toeta Face Unlocki."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Näoga avamine ei ole seadistatud."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Seade ei toeta näoga avamist."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Andur on ajutiselt keelatud."</string>
<string name="face_name_template" msgid="3877037340223318119">"Nägu <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Face Unlocki kasutamine"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Face Unlocki või ekraaniluku kasutamine"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Näoga avamise kasutamine"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Näoga avamise või ekraaniluku kasutamine"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Jätkamiseks kasutage oma nägu"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Jätkamiseks kasutage oma nägu või ekraanilukku"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Proovige uuesti"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Proovige uuesti"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Ava kõigi funktsioonide ja andmete nägemiseks"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maksimaalne teenusega Face Unlock avamise katsete arv on ületatud"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Maksimaalne näoga avamise katsete arv on ületatud"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM-kaarti pole"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Tahvelarvutis pole SIM-kaarti."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Teie Android TV seadmes pole SIM-kaarti."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Lõikelauale kopeeritud tekst."</string>
<string name="copied" msgid="4675902854553014676">"Kopeeritud"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kleepis rakendusest <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kleepis teie lõikelaualt"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kleepis teie kopeeritud teksti"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kleepis teie kopeeritud pildi"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kleepis teie kopeeritud sisu"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> ettevalmistamine."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Rakenduste käivitamine."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Käivitamise lõpuleviimine."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Kas lülitada ekraan välja?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Sõrmejälje seadistamisel vajutasite toitenuppu.\n\nSee lülitab ekraanikuva tavaliselt välja."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Lülita välja"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Tühista"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> töötab"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Puudutage mängu naasmiseks"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Valige mäng"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kasuta otseteed"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Värvide ümberpööramine"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Värvide korrigeerimine"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Ühekäerežiim"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Eriti tume"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Administraator on seda värskendanud"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Administraator on selle kustutanud"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Akusäästja lülitab sisse tumeda teema ja lülitab välja taustategevused, mõned visuaalsed efektid, teatud funktsioonid ja võrguühendused või piirab neid.\n\n"<annotation id="url">"Lisateave"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Akusäästja lülitab sisse tumeda teema ja lülitab välja taustategevused, mõned visuaalsed efektid, teatud funktsioonid ja võrguühendused või piirab neid."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Andmekasutuse vähendamiseks keelab andmemahu säästja mõne rakenduse puhul andmete taustal saatmise ja vastuvõtmise. Rakendus, mida praegu kasutate, pääseb andmesidele juurde, kuid võib seda teha väiksema sagedusega. Seetõttu võidakse näiteks pildid kuvada alles siis, kui neid puudutate."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Lülitada andmemahu säästja sisse?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Lülita sisse"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> pole praegu saadaval. Seda haldab rakendus <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Lisateave"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Jätka rakendust"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Lülitada töörakendused sisse?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Hankige juurdepääs oma töörakendustele ja märguannetele"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Lülita sisse"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Rakendus ei ole saadaval"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole praegu saadaval."</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 428832d..06e73b1 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -42,7 +42,7 @@
<string name="serviceErased" msgid="997354043770513494">"Behar bezala ezabatu da."</string>
<string name="passwordIncorrect" msgid="917087532676155877">"Pasahitz okerra."</string>
<string name="mmiComplete" msgid="6341884570892520140">"MMI osatu da."</string>
- <string name="badPin" msgid="888372071306274355">"Idatzi duzun PIN kode zaharra ez da zuzena."</string>
+ <string name="badPin" msgid="888372071306274355">"Idatzi duzun PIN zaharra ez da zuzena."</string>
<string name="badPuk" msgid="4232069163733147376">"Idatzi duzun PUK kode zaharra ez da zuzena. Saiatu berriro."</string>
<string name="mismatchPin" msgid="2929611853228707473">"Idatzi dituzun PIN kodeak ez datoz bat."</string>
<string name="invalidPin" msgid="7542498253319440408">"Idatzi 4 eta 8 zenbaki bitarteko PIN bat."</string>
@@ -906,7 +906,7 @@
<string name="emergency_calls_only" msgid="3057351206678279851">"Larrialdi-deiak soilik"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Sarea blokeatuta dago"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"SIM txartela PUK bidez blokeatuta dago."</string>
- <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Ikusi erabiltzailearen gida edo jarri bezeroarentzako laguntza-zerbitzuarekin harremanetan."</string>
+ <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Ikusi erabiltzailearentzako gida edo jarri bezeroarentzako laguntza-zerbitzuarekin harremanetan."</string>
<string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"SIM txartela blokeatuta dago."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"SIM txartela desblokeatzen…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%2$d</xliff:g> segundo barru."</string>
@@ -1869,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Administratzaileak eguneratu du"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Administratzaileak ezabatu du"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Ados"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Bateria-aurrezleak gai iluna aktibatzen du, eta murriztu edo desaktibatu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual, eta eginbide jakin eta sareko konexio batzuk.\n\n"<annotation id="url">"Lortu informazio gehiago"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Bateria-aurrezleak gai iluna aktibatzen du, eta murriztu edo desaktibatu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual, eta eginbide jakin eta sareko konexio batzuk."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Datuen erabilera murrizteko, atzeko planoan datuak bidaltzea eta jasotzea galarazten die datu-aurrezleak aplikazio batzuei. Une honetan erabiltzen ari zaren aplikazio batek datuak atzitu ahal izango ditu, baina baliteke maiztasun txikiagoarekin atzitzea. Horrela, adibidez, baliteke irudiak ez erakustea haiek sakatu arte."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Datu-aurrezlea aktibatu nahi duzu?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktibatu"</string>
@@ -1975,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ez dago erabilgarri une honetan. Haren erabilgarritasuna <xliff:g id="APP_NAME_1">%2$s</xliff:g> aplikazioak kudeatzen du."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Lortu informazio gehiago"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Kendu pausaldia"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Laneko aplikazioak aktibatu nahi dituzu?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Atzitu laneko aplikazioak eta jakinarazpenak"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktibatu"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikazioa ez dago erabilgarri"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ez dago erabilgarri une honetan."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b66ad5a..a74b62a 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"نماد اثر انگشت"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"مدیریت سختافزار «بازگشایی با چهره»"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"مدیریت سختافزار «قفلگشایی با چهره»"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"به برنامه امکان میدهد روشهایی را برای افزودن و حذف الگوهای چهره جهت استفاده فرابخواند."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"استفاده از سختافزار «بازگشایی با چهره»"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"به برنامه امکان میدهد از سختافزار «بازگشایی با چهره» برای اصالتسنجی استفاده کند"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"بازگشایی با چهره"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"استفاده از سختافزار «قفلگشایی با چهره»"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"به برنامه امکان میدهد از سختافزار «قفلگشایی با چهره» برای اصالتسنجی استفاده کند"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"قفلگشایی با چهره"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ثبت مجدد چهره"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"برای بهبود تشخیص، لطفاً چهرهتان را دوباره ثبت کنید"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"راهاندازی «بازگشایی با چهره»"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"راهاندازی «قفلگشایی با چهره»"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"برای باز کردن قفل تلفن خود به آن نگاه کنید"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"راهاندازی روشهای بیشتر برای باز کردن قفل"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"برای افزودن اثر انگشت، ضربه بزنید"</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"چهره تأیید نشد. سختافزار در دسترس نیست."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"«بازگشایی با چهره» را دوباره امتحان کنید."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"«قفلگشایی با چهره» را دوباره امتحان کنید."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"داده چهره جدید ذخیره نشد. اول داده چهره قدیمی را حذف کنید."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"عملیات شناسایی چهره لغو شد."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"کاربر «بازگشایی با چهره» را لغو کرد."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"کاربر «قفلگشایی با چهره» را لغو کرد."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"تعداد زیادی تلاش ناموفق. بعداً دوباره امتحان کنید."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"تعداد تلاشها بیشازحد مجاز است. «بازگشایی با چهره» غیرفعال است."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"تعداد تلاشها بیشازحد مجاز است. «قفلگشایی با چهره» غیرفعال است."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"چهره تأیید نشد. دوباره امتحان کنید."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"«بازگشایی با چهره» را راهاندازی نکردهاید."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"«بازگشایی با چهره» در این دستگاه پشتیبانی نمیشود."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"«قفلگشایی با چهره» را راهاندازی نکردهاید."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"«قفلگشایی با چهره» در این دستگاه پشتیبانی نمیشود."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"حسگر بهطور موقت غیرفعال است."</string>
<string name="face_name_template" msgid="3877037340223318119">"چهره <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"استفاده از «بازگشایی با چهره»"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"استفاده از «قفلگشایی با چهره»"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"استفاده از قفل صفحه یا چهره"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"برای ادامه، از چهرهتان استفاده کنید"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"برای ادامه، از تشخیص چهره یا قفل صفحه استفاده کنید"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"دوباره امتحان کنید"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"دوباره امتحان کنید"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"باز کردن قفل تمام قابلیتها و دادهها"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"دفعات تلاش برای «بازگشایی با چهره» از حداکثر مجاز بیشتر شد"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"دفعات تلاش برای «قفلگشایی با چهره» از حداکثر مجاز بیشتر شد"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"سیم کارت موجود نیست"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"سیم کارت درون رایانهٔ لوحی نیست."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"هیچ سیمکارتی در دستگاه Android TV شما قرار داده نشده است."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"گسترده کردن منطقه بازگشایی شده."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"باز کردن قفل با کشیدن انگشت روی صفحه."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"باز کردن قفل با الگو."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"بازگشایی با چهره."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"قفلگشایی با چهره."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"باز کردن قفل با پین."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"قفل پین سیمکارت باز شد."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"قفل Puk سیمکارت باز شد."</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استفاده از میانبر"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"وارونگی رنگ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"تصحیح رنگ"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"حالت تکحرکت"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"بسیار کمنور"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> روشن شد."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 270d594a..5655d10 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Sormenjälkikuvake"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"hallinnoida Face Unlock ‑laitteistoa"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"hallinnoida kasvojentunnistusavauksen laitteistoa"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Sallii sovelluksen käyttää menetelmiä, joilla voidaan lisätä tai poistaa kasvomalleja."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"käyttää Face Unlock ‑laitteistoa"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Sallii sovelluksen käyttää Face Unlock ‑laitteistoa todennukseen"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face Unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"käyttää kasvojentunnistusavauksen laitteistoa"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Sallii sovelluksen käyttää kasvojentunnistusavauksen laitteistoa todennukseen"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Kasvojentunnistusavaus"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Lisää kasvot uudelleen"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Lisää kasvosi uudelleen tunnistamisen parantamiseksi"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Ota Face Unlock käyttöön"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Ota kasvojentunnistusavaus käyttöön"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Avaa puhelimesi lukitus katsomalla laitetta"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ota käyttöön lisää tapoja avata lukitus"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Napauta lisätäksesi sormenjälki"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Kasvoja ei voi vahvistaa. Laitteisto ei käytettäv."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Yritä käyttää Face Unlockia uudelleen."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Yritä käyttää kasvojentunnistusavausta uudelleen."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Uutta kasvodataa ei voi tallentaa. Poista ensin vanhaa."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Kasvotoiminto peruutettu"</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Käyttäjä peruutti Face Unlockin."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Käyttäjä peruutti kasvojentunnistusavauksen."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Liian monta yritystä. Yritä myöhemmin uudelleen."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Liian monta yritystä. Face Unlock poistettu käytöstä."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Liian monta yritystä. Kasvojentunnistusavaus poistettu käytöstä."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Kasvoja ei voi vahvistaa. Yritä uudelleen."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Et ole määrittänyt Face Unlockia."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Tämä laite ei tue Face Unlockia."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Et ole määrittänyt kasvojentunnistusavausta."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Tämä laite ei tue kasvojentunnistusavausta."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Tunnistin poistettu väliaikaisesti käytöstä."</string>
<string name="face_name_template" msgid="3877037340223318119">"Kasvot <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Käytä Face Unlockia"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Käytä Face Unlockia tai näytön lukitusta"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Käytä kasvojentunnistusavausta"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Käytä kasvojentunnistusavausta tai näytön lukitusta"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Jatka kasvojesi avulla"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Jatka kasvojentunnistuksen tai näytön lukituksen avulla"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Yritä uudelleen"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Yritä uudelleen"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Käytä kaikkia ominaisuuksia avaamalla lukitus."</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Face Unlock -yrityksiä tehty suurin sallittu määrä."</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Kasvojentunnistusavauksen yrityksiä tehty suurin sallittu määrä."</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Ei SIM-korttia"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Tablet-laitteessa ei ole SIM-korttia."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Android TV ‑laitteessa ei ole SIM-korttia."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Laajenna lukituksen poiston aluetta."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Lukituksen poisto liu\'uttamalla."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Lukituksen poisto salasanalla."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face Unlock"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Kasvojentunnistusavaus"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Lukituksen poisto PIN-koodilla."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM-kortin PIN-koodin lukituksen avaus"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM-kortin PUK-koodin lukituksen avaus"</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Teksti kopioitu leikepöydälle."</string>
<string name="copied" msgid="4675902854553014676">"Kopioitu"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> liitetty täältä: <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> liitti leikepöydältäsi"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> on liittänyt kopioimasi tekstin"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> on liittänyt kopioimasi kuvan"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> on liittänyt kopioimasi sisällön"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Valmistellaan: <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Käynnistetään sovelluksia."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Viimeistellään päivitystä."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Sammutetaanko näyttö?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Painoit virtapainiketta ottaessasi sormenjäljen käyttöön.\n\nTämä saa yleensä näytön sammumaan."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Laita pois päältä"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Peru"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> käynnissä"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Palaa peliin napauttamalla"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Valitse peli"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Käytä pikanäppäintä"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Käänteiset värit"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Värinkorjaus"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Yhden käden moodi"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Erittäin himmeä"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin päälle."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin pois päältä."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Järjestelmänvalvoja päivitti tämän."</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Järjestelmänvalvoja poisti tämän."</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, tiettyjä ominaisuuksia sekä joitakin visuaalisia tehosteita ja verkkoyhteyksiä.\n\n"<annotation id="url">"Lue lisää"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, tiettyjä ominaisuuksia sekä joitakin visuaalisia tehosteita ja verkkoyhteyksiä."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Data Saver estää joitakin sovelluksia lähettämästä tai vastaanottamasta tietoja taustalla, jotta datan käyttöä voidaan vähentää. Käytössäsi oleva sovellus voi yhä käyttää dataa, mutta se saattaa tehdä niin tavallista harvemmin. Tämä voi tarkoittaa esimerkiksi sitä, että kuva ladataan vasta, kun kosketat sitä."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Otetaanko Data Saver käyttöön?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ota käyttöön"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ei ole juuri nyt saatavilla. Tästä vastaa <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Lue lisää"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Peru keskeytys"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Käytetäänkö työsovelluksia?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Palauta työsovellukset ja ilmoitukset käyttöön"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Ota käyttöön"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Sovellus ei ole käytettävissä"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole nyt käytettävissä."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index d717f5b..ec73dad 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -612,7 +612,7 @@
<string name="permlab_manageFace" msgid="4569549381889283282">"gérer le matériel de déverrouillage par reconnaissance faciale"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Permet à l\'appli d\'employer des méthodes d\'aj. et de suppr. de modèles de reconn. visage."</string>
<string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"utiliser le matériel de déverrouillage par reconnaissance faciale"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Permet à l\'appli d\'utiliser du matériel de déverr. par reconn faciale pour l\'authentific."</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Permet à l\'appli d\'utiliser du matériel de déverr. par reconn. faciale pour l\'authentific."</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Déverrouillage par reconnaissance faciale"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Inscrivez votre visage à nouveau"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Pour améliorer la reconnaissance, veuillez enregistrer à nouveau votre visage"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Réessayer"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Réessayer"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Déverr. pour acc. aux autres fonction. et données"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Nombre maximal autorisé de tentatives Face Unlock atteint."</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Nombre maximal atteint de tentatives de déverrouillage par reconnaissance faciale"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Aucune carte SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Aucune carte SIM n\'est insérée dans la tablette."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Aucune carte SIM ne se trouve dans votre appareil Android TV."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Le texte a été copié dans le presse-papiers."</string>
<string name="copied" msgid="4675902854553014676">"Copié"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> collé à partir de <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé du contenu de votre presse-papiers"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé du texte que vous avez copié"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé une image que vous avez copiée"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé le contenu que vous avez copié"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Préparation de <xliff:g id="APPNAME">%1$s</xliff:g> en cours…"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Lancement des applications…"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalisation de la mise à jour."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Éteindre l\'écran?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pendant que vous configuriez votre empreinte digitale, vous avez appuyé sur l\'interrupteur.\n\nAppuyer sur ce bouton ferme habituellement l\'écran."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Éteindre"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annuler"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Touchez pour revenir au jeu"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choisissez un jeu"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode Une main"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Réduction supplémentaire de la luminosité"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Mise à jour par votre administrateur"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Le mode Économiseur de pile active le mode sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels, d\'autres fonctionnalités et certaines connexions réseau.\n\n"<annotation id="url">"En savoir plus"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Le mode Économiseur de pile active le thème sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels, certaines fonctionnalités et certaines connexions réseau."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Pour aider à diminuer l\'utilisation des données, la fonctionnalité Économiseur de données empêche certaines applications d\'envoyer ou de recevoir des données en arrière-plan. Une application que vous utilisez actuellement peut accéder à des données, mais peut le faire moins souvent. Cela peut signifier, par exemple, que les images ne s\'affichent pas jusqu\'à ce que vous les touchiez."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"L\'application <xliff:g id="APP_NAME_0">%1$s</xliff:g> n\'est pas accessible pour le moment. Ceci est géré par <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"En savoir plus"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Réactiver l\'application"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Activer applis professionnelles?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Accédez à vos applications professionnelles et à vos notifications"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activer"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"L\'application n\'est pas accessible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas accessible pour le moment."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 28990b4..22f699a 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icône d\'empreinte digitale"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"gérer les composants de Face Unlock"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"gérer le matériel de déverrouillage par authentification faciale"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Autorise l\'appli à invoquer des méthodes pour ajouter et supprimer des modèles de visages."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"utiliser les composants de Face Unlock"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Autorise l\'application à utiliser les composants de Face Unlock pour l\'authentification"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face Unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"utiliser le matériel de déverrouillage par authentification faciale"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Autorise l\'appli à utiliser le matériel de déverrouillage par authentification faciale"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Déverrouillage par authentification faciale"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Enregistrer à nouveau votre visage"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Pour améliorer la reconnaissance, veuillez enregistrer à nouveau votre visage"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Configurer Face Unlock"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Configurer le déverrouillage par authentification faciale"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Appuyez pour ajouter une empreinte digitale"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Imposs. valider visage. Matériel non disponible."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Réessayez d\'activer Face Unlock."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Réessayez d\'activer le déverrouillage."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Impossible stocker nouv. visages. Veuillez en supprimer un."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Opération de reconnaissance faciale annulée."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Face Unlock annulé par l\'utilisateur."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Déverrouillage par authentification faciale annulé par l\'utilisateur"</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Trop de tentatives. Réessayez plus tard."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Tentatives trop nombreuses. Désactivation de Face Unlock."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Trop de tentatives. Déverrouillage par authentification faciale désactivé."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Impossible de valider votre visage. Réessayez."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Face Unlock n\'est pas configuré."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Face Unlock n\'est pas compatible avec cet appareil."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Déverrouillage par authentification faciale non configuré"</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Déverrouillage par authentification faciale non compatible"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Capteur temporairement désactivé."</string>
<string name="face_name_template" msgid="3877037340223318119">"Visage <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Utiliser Face Unlock"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Utiliser Face Lock ou le verrouillage de l\'écran"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Utiliser déverrouillage facial"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Utiliser déverrouillage par authent. faciale ou verrouillage écran"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Utilisez la reconnaissance faciale pour continuer"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Utilisez la reconnaissance faciale ou le verrouillage de l\'écran pour continuer"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Veuillez réessayer."</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Veuillez réessayer."</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Déverr. pour autres fonctionnalités et données"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Nombre maximal autorisé de tentatives Face Unlock atteint."</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Nombre maximal de tentatives de déverrouillage par authentification faciale atteint"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Pas de carte SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Aucune carte SIM n\'est insérée dans la tablette."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Aucune carte SIM n\'est installée dans votre appareil Android TV."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Développer la zone de déverrouillage"</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Déverrouillage en faisant glisser votre doigt sur l\'écran"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Déverrouillage par schéma"</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Déverrouillage par reconnaissance faciale"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Déverrouillage par authentification faciale"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Déverrouillage par code PIN"</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Déverrouillage de la carte SIM à l\'aide d\'un code."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Déverrouillage de la carte SIM à l\'aide d\'une clé PUK."</string>
@@ -1869,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Mis à jour par votre administrateur"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"L\'économiseur de batterie active le thème sombre et limite ou désactive les activités en arrière-plan ainsi que certains effets visuels, fonctionnalités et connexions réseau.\n\n"<annotation id="url">"En savoir plus"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"L\'économiseur de batterie active le thème sombre et limite ou désactive les activités en arrière-plan ainsi que certains effets visuels, fonctionnalités et connexions réseau."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation des données, l\'Économiseur de données empêche certaines applis d\'envoyer ou de recevoir des données en arrière-plan. Les applis que vous utiliserez pourront toujours accéder aux données, mais le feront moins fréquemment. Par exemple, les images pourront ne pas s\'afficher tant que vous n\'aurez pas appuyé pas dessus."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string>
@@ -1975,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"L\'application <xliff:g id="APP_NAME_0">%1$s</xliff:g> n\'est pas disponible pour le moment. Cette suspension est gérée par l\'application <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"En savoir plus"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Débloquer l\'application"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Activer les applis pros ?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Accéder à vos applis et notifications professionnelles"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activer"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Application non disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas disponible pour le moment."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index e2bd3cb..bbc868f 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1869,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado polo teu administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado polo teu administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Coa función Aforro de batería, actívase o tema escuro e restrínxense ou desactívanse a actividade en segundo plano, algúns efectos visuais e determinadas funcións e conexións de rede.\n\n"<annotation id="url">"Máis información"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Coa función Aforro de batería, actívase o tema escuro e restrínxense ou desactívanse a actividade en segundo plano, algúns efectos visuais e determinadas funcións e conexións de rede."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Para contribuír a reducir o uso de datos, o aforro de datos impide que algunhas aplicacións envíen ou reciban datos en segundo plano. Cando esteas utilizando unha aplicación, esta poderá acceder aos datos, pero é posible que o faga con menos frecuencia. Por exemplo, poida que as imaxes non se mostren ata que as toques."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Queres activar o aforro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
@@ -1975,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"A aplicación <xliff:g id="APP_NAME_0">%1$s</xliff:g> non está dispoñible neste momento. A dispoñibilidade está xestionada por <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Máis información"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Volver activar aplicación"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Activar as apps do traballo?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Obtén acceso ás túas aplicacións e notificacións do traballo"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activar"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"A aplicación non está dispoñible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> non está dispoñible neste momento."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 4f32f29..6bf1ff8 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"ક્લિપબોર્ડ પર ટેક્સ્ટ કૉપિ કરી."</string>
<string name="copied" msgid="4675902854553014676">"કૉપિ કરેલ"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>માંથી કૉપિ કરાયેલો ડેટા <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>માં પેસ્ટ કરવામાં આવ્યો"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> દ્વારા તમારા ક્લિપબોર્ડ પરથી પેસ્ટ કરવામાં આવ્યું"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> દ્વારા તમે કૉપિ કરેલી ટેક્સ્ટ પેસ્ટ કરાઈ"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> દ્વારા તમે કૉપિ કરેલી છબી પેસ્ટ કરાઈ"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> દ્વારા તમે કૉપિ કરેલું કન્ટેન્ટ પેસ્ટ કરાયું"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> તૈયાર કરી રહ્યું છે."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ઍપ્લિકેશનો શરૂ કરી રહ્યાં છે."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"બૂટ સમાપ્ત કરી રહ્યાં છે."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"સ્ક્રીન બંધ કરીએ?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"તમારી ફિંગરપ્રિન્ટની સેટિંગનું સેટઅપ કરતી વખતે, તમે પાવર બટન દબાવ્યું.\n\nઆનાથી સામાન્ય રીતે તમારી સ્ક્રીન બંધ થઈ જાય છે."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"બંધ કરો"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"રદ કરો"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ચાલુ છે"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ગેમ પર પાછા આવવા માટે ટૅપ કરો"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ગેમ પસંદ કરો"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"શૉર્ટકટનો ઉપયોગ કરો"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"વિપરીત રંગમાં બદલવું"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"રંગ સુધારણા"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"એક-હાથે વાપરો મોડ"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"એક્સ્ટ્રા ડિમ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b4594e1..422b4fa 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -611,9 +611,9 @@
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फ़िंगरप्रिंट आइकॉन"</string>
<string name="permlab_manageFace" msgid="4569549381889283282">"\'मालिक का चेहरा पहचानकर अनलॉक\' वाला हार्डवेयर प्रबंधित करें"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"ऐप्लिकेशन को चेहरे के टेम्पलेट इस्तेमाल के तरीके जोड़ने और मिटाने की मंज़ूरी मिलती है."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"\'मालिक का चेहरा पहचानकर अनलॉक\' वाला हार्डवेयर इस्तेमाल करें"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"ऐप्लिकेशन को \'मालिक का चेहरा पहचानकर अनलॉक\' वाले हार्डवेयर के इस्तेमाल की मंज़ूरी देता है"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"मालिक का चेहरा पहचानकर अनलॉक"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"फ़ेस अनलॉक हार्डवेयर इस्तेमाल करें"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"फ़ेस अनलॉक हार्डवेयर के इस्तेमाल की मंज़ूरी देता है"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"फ़ेस अनलॉक"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"अपना चेहरा फिर से दर्ज करें"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"कृपया अपना चेहरा फिर से दर्ज करें ताकि आपको बेहतर तरीके से पहचाना जा सके"</string>
<string name="face_setup_notification_title" msgid="550617822603450009">"फ़ेस अनलॉक की सुविधा सेट अप करें"</string>
@@ -643,7 +643,7 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"चेहरा नहीं पहचान पा रहे. हार्डवेयर उपलब्ध नहीं है."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"\'मालिक का चेहरा पहचानकर अनलॉक\' फिर से आज़माएं."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"फ़ेस अनलॉक की सुविधा फिर से आज़माएं."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"चेहरे का नया डेटा सेव नहीं हो सकता. कोई पुराना डेटा मिटाएं."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"चेहरा पहचानने की कार्रवाई रद्द की गई."</string>
<string name="face_error_user_canceled" msgid="8553045452825849843">"उपयोगकर्ता ने \'मालिक का चेहरा पहचानकर अनलॉक\' रद्द की."</string>
@@ -651,7 +651,7 @@
<string name="face_error_lockout_permanent" msgid="8277853602168960343">"कई बार कोशिश की जा चुकी है. \'मालिक का चेहरा पहचानकर अनलॉक\' बंद है."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"चेहरा नहीं पहचान पा रहे. फिर से कोशिश करें."</string>
<string name="face_error_not_enrolled" msgid="7369928733504691611">"आपने \'मालिक का चेहरा पहचानकर अनलॉक\' सेट नहीं की है."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"इस डिवाइस पर \'मालिक का चेहरा पहचानकर अनलॉक\' काम नहीं करती है."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"इस डिवाइस पर फ़ेस अनलॉक की सुविधा काम नहीं करती है."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"सेंसर कुछ समय के लिए बंद कर दिया गया है."</string>
<string name="face_name_template" msgid="3877037340223318119">"चेहरा <xliff:g id="FACEID">%d</xliff:g>"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"\'फ़ेस अनलॉक\' इस्तेमाल करें"</string>
@@ -1869,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"आपके व्यवस्थापक ने अपडेट किया है"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"आपके व्यवस्थापक ने हटा दिया है"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ठीक है"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, यह बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, कुछ खास सुविधाओं, और कुछ खास तरह के इंटरनेट कनेक्शन इस्तेमाल करने से डिवाइस को रोकता है या इन्हें बंद कर देता है.\n\n"<annotation id="url">"ज़्यादा जानें"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, यह बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, कुछ खास सुविधाओं, और कुछ खास तरह के इंटरनेट कनेक्शन इस्तेमाल करने से डिवाइस को रोकता है या इन्हें बंद कर देता है."</string>
<string name="data_saver_description" msgid="4995164271550590517">"डेटा खर्च को कम करने के लिए, डेटा बचाने की सेटिंग कुछ ऐप्लिकेशन को बैकग्राउंड में डेटा भेजने या डेटा पाने से रोकती है. फ़िलहाल, आप जिस ऐप्लिकेशन का इस्तेमाल कर रहे हैं वह डेटा ऐक्सेस कर सकता है, लेकिन ऐसा कभी-कभी ही हो पाएगा. उदाहरण के लिए, इमेज तब तक दिखाई नहीं देंगी जब तक कि आप उन पर टैप नहीं करते."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा बचाने की सेटिंग चालू करें?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"चालू करें"</string>
@@ -1975,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"फ़िलहाल <xliff:g id="APP_NAME_0">%1$s</xliff:g> उपलब्ध नहीं है. इसे <xliff:g id="APP_NAME_1">%2$s</xliff:g> के ज़रिए प्रबंधित किया जाता है."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"ज़्यादा जानें"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ऐप्लिकेशन पर लगी रोक हटाएं"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन चालू करना चाहते हैं?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"अपने ऑफ़िस के काम से जुड़े ऐप्लिकेशन और सूचनाओं का ऐक्सेस पाएं"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"चालू करें"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ऐप्लिकेशन उपलब्ध नहीं है"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> इस समय उपलब्ध नहीं है."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 8759f7a..acccfa9 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -658,7 +658,7 @@
<string name="face_error_security_update_required" msgid="5076017208528750161">"Senzor je privremeno onemogućen."</string>
<string name="face_name_template" msgid="3877037340223318119">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"Upotreba otključavanja licem"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Upotreba lica ili zaključavanja zaslona"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Upotreba otključavanja licem ili zaključavanja zaslona"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Autentificirajte se licem da biste nastavili"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Za nastavak se identificirajte licem ili vjerodajnicom zaključavanja zaslona"</string>
<string-array name="face_error_vendor">
@@ -890,7 +890,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Pokušajte ponovo"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Pokušajte ponovo"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Otključajte za sve značajke i podatke"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Premašen je maksimalni broj Otključavanja licem"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Premašen je maksimalni broj pokušaja otključavanja licem"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Nema SIM kartice"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"U tabletnom uređaju nema SIM kartice."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Na Android TV uređaju nema SIM kartice."</string>
@@ -1892,10 +1892,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"U redu"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Štednja baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizualne efekte, određene značajke i neke mrežne veze.\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Štednja baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizualne efekte, određene značajke i neke mrežne veze."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjio podatkovni promet, značajka Štednja podatkovnog prometa onemogućuje nekim aplikacijama slanje ili primanje podataka u pozadini. Aplikacija koju trenutačno upotrebljavate može pristupiti podacima, no možda će to činiti rjeđe. To može značiti da se, na primjer, slike neće prikazivati dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Uključiti Štednju podatkovnog prometa?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string>
@@ -2007,10 +2005,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> trenutačno nije dostupna. Ovime upravlja aplikacija <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Saznajte više"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Prekini pauzu aplikacije"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Uključiti poslovne aplikacije?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Pristupite svojim poslovnim aplikacijama i obavijestima"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutačno nije dostupna."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index dd4afa53..4671f4a 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Billentyűparancs használata"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Színek invertálása"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Színkorrekció"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Egykezes mód"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extrasötét"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolva."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kikapcsolva."</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"A rendszergazda által frissítve"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"A rendszergazda által törölve"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Az Akkumulátorkímélő mód bekapcsolja a Sötét témát, és korlátozza vagy kikapcsolja a háttérbeli tevékenységeket, valamint bizonyos vizuális effekteket, funkciókat és hálózati kapcsolatokat.\n\n"<annotation id="url">"További információ"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Az Akkumulátorkímélő mód bekapcsolja a Sötét témát, és korlátozza vagy kikapcsolja a háttérbeli tevékenységeket, valamint bizonyos vizuális effekteket, funkciókat és hálózati kapcsolatokat."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Az adatforgalom csökkentése érdekében az Adatforgalom-csökkentő megakadályozza, hogy egyes alkalmazások adatokat küldjenek vagy fogadjanak a háttérben. Az Ön által jelenleg használt alkalmazások hozzáférhetnek az adatokhoz, de csak ritkábban. Ez például azt jelentheti, hogy a képek csak rákoppintás után jelennek meg."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Bekapcsolja az Adatforgalom-csökkentőt?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Bekapcsolás"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"A(z) <xliff:g id="APP_NAME_0">%1$s</xliff:g> alkalmazás jelenleg nem áll rendelkezésre. Ezt a(z) <xliff:g id="APP_NAME_1">%2$s</xliff:g> kezeli."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"További információ"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Alkalmazás szüneteltetésének feloldása"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Bekapcsolja a munkaappokat?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Hozzáférést kaphat munkahelyi alkalmazásaihoz és értesítéseihez"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Bekapcsolás"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Az alkalmazás nem hozzáférhető"</string>
<string name="app_blocked_message" msgid="542972921087873023">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg nem hozzáférhető."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 988010b..fe6be14 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Կրկին փորձեք"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Կրկին փորձեք"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Ապակողպեք՝ բոլոր գործառույթներն ու տվյալներն օգտագործելու համար"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Առավելագույն Դեմքով ապակողպման փորձերը գերազանցված են"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Դեմքով ապակողպման փորձերի առավելագույն քանակը գերազանցված են"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM քարտ չկա"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Գրասալիկում SIM քարտ չկա:"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Ձեր Android սարքում SIM քարտ չկա։"</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Տեքստը պատճենված է սեղմատախտակին:"</string>
<string name="copied" msgid="4675902854553014676">"Պատճենվեց"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> հավելվածը տվյալներ տեղադրեց <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> հավելվածից"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> հավելվածը տվյալներ է տեղադրել ձեր սեղմատախտակից"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> հավելվածը տեղադրեց ձեր պատճենած տեքստը"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> հավելվածը տեղադրեց ձեր պատճենած պատկերը"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> հավելվածը տեղադրեց ձեր պատճենած բովանդակությունը"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը պատրաստվում է:"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Հավելվածները մեկնարկում են:"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Բեռնումն ավարտվում է:"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Անջատե՞լ էկրանը"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Մատնահետքը կարգավորելու ժամանակ դուք սեղմել եք սնուցման կոճակը։\n\nՍովորաբար դրա արդյունքում էկրանն անջատվում է։"</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Անջատել"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Չեղարկել"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g>-ն աշխատում է"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Հպեք՝ խաղին վերադառնալու համար"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Ընտրեք խաղ"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Օգտագործել դյուրանցումը"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Գունաշրջում"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Գունաշտկում"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Մեկ ձեռքի ռեժիմ"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Հավելյալ խամրեցում"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը միացավ։"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունն անջատվեց։"</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Թարմացվել է ձեր ադմինիստրատորի կողմից"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Ջնջվել է ձեր ադմինիստրատորի կողմից"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Եղավ"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"«Մարտկոցի տնտեսում» գործառույթը միացնում է մուգ թեման և անջատում կամ սահմանափակում է աշխատանքը ֆոնային ռեժիմում, որոշ վիզուալ էֆեկտներ, ցանցային միացումներ և այլ գործառույթներ։\n\n"<annotation id="url">"Իմանալ ավելին"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"«Մարտկոցի տնտեսում» գործառույթը միացնում է մուգ թեման և անջատում կամ սահմանափակում է աշխատանքը ֆոնային ռեժիմում, որոշ վիզուալ էֆեկտներ, ցանցային միացումներ և այլ գործառույթներ։"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Թրաֆիկի տնտեսման ռեժիմում որոշ հավելվածների համար տվյալների ֆոնային փոխանցումն անջատված է։ Հավելվածը, որն օգտագործում եք, կարող է տվյալներ փոխանցել և ստանալ, սակայն ոչ այնքան հաճախ: Օրինակ՝ պատկերները կցուցադրվեն միայն դրանց վրա սեղմելուց հետո։"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Միացնե՞լ թրաֆիկի տնտեսումը"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Միացնել"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> հավելվածը հասանելի չէ։ Դրա աշխատանքը սահմանափակում է <xliff:g id="APP_NAME_1">%2$s</xliff:g> հավելվածը։"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Մանրամասն"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Չեղարկել դադարեցումը"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Միացնե՞լ հավելվածները"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Միացրեք աշխատանքային հավելվածներն ու ծանուցումները"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Միացնել"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Հավելվածը հասանելի չէ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն այս պահին հասանելի չէ։"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 990f94d..b4c9aa1 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Teks disalin ke papan klip."</string>
<string name="copied" msgid="4675902854553014676">"Disalin"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ditempelkan dari <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempel dari papan klip"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan teks yang Anda salin"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan gambar yang Anda salin"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan konten yang Anda salin"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Menyiapkan <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Memulai aplikasi."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Menyelesaikan boot."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Nonaktifkan layar?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Saat menyiapkan sidik jari, Anda menekan Tombol daya.\n\nTindakan ini biasanya akan menonaktifkan layar."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Nonaktifkan"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Batal"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> sedang berjalan"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ketuk untuk kembali ke game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pilih game"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Diupdate oleh admin Anda"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Dihapus oleh admin Anda"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Oke"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Penghemat Baterai akan mengaktifkan Tema gelap dan membatasi atau menonaktifkan aktivitas latar belakang, beberapa efek visual, fitur tertentu, dan beberapa koneksi jaringan.\n\n"<annotation id="url">"Pelajari lebih lanjut"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Penghemat Baterai akan mengaktifkan Tema gelap dan membatasi atau menonaktifkan aktivitas latar belakang, beberapa efek visual, fitur tertentu, dan beberapa koneksi jaringan."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Untuk membantu mengurangi penggunaan data, Penghemat Data mencegah beberapa aplikasi mengirim atau menerima data di latar belakang. Aplikasi yang sedang digunakan dapat mengakses data, tetapi frekuensinya agak lebih jarang. Misalnya saja, gambar hanya akan ditampilkan setelah diketuk."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Aktifkan Penghemat Data?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktifkan"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saat ini tidak tersedia. Aplikasi ini dikelola oleh <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Pelajari lebih lanjut"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Batalkan jeda aplikasi"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Aktifkan aplikasi kerja?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Dapatkan akses ke aplikasi kerja dan notifikasi"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktifkan"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikasi tidak tersedia"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak tersedia saat ini."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index ba889b6..2d28175 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -612,7 +612,7 @@
<string name="permlab_manageFace" msgid="4569549381889283282">"stjórna vélbúnaði andlitsopnunar"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Leyfir forritinu að beita aðferðum til að bæta við og eyða andlitssniðmátum til notkunar."</string>
<string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"nota vélbúnað andlitsopnunar"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Leyfir forritinu að nota andlitsopnunarvélbúnað til auðkenningar"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Leyfir forritinu að nota vélbúnað andlitsopnunar til auðkenningar"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Andlitsopnun"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Skráðu andlitið þitt aftur"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Skráðu andlitið þitt til að bæta kennsl"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Reyndu aftur"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Reyndu aftur"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Taktu úr lás til að sjá alla eiginleika og gögn"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Hámarksfjölda tilrauna til að opna með andliti náð"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Hámarksfjölda tilrauna til andlitsopnunar náð"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Ekkert SIM-kort"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Ekkert SIM-kort í spjaldtölvunni."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Ekkert SIM-kort er í Android TV tækinu."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Stækka opnunarsvæði."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Opnun með stroku."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Opnun með mynstri."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Opnun með andliti."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Andlitsopnun."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Opnun með PIN-númeri."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Taka PIN-númer SIM-korts úr lás."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Taka PUK-númer SIM-korts úr lás."</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Nota flýtileið"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Umsnúningur lita"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Litaleiðrétting"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Einhent stilling"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mjög dökkt"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Hljóðstyrkstökkum haldið inni. Kveikt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Hljóðstyrkstökkum haldið inni. Slökkt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Kerfisstjóri uppfærði"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Kerfisstjóri eyddi"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Í lagi"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Rafhlöðusparnaður kveikir á dökku þema og takmarkar eða slekkur á bakgrunnsvirkni, sumum áhrifum, tilteknum eiginleikum og sumum nettengingum.\n\n"<annotation id="url">"Nánar"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Rafhlöðusparnaður kveikir á dökku þema og takmarkar eða slekkur á bakgrunnsvirkni, sumum áhrifum, tilteknum eiginleikum og sumum nettengingum."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Gagnasparnaður getur hjálpað til við að draga úr gagnanotkun með því að hindra forrit í að senda eða sækja gögn í bakgrunni. Forrit sem er í notkun getur náð í gögn, en gerir það kannski sjaldnar. Niðurstaðan getur verið að myndir eru ekki birtar fyrr en þú ýtir á þær, svo dæmi sé tekið."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Kveikja á gagnasparnaði?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Kveikja"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er ekki í boði eins og er. Þessu er stjórnað með <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Nánari upplýsingar"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Halda áfram að nota"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Kveikja á vinnuforritum?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Fá aðgang að vinnuforritum og tilkynningum"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Kveikja"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Forrit er ekki tiltækt"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ekki tiltækt núna."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 411c4e1..116b2082 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona dell\'impronta"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"gestione dell\'hardware per Sblocco con il volto"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"gestione dell\'hardware per lo sblocco con il volto"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Consente all\'app di richiamare i metodi per aggiungere e rimuovere i modelli di volti."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"utilizzo dell\'hardware per Sblocco con il volto"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Consente all\'app di utilizzare hardware per l\'autenticazione con Sblocco con il volto"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"utilizzo dell\'hardware per lo sblocco con il volto"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Consente all\'app di usare hardware per l\'autenticazione mediante lo sblocco con il volto"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Sblocco con il volto"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Registra di nuovo il volto"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Per migliorare il riconoscimento, registra di nuovo il tuo volto"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Configura Sblocco con il volto"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Configura lo sblocco con il volto"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Sblocca il telefono guardandolo"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura altri modi per sbloccare"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tocca per aggiungere un\'impronta"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Imposs. verificare volto. Hardware non disponibile."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Riprova Sblocco con il volto."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Riprova lo sblocco con il volto."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Imposs. salvare dati nuovi volti. Elimina un volto vecchio."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Operazione associata al volto annullata."</string>
<string name="face_error_user_canceled" msgid="8553045452825849843">"Sblocco con il volto annullato dall\'utente."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Troppi tentativi. Riprova più tardi."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Troppi tentativi. Sblocco con il volto disattivato"</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Troppi tentativi. Lo sblocco con il volto è disattivato."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Impossibile verificare il volto. Riprova."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Non hai configurato Sblocco con il volto."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Non hai configurato lo sblocco con il volto."</string>
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Sblocco con il volto non supportato su questo dispositivo."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensore temporaneamente disattivato."</string>
<string name="face_name_template" msgid="3877037340223318119">"Volto <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Usa Sblocco con il volto"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usa Sblocco con il volto o il blocco schermo"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Usa lo sblocco con il volto"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usa lo sblocco con il volto o il blocco schermo"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Usa il tuo volto per continuare"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Per continuare devi usare il tuo volto o il tuo blocco schermo"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Riprova"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Riprova"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Sblocca per accedere a funzioni e dati"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Numero massimo di tentativi di Sblocco con il volto superato"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Numero massimo di tentativi di sblocco con il volto superato"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Nessuna SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Nessuna scheda SIM presente nel tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Nessuna scheda SIM nel dispositivo Android TV."</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usa scorciatoia"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversione dei colori"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correzione del colore"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modalità one-hand"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Attenuazione extra"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> attivato."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> disattivato."</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Aggiornato dall\'amministratore"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminato dall\'amministratore"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete.\n\n"<annotation id="url">"Scopri di più"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Per contribuire a ridurre l\'utilizzo dei dati, la funzione Risparmio dati impedisce ad alcune app di inviare o ricevere dati in background. Un\'app in uso può accedere ai dati, ma potrebbe farlo con meno frequenza. Esempio: le immagini non vengono visualizzate finché non le tocchi."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Attivare Risparmio dati?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Attiva"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> non è al momento disponibile. Viene gestita tramite <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Scopri di più"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Riattiva app"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Attivare le app di lavoro?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Attiva l\'accesso alle app di lavoro e alle notifiche"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Attiva"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"L\'app non è disponibile"</string>
<string name="app_blocked_message" msgid="542972921087873023">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> non è al momento disponibile."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 9f705fb..b43fb55 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -615,14 +615,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"סמל טביעת אצבע"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"ניהול החומרה לשחרור נעילה על ידי זיהוי פנים"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"ניהול החומרה לפתיחה ע\"י זיהוי הפנים"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"מאפשרת לאפליקציה להפעיל שיטות להוספה ומחיקה של תבניות פנים שבהן ייעשה שימוש."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"שימוש בחומרה לשחרור נעילה על ידי זיהוי פנים"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"מאפשרת לאפליקציה להשתמש בחומרה לשחרור נעילה על ידי זיהוי פנים לצורך אימות"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"שחרור נעילה על ידי זיהוי פנים"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"שימוש בחומרה לפתיחה ע\"י זיהוי הפנים"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"מאפשרת לאפליקציה להשתמש בחומרה לפתיחה ע\"י זיהוי הפנים"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"פתיחה ע\"י זיהוי הפנים"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"יש לבצע סריקה חוזרת של הפנים שלך"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"לשיפור הזיהוי יש לסרוק מחדש את הפנים שלך"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"הגדרת שחרור נעילה על ידי זיהוי פנים"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"הגדרה של פתיחה ע\"י זיהוי הפנים"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"יש להביט בטלפון כדי לבטל את נעילתו"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"אפשר להגדיר דרכים נוספות לביטול נעילה"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"יש להקיש כדי להוסיף טביעת אצבע"</string>
@@ -649,18 +649,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"לא ניתן לאמת את הפנים. החומרה לא זמינה."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"יש לנסות שוב את שחרור הנעילה על ידי זיהוי פנים."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"יש לנסות שוב את הפתיחה ע\"י זיהוי הפנים."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"לא ניתן לאחסן נתוני פנים חדשים. תחילה יש למחוק את הנתונים הישנים."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"הפעולה לאימות הפנים בוטלה."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"שחרור הנעילה על ידי זיהוי פנים בוטל על ידי המשתמש."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"פתיחה ע\"י זיהוי הפנים בוטל על ידי המשתמש."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"יותר מדי ניסיונות. יש לנסות שוב מאוחר יותר."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"יותר מדי ניסיונות. שחרור נעילה על ידי זיהוי פנים מושבת."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"יותר מדי ניסיונות. \'פתיחה ע\"י זיהוי הפנים\' מושבת."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"לא ניתן לאמת את הפנים. יש לנסות שוב."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"לא הגדרת שחרור נעילה על ידי זיהוי פנים."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"המכשיר הזה לא תומך בשחרור נעילה על ידי זיהוי פנים."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"לא הגדרת פתיחה ע\"י זיהוי הפנים."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"המכשיר הזה לא תומך בפתיחה ע\"י זיהוי הפנים."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"החיישן מושבת באופן זמני."</string>
<string name="face_name_template" msgid="3877037340223318119">"פנים <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"שחרור נעילה על ידי זיהוי פנים"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"פתיחה ע\"י זיהוי הפנים"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"שימוש בזיהוי פנים או בנעילת מסך"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"יש להשתמש באימות פנים כדי להמשיך"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"יש להשתמש בזיהוי הפנים או בנעילת המסך כדי להמשיך"</string>
@@ -893,7 +893,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"כדאי לנסות שוב"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"כדאי לנסות שוב"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"צריך לבטל את הנעילה כדי שכל התכונות והנתונים יהיו זמינים"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"חרגת ממספר הניסיונות המרבי לשחרור נעילה על ידי זיהוי פנים"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"חרגת ממספר הניסיונות המרבי לפתיחה ע\"י זיהוי הפנים"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"אין כרטיס SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"אין כרטיס SIM בטאבלט."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"אין כרטיס SIM במכשיר ה-Android TV."</string>
@@ -963,7 +963,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"הרחבה של אזור ביטול הנעילה."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"ביטול נעילה באמצעות הסטה."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"ביטול נעילה על ידי שרטוט קו."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"שחרור נעילה על ידי זיהוי פנים."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"פתיחה ע\"י זיהוי הפנים."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"ביטול נעילה באמצעות קוד אימות."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"ביטול הנעילה של קוד האימות ל-SIM."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"ביטול נעילה של PUK ל-SIM."</string>
@@ -1028,8 +1028,7 @@
<string name="text_copied" msgid="2531420577879738860">"הטקסט הועתק ללוח."</string>
<string name="copied" msgid="4675902854553014676">"ההעתקה בוצעה"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"האפליקציה <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> הודבקה מ-<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> הודבקה מהלוח שלך"</string>
<string name="pasted_text" msgid="4298871641549173733">"טקסט שהעתקת הודבק על ידי <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
<string name="pasted_image" msgid="4729097394781491022">"תמונה שהעתקת הודבקה על ידי <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
<string name="pasted_content" msgid="646276353060777131">"התוכן שהעתקת הודבק על ידי <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
@@ -1300,14 +1299,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"המערכת מכינה את <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"מתבצעת הפעלה של אפליקציות."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"תהליך האתחול בשלבי סיום."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"לכבות את המסך?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"כשהגדרת את טביעת האצבע, לחצת על לחצן ההפעלה.\n\nלרוב, הפעולה הזו מכבה את המסך."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"כיבוי"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ביטול"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"האפליקציה <xliff:g id="APP">%1$s</xliff:g> פועלת"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"יש להקיש כדי לחזור למשחק"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"בחירת משחק"</string>
@@ -1762,8 +1757,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"שימוש בקיצור הדרך"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"היפוך צבעים"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"תיקון צבעים"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"מצב שימוש ביד אחת"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"מעומעם במיוחד"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. שירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. שירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת."</string>
@@ -1921,10 +1915,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"עודכנה על ידי מנהל המערכת"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"נמחקה על ידי מנהל המערכת"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"אישור"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"התכונה \'חיסכון בסוללה\' מפעילה עיצוב כהה ומגבילה או מכבה פעילות ברקע, חלק מהאפקטים החזותיים, תכונות מסוימות וחלק מהחיבורים לרשתות.\n\n"<annotation id="url">"מידע נוסף"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"התכונה \'חיסכון בסוללה\' מפעילה עיצוב כהה ומגבילה או מכבה פעילות ברקע, חלק מהאפקטים החזותיים, תכונות מסוימות וחלק מהחיבורים לרשתות."</string>
<string name="data_saver_description" msgid="4995164271550590517">"כדי לסייע בהפחתת השימוש בנתונים, חוסך הנתונים (Data Saver) מונע מאפליקציות מסוימות לשלוח או לקבל נתונים ברקע. אפליקציות שבהן נעשה שימוש כרגע יכולות לגשת לנתונים, אבל בתדירות נמוכה יותר. המשמעות היא, למשל, שתמונות יוצגו רק לאחר שמקישים עליהן."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"להפעיל את חוסך הנתונים?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"הפעלה"</string>
@@ -2045,10 +2037,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"האפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> לא זמינה כרגע. אפשר לנהל זאת באפליקציה <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"מידע נוסף"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ביטול ההשהיה של האפליקציה"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"להפעיל את האפליקציות לעבודה?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"קבלת גישה להתראות ולאפליקציות לעבודה"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"הפעלה"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"האפליקציה לא זמינה"</string>
<string name="app_blocked_message" msgid="542972921087873023">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> לא זמינה בשלב זה."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 7238cde..f5a8ba1 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ショートカットを使用"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"色反転"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色補正"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"片手モード"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"さらに輝度を下げる"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が ON になりました。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が OFF になりました。"</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"管理者により更新されています"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"管理者により削除されています"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"バッテリー セーバーを有効にすると、ダークテーマが ON になり、バックグラウンド アクティビティ、一部の視覚効果、特定の機能、一部のネットワーク接続が制限されるか OFF になります。\n\n"<annotation id="url">"詳細"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"バッテリー セーバーを有効にすると、ダークテーマが ON になり、バックグラウンド アクティビティ、一部の視覚効果、特定の機能、一部のネットワーク接続が制限されるか OFF になります。"</string>
<string name="data_saver_description" msgid="4995164271550590517">"データセーバーは、一部のアプリによるバックグラウンドでのデータ送受信を停止することでデータ使用量を抑制します。使用中のアプリからデータを送受信することはできますが、その頻度は低くなる場合があります。この影響として、たとえば画像はタップしないと表示されないようになります。"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"データセーバーを ON にしますか?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ON にする"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"現在、<xliff:g id="APP_NAME_0">%1$s</xliff:g> は使用できません。このアプリの使用は [<xliff:g id="APP_NAME_1">%2$s</xliff:g>] で管理されています。"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"詳細"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"アプリの一時停止を解除"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"仕事用アプリを ON にしますか?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"仕事用のアプリや通知を利用する"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ON にする"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"アプリの利用不可"</string>
<string name="app_blocked_message" msgid="542972921087873023">"現在 <xliff:g id="APP_NAME">%1$s</xliff:g> はご利用になれません。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index a2b337d..bdad7f6 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -613,7 +613,7 @@
<string name="permdesc_manageFace" msgid="6204569688492710471">"საშუალებას აძლევს აპს, დაამატოს და წაშალოს სახეების შაბლონები."</string>
<string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"სახით განბლოკვის აპარატურის გამოყენება"</string>
<string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"საშუალებას აძლევს აპს, ამოცნობისთვის გამოიყენოს სახით განბლოკვის აპარატურა"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"განბლოკვა სახით"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"სახით განბლოკვა"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"დაარეგისტრირეთ თქვენი სახე ხელახლა"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"ამოცნობის გასაუმჯობესებლად, გთხოვთ, ხელახლა დაარეგისტრიროთ თქვენი სახე"</string>
<string name="face_setup_notification_title" msgid="550617822603450009">"სახით განბლოკვის დაყენება"</string>
@@ -646,16 +646,16 @@
<string name="face_error_timeout" msgid="522924647742024699">"ცადეთ ხელახლა სახით განბლოკვა."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"სახის ახალი მონაცემები ვერ ინახება. ჯერ ძველი წაშალეთ."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"სახის ამოცნობა გაუქმდა."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"განბლოკვა სახით გაუქმდა მომხმარებლის მიერ."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"სახით განბლოკვა გაუქმდა მომხმარებლის მიერ."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"დაფიქსირდა ბევრი მცდელობა. ცადეთ მოგვიანებით."</string>
<string name="face_error_lockout_permanent" msgid="8277853602168960343">"მეტისმეტად ბევრი მცდელობა იყო. სახით განბლოკვა გათიშულია."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"სახის დადასტურება ვერ ხერხდება. ცადეთ ხელახლა."</string>
<string name="face_error_not_enrolled" msgid="7369928733504691611">"თქვენ არ დაგიყენებიათ სახით განბლოკვა."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"განბლოკვა სახით ამ მოწყობილობაზე მხარდაჭერილი არ არის."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"სახით განბლოკვა ამ მოწყობილობაზე მხარდაჭერილი არ არის."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"სენსორი დროებით გათიშულია."</string>
<string name="face_name_template" msgid="3877037340223318119">"სახე <xliff:g id="FACEID">%d</xliff:g>"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"გამოიყენეთ სახით განბლოკვა"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"გამოიყენეთ სახე ან ეკრანის დაბლოკვა"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"გამოიყენეთ სახით ან ეკრანის დაბლოკვა"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"გასაგრძელებლად გამოიყენეთ თქვენი სახე"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"გასაგრძელებლად გამოიყენეთ თქვენი სახე ან ეკრანის განბლოკვის ნიმუში"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"კიდევ სცადეთ"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"კიდევ სცადეთ"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"ყველა ფუნქციისა და მონაცემის განბლოკვა"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"სახის ამოცნობით განბლოკვის მცდელობამ დაშვებულ რაოდენობას გადააჭარბა"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"სახით განბლოკვის მცდელობამ დაშვებულ რაოდენობას გადააჭარბა"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM ბარათი არ არის"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"ტაბლეტში არ დევს SIM ბარათი."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"თქვენს Android TV მოწყობილობაში SIM ბარათი არ არის."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"განბლოკვის სივრცის გაშლა."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"გასრიალებით განბლოკვა"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"განბლოკვა ნიმუშით."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"განბლოკვა სახით"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"სახით განბლოკვა"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"განბლოკვა Pin-ით."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM-ის PIN-კოდით განბლოკვა."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM-ის PUK-კოდით განბლოკვა."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"ტექსტი დაკოპირებულია გაცვლის ბუფერში."</string>
<string name="copied" msgid="4675902854553014676">"დაკოპირდა"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-დან <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>-ში ჩასმული"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ჩასმულია თქვენი გაცვლის ბუფერიდან"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-მ(ა) ჩასვა თქვენ მიერ დაკოპირებული ტექსტი"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-მ(ა) ჩასვა თქვენ მიერ დაკოპირებული სურათი"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-მ(ა) ჩასვა თქვენ მიერ დაკოპირებული კონტენტი"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"ემზადება <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"აპების ჩართვა"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ჩატვირთვის დასასრული."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"გამოირთოს ეკრანი?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"თითის ანაბეჭდის დაყენებისას ჩართვის ღილაკს დააჭირეთ.\n\nეს, ჩვეულებრივ, თქვენს ეკრანს გამორთავს."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"გამორთვა"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"გაუქმება"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> გაშვებულია"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"შეეხეთ თამაშში დასაბრუნებლად"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"აირჩიეთ თამაში"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"განახლებულია თქვენი ადმინისტრატორის მიერ"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"წაიშალა თქვენი ადმინისტრატორის მიერ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"კარგი"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ბატარეის დამზოგი ჩართავს მუქ თემას და შეზღუდავს ან გამორთავს ფონურ აქტივობას, ზოგიერთ ვიზუალურ ეფექტს, გარკვეულ ფუნქციებსა და ზოგიერთ ქსელთან კავშირს.\n\n"<annotation id="url">"შეიტყვეთ მეტი"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"ბატარეის დამზოგი ჩართავს მუქ თემას და შეზღუდავს ან გამორთავს ფონურ აქტივობას, ზოგიერთ ვიზუალურ ეფექტს, გარკვეულ ფუნქციებსა და ზოგიერთ ქსელთან კავშირს."</string>
<string name="data_saver_description" msgid="4995164271550590517">"მობილური ინტერნეტის მოხმარების შემცირების მიზნით, მონაცემთა დამზოგველი ზოგიერთ აპს ფონურ რეჟიმში მონაცემთა გაგზავნასა და მიღებას შეუზღუდავს. თქვენ მიერ ამჟამად გამოყენებული აპი მაინც შეძლებს მობილურ ინტერნეტზე წვდომას, თუმცა ამას ნაკლები სიხშირით განახორციელებს. ეს ნიშნავს, რომ, მაგალითად, სურათები არ გამოჩნდება მანამ, სანამ მათ საგანგებოდ არ შეეხებით."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ჩაირთოს მონაცემთა დამზოგველი?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ჩართვა"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ამჟამად მიუწვდომელია. ის იმართება <xliff:g id="APP_NAME_1">%2$s</xliff:g>-ის მიერ."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"შეიტყვეთ მეტი"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"აპის დაპაუზების გაუქმება"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"გსურთ სამსახურის აპების ჩართვა?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"თქვენი სამსახურის აპებსა და შეტყობინებებზე წვდომის მოპოვება"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ჩართვა"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"აპი მიუწვდომელია"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ამჟამად მიუწვდომელია."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index e748545..43fc7b1 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Саусақ ізі белгішесі"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"Face Unlock жабдығын басқару"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"Бет тану жабдығын басқару"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Қолданбаға пайдаланатын бет үлгілерін енгізу және жою әдістерін шақыруға мүмкіндік береді."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"Face Unlock жабдығын пайдалану"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Аутентификациялау үшін қолданбаға Face Unlock жабдығын пайдалануға рұқсат береді."</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face Unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"Бет тану жабдығын пайдалану"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Аутентификациялау үшін қолданбаға бет тану жабдығын пайдалануға рұқсат береді."</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Бет тану"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Бетті қайта тіркеу"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Құрылғы жүзіңізді жақсырақ тануы үшін, бетіңізді қайта тіркеңіз."</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Face Unlock функциясын реттеу"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Бет тану функциясын реттеу"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефоныңызға қарап, оның құлпын ашыңыз."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Құлыпты ашудың басқа тәсілдерін реттеу"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Саусақ ізін қосу үшін түртіңіз."</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Бетті тану мүмкін емес. Жабдық қолжетімді емес."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Face Unlock функциясын қайта қолданып көріңіз."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Бет тану функциясын қайта қолданып көріңіз."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Жаңа бетті сақтау мүмкін емес. Алдымен ескісін жойыңыз."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Бетті танудан бас тартылды."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Пайдаланушы Face Unlock функциясынан бас тартты."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Пайдаланушы бет тану функциясынан бас тартты."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Тым көп әрекет жасалды. Кейінірек қайталаңыз."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Тым көп әрекет жасалды. Face Unlock функциясы өшірілді."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Тым көп әрекет жасалды. Бет тану функциясы өшірілді."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Бетті тану мүмкін емес. Әрекетті қайталаңыз."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Face Unlock реттелмеді."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Бұл құрылғыда Face Unlock функциясы істемейді."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Бет тану реттелмеді."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Бұл құрылғыда бет тану функциясы істемейді."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Датчик уақытша өшірулі."</string>
<string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g> беті"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Face Unlock функциясын пайдалану"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Бет тану функциясын пайдалану"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Face Lock функциясын немесе экран құлпын пайдалану"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Жалғастыру үшін бетіңізді көрсетіңіз."</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Жалғастыру үшін бетті анықтау функциясын немесе экран құлпын пайдаланыңыз."</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Қайталап көріңіз"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Қайталап көріңіз"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Мүмкіндіктер мен деректер үшін құлыпты ашыңыз"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Бет-әлпет арқылы ашу әрекеттері анықталған шегінен асып кетті"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Бет тану арқылы ашу әрекеттері анықталған шегінен асып кетті"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM картасы жоқ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Планшетте SIM картасы жоқ."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Android TV құрылғыңызда SIM картасы жоқ."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Ашу аймағын кеңейту."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Сырғыту арқылы ашу."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Кескін арқылы ашу."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Бет-әлпет арқылы ашу."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Бет тану."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Pin арқылы ашу."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM құлпын PIN кодымен ашу"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM құлпын PUK кодымен ашу"</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Мәтін ақпарат алмастыру қорына сақталды."</string>
<string name="copied" msgid="4675902854553014676">"Көшірілді"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> қолданбасынан <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> қолданбасына қойылды."</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> қолданбасы буферіңізден алынған деректерді қойды."</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> сіз көшірген мәтінді қойды."</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> сіз көшірген суретті қойды."</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> сіз көшірген мазмұнды қойды."</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> дайындалуда."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Қолданбалар іске қосылуда."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Қосуды аяқтауда."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Экранды өшіру керек пе?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Саусақ ізіңізді орнату кезінде қуат түймесін басып қалдыңыз.\n\nБұл әрекет әдетте экранды өшіреді."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Өшіру"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Бас тарту"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> қосылған"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ойынды жалғастыру үшін түртіңіз"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Ойынды таңдаңыз"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Төте жолды пайдалану"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Түс инверсиясы"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Түсті түзету"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Бір қолмен басқару режимі"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Экранды қарайту"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қосулы."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дыбыс деңгейі пернелерін басып тұрған соң, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өшірілді."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Әкімші жаңартқан"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Әкімші жойған"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Жарайды"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Батареяны үнемдеу режимі қараңғы тақырыпты іске қосады және фондық әрекеттерге, кейбір визуалдық әсерлерге, белгілі бір функциялар мен кейбір желі байланыстарына шектеу қояды немесе оларды өшіреді.\n\n"<annotation id="url">"Толығырақ"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Батареяны үнемдеу режимі қараңғы тақырыпты іске қосады және фондық әрекеттерге, кейбір визуалдық әсерлерге, белгілі бір функциялар мен кейбір желі байланыстарына шектеу қояды немесе оларды өшіреді."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Дерек шығынын азайту үшін Трафикті үнемдеу режимінде кейбір қолданбаларға деректі фондық режимде жіберуге және алуға тыйым салынады. Ашық тұрған қолданба деректі шектеулі шамада пайдаланады (мысалы, кескіндер оларды түрткенге дейін көрсетілмейді)."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Трафикті үнемдеу режимі қосылсын ба?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Қосу"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> дәл қазір қолжетімді емес. Ол <xliff:g id="APP_NAME_1">%2$s</xliff:g> арқылы басқарылады."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Толығырақ"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Қолданбаны қайта қосу"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Жұмыс қолданбаларын қосасыз ба?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Жұмыс қолданбалары мен хабарландыруларына рұқсат алу"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Қосу"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Қолданба қолжетімді емес"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> қазір қолжетімді емес."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 31f2b50..b5e5102 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -613,7 +613,7 @@
<string name="permdesc_manageFace" msgid="6204569688492710471">"អនុញ្ញាតឱ្យកម្មវិធីប្រើវិធីសាស្ត្រដើម្បីបញ្ចូល និងលុបទម្រង់គំរូផ្ទៃមុខសម្រាប់ប្រើប្រាស់។"</string>
<string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ប្រើហាតវែរដោះសោតាមទម្រង់មុខ"</string>
<string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"អនុញ្ញាតឱ្យកម្មវិធីប្រើហាតវែរដោះសោតាមទម្រង់មុខសម្រាប់ការផ្ទៀងផ្ទាត់"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ការដោះសោតាមទម្រង់មុខ"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ដោះសោតាមទម្រង់មុខ"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ស្កេនបញ្ចូលមុខរបស់អ្នកម្ដងទៀត"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"ដើម្បីធ្វើឱ្យការសម្គាល់មុខប្រសើរជាងមុន សូមស្កេនបញ្ចូលមុខរបស់អ្នកម្ដងទៀត"</string>
<string name="face_setup_notification_title" msgid="550617822603450009">"រៀបចំការដោះសោតាមទម្រង់មុខ"</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ប្រើប្រាស់ផ្លូវកាត់"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"បញ្ច្រាសពណ៌"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ការកែពណ៌"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"មុខងារប្រើដៃម្ខាង"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ពន្លឺតិចខ្លាំង"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ធ្វើបច្ចុប្បន្នភាពដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"លុបដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"យល់ព្រម"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"មុខងារសន្សំថ្មបើករចនាប័ទ្មងងឹត និងដាក់កំហិត ឬបិទសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលរូបភាពមួយចំនួន មុខងារជាក់លាក់ និងការតភ្ជាប់បណ្ដាញមួយចំនួន។\n\n"<annotation id="url">"ស្វែងយល់បន្ថែម"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"មុខងារសន្សំថ្មបើករចនាប័ទ្មងងឹត និងដាក់កំហិត ឬបិទសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលរូបភាពមួយចំនួន មុខងារជាក់លាក់ និងការតភ្ជាប់បណ្ដាញមួយចំនួន។"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ដើម្បីជួយកាត់បន្ថយការប្រើប្រាស់ទិន្នន័យ កម្មវិធីសន្សំសំចៃទិន្នន័យរារាំងកម្មវិធីមួយចំនួនមិនឲ្យបញ្ជូន ឬទទួលទិន្នន័យនៅផ្ទៃខាងក្រោយទេ។ កម្មវិធីដែលអ្នកកំពុងប្រើនាពេលបច្ចុប្បន្នអាចចូលប្រើប្រាស់ទិន្នន័យបាន ប៉ុន្តែអាចនឹងមិនញឹកញាប់ដូចមុនទេ។ ឧទាហរណ៍ រូបភាពមិនបង្ហាញទេ លុះត្រាតែអ្នកប៉ះរូបភាពទាំងនោះ។"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"បើកកម្មវិធីសន្សំសំចៃទិន្នន័យ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"បើក"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> មិនអាចប្រើបានទេនៅពេលនេះ។ វាស្ថិតក្រោមការគ្រប់គ្រងរបស់ <xliff:g id="APP_NAME_1">%2$s</xliff:g> ។"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"ស្វែងយល់បន្ថែម"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ឈប់ផ្អាកកម្មវិធី"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"បើកកម្មវិធីការងារឬ?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"ទទួលបានសិទ្ធិចូលប្រើការជូនដំណឹង និងកម្មវិធីការងាររបស់អ្នក"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"បើក"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"មិនអាចប្រើកម្មវិធីនេះបានទេ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"មិនអាចប្រើ <xliff:g id="APP_NAME">%1$s</xliff:g> នៅពេលនេះបានទេ។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index f3d0d58..403a76a 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"ಪಠ್ಯವನ್ನು ಕ್ಲಿಪ್ಬೋರ್ಡ್ಗೆ ನಕಲಿಸಲಾಗಿದೆ."</string>
<string name="copied" msgid="4675902854553014676">"ನಕಲಿಸಲಾಗಿದೆ"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ಅನ್ನು <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ಅನ್ನು ನಿಮ್ಮ ಕ್ಲಿಪ್ಬೋರ್ಡ್ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string>
<string name="pasted_text" msgid="4298871641549173733">"ನೀವು ನಕಲಿಸಿರುವ ಪಠ್ಯವನ್ನು <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string>
<string name="pasted_image" msgid="4729097394781491022">"ನೀವು ನಕಲಿಸಿರುವ ಚಿತ್ರವನ್ನು <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string>
<string name="pasted_content" msgid="646276353060777131">"ನೀವು ನಕಲಿಸಿರುವ ವಿಷಯವನ್ನು <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ಬೂಟ್ ಪೂರ್ಣಗೊಳಿಸಲಾಗುತ್ತಿದೆ."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಬೇಕೇ?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಅನ್ನು ಸೆಟ್ಟಪ್ ಮಾಡುವಾಗ ನೀವು ಪವರ್ ಬಟನ್ಅನ್ನು ಒತ್ತಿದ್ದೀರಿ \n\nಸಾಮಾನ್ಯವಾಗಿ ಇದರಿಂದ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಆಫ್ ಆಗುತ್ತದೆ."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ಆಫ್ ಮಾಡಿ"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ರದ್ದುಗೊಳಿಸಿ"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ರನ್ ಆಗುತ್ತಿದೆ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ಆಟಕ್ಕೆ ಹಿಂತಿರುಗಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ಆಟವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ಶಾರ್ಟ್ಕಟ್ ಬಳಸಿ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ಬಣ್ಣ ವಿಲೋಮ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ಒಂದು ಕೈ ಮೋಡ್"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ಇನ್ನಷ್ಟು ಮಬ್ಬು"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಲಾಗಿದೆ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index c74eae7..4c3abec 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"지문 아이콘"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"얼굴인식 잠금해제 하드웨어 관리"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"얼굴 인식 잠금 해제 하드웨어 관리"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"사용할 얼굴 템플릿의 추가 및 삭제 메서드를 앱에서 호출하도록 허용합니다."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"얼굴인식 잠금해제 하드웨어 사용"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"앱에서 얼굴인식 잠금해제 하드웨어를 인증에 사용하도록 허용합니다."</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"얼굴인식 잠금해제"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"얼굴 인식 잠금 해제 하드웨어 사용"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"앱에서 얼굴 인식 잠금 해제 하드웨어를 인증에 사용하도록 허용합니다."</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"얼굴 인식 잠금 해제"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"얼굴 재등록 필요"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"인식률을 개선하려면 얼굴을 다시 등록하세요."</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"얼굴인식 잠금해제 설정"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"얼굴 인식 잠금 해제 설정"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"휴대전화의 화면을 응시하여 잠금 해제할 수 있습니다."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"다른 잠금 해제 방법 설정"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"지문을 추가하려면 탭하세요."</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"얼굴을 확인할 수 없습니다. 하드웨어를 사용할 수 없습니다."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"얼굴인식 잠금해제를 다시 시도해 주세요."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"얼굴 인식 잠금 해제를 다시 시도해 주세요."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"새 얼굴 데이터를 저장할 수 없습니다. 먼저 기존 얼굴 데이터를 삭제하세요."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"얼굴 인식 작업이 취소되었습니다."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"사용자가 얼굴인식 잠금해제를 취소했습니다."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"사용자가 얼굴 인식 잠금 해제를 취소했습니다."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"시도 횟수가 너무 많습니다. 나중에 다시 시도하세요."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"시도 횟수가 너무 많습니다. 얼굴인식 잠금해제가 사용 중지되었습니다."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"시도 횟수가 너무 많습니다. 얼굴 인식 잠금 해제가 사용 중지되었습니다."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"얼굴을 확인할 수 없습니다. 다시 시도하세요."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"얼굴인식 잠금해제를 설정하지 않았습니다."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"이 기기에서는 얼굴인식 잠금해제가 지원되지 않습니다."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"얼굴 인식 잠금 해제를 설정하지 않았습니다."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"이 기기에서는 얼굴 인식 잠금 해제가 지원되지 않습니다."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"센서가 일시적으로 사용 중지되었습니다."</string>
<string name="face_name_template" msgid="3877037340223318119">"얼굴 <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"얼굴인식 잠금해제 사용"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"얼굴 인식 잠금 해제 사용"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"얼굴 또는 화면 잠금 사용"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"계속하려면 얼굴로 인증하세요"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"계속하려면 얼굴 또는 화면 잠금을 사용하세요"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"다시 시도"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"다시 시도"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"모든 기능 및 데이터 잠금 해제"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"얼굴 인식 잠금해제 최대 시도 횟수를 초과했습니다."</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"얼굴 인식 잠금 해제 최대 시도 횟수를 초과했습니다."</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM 카드가 없습니다."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"태블릿에 SIM 카드가 없습니다."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Android TV 기기에 SIM 카드가 없습니다."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"잠금 해제 영역 확장"</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"슬라이드하여 잠금해제합니다."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"패턴을 사용하여 잠금해제합니다."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"얼굴 인식을 사용하여 잠금해제합니다."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"얼굴을 인식하여 잠금 해제합니다."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"핀을 사용하여 잠금해제합니다."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM PIN 잠금 해제"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM PUK 잠금 해제"</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"텍스트가 클립보드에 복사되었습니다."</string>
<string name="copied" msgid="4675902854553014676">"복사 완료"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> 앱이 <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> 앱에서 복사하여 붙여넣음"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>에서 클립보드 데이터를 붙여넣음"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>에서 복사한 텍스트를 붙여넣음"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>에서 복사한 이미지를 붙여넣음"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>에서 복사한 콘텐츠를 붙여넣음"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> 준비 중..."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"앱을 시작하는 중입니다."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"부팅 완료"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"화면을 끄시겠습니까?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"지문을 설정하는 중에 전원 버튼이 눌렸습니다.\n\n이렇게 하면 보통 화면이 꺼집니다."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"끄기"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"취소"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> 실행 중"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"게임으로 돌아가려면 탭하세요."</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"게임 선택"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"단축키 사용"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"색상 반전"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"색상 보정"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"한 손 사용 모드"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"더 어둡게"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"관리자에 의해 업데이트되었습니다."</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"관리자에 의해 삭제되었습니다."</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"확인"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"절전 기능은 어두운 테마를 사용 설정하고 백그라운드 활동, 일부 시각 효과, 특정 기능 및 일부 네트워크 연결을 제한하거나 사용 중지합니다.\n\n"<annotation id="url">"자세히 알아보기"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"절전 기능은 어두운 테마를 사용 설정하고 백그라운드 활동, 일부 시각 효과, 특정 기능 및 일부 네트워크 연결을 제한하거나 사용 중지합니다."</string>
<string name="data_saver_description" msgid="4995164271550590517">"데이터 사용량을 줄이기 위해 데이터 절약 모드는 일부 앱이 백그라운드에서 데이터를 전송하거나 수신하지 못하도록 합니다. 현재 사용 중인 앱에서 데이터에 액세스할 수 있지만 빈도가 줄어듭니다. 예를 들면, 이미지를 탭하기 전에는 이미지가 표시되지 않습니다."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"데이터 절약 모드를 사용 설정하시겠습니까?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"사용 설정"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>은(는) 현재 사용할 수 없습니다. <xliff:g id="APP_NAME_1">%2$s</xliff:g>에서 관리하는 앱입니다."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"자세히 알아보기"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"앱 일시중지 해제"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"직장 앱을 사용 설정하시겠습니까?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"직장 앱 및 알림에 액세스하세요."</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"사용 설정"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"앱을 사용할 수 없습니다"</string>
<string name="app_blocked_message" msgid="542972921087873023">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 사용할 수 없습니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index a60bdd5..01d4219 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -609,11 +609,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Манжа изинин сүрөтчөсү"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"жүзүнөн таануу функциясынын аппараттык камсыздоосун башкаруу"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"жүзүнөн таанып ачуу функциясынын аппараттык камсыздоосун башкаруу"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Колдонмого пайдалануу үчүн жүздүн үлгүлөрүн кошуу жана жок кылуу мүмкүндүгүн берет."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"аппараттык камсыздоо үчүн жүзүнөн таанууну колдонуу"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Колдонмо аныктыкты текшерүүдө Жүзүнөн таануу функциясынын аппараттык камсыздоосун колдонот"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Жүзүнөн таануу"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"аппараттык камсыздоо үчүн жүзүнөн таанып ачуу функциясын колдонуу"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Колдонмо аныктыкты текшерүүдө Жүзүнөн таанып ачуу функциясынын аппараттык камсыздоосун колдонот"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Жүзүнөн таанып ачуу"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Жүзүңүздү кайра таанытыңыз."</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Мыкты таануу үчүн, жүзүңүздү кайра таанытыңыз"</string>
<string name="face_setup_notification_title" msgid="550617822603450009">"Жүзүнөн таанып ачуу функциясын жөндөңүз"</string>
@@ -643,15 +643,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Жүз ырасталбай жатат. Аппараттык камсыздоо жеткиликсиз."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Жүзүнөн таануу функциясын кайра текшерип көрүңүз."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Жүзүнөн таанып ачуу функциясын кайра текшерип көрүңүз."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Жаңы жүздү сактоо мүмкүн эмес. Адегенде эскисин өчүрүңүз."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Жүздүн аныктыгын текшерүү жокко чыгарылды."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Жүзүнөн таануу функциясын колдонуучу өчүрүп салды."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Жүзүнөн таанып ачуу функциясын колдонуучу өчүрүп салды."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Өтө көп жолу аракет жасадыңыз. Бир аздан кийин кайталап көрүңүз."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Өтө көп жолу аракет кылдыңыз. Жүзүнөн таануу функциясы өчүрүлдү."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Өтө көп жолу аракет кылдыңыз. Жүзүнөн таанып ачуу функциясы өчүрүлдү."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Жүз ырасталбай жатат. Кайталап көрүңүз."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Жүзүнөн таануу функциясын жөндөй элексиз."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Жүзүнөн таануу функциясы бул түзмөктө иштебейт."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Жүзүнөн таанып ачуу функциясын жөндөй элексиз."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Жүзүнөн таанып ачуу функциясы бул түзмөктө иштебейт."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Сенсор убактылуу өчүрүлгөн."</string>
<string name="face_name_template" msgid="3877037340223318119">"Жүз <xliff:g id="FACEID">%d</xliff:g>"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"Жүзүнөн таанып ачууну колдонуу"</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Бөгөттөн чыгаруу аймагын кеңейтүү."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Жылмыштырып ачуу."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Үлгү менен ачуу."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Жүзүнөн таануу"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Жүзүнөн таанып ачуу"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Пин код менен ачуу."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM-картанын кулпусун PIN-код менен ачуу."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM-картанын кулпусун PUK-код менен ачуу."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Текст алмашуу буферине көчүрүлдү."</string>
<string name="copied" msgid="4675902854553014676">"Көчүрүлдү"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> колдонмосунан чапталды"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> алмашуу буферинен чапталды"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: көчүрүлгөн текст чапталды"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: көчүрүлгөн сүрөт чапталды"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: көчүрүлгөн мазмун чапталды"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> даярдалууда."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Колдонмолорду иштетип баштоо"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөлүүдө"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Экран өчүрүлсүнбү?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Манжаңыздын изин жөндөп жатканда күйгүзүү/өчүрүү баскычын басып алдыңыз.\n\nБул адатта экранды өчүрөт."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Өчүрүү"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Жокко чыгаруу"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> иштеп жатат"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Оюнга кайтуу үчүн таптаңыз"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Оюн тандоо"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Кыска жолду колдонуу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Түстү инверсиялоо"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Түсүн тууралоо"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Бир колдуу режим"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Кошумча караңгылатуу"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> күйгүзүлдү."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өчүрүлдү."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Администраторуңуз жаңыртып койгон"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Администраторуңуз жок кылып салган"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ЖАРАЙТ"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Батареяны үнөмдөгүч режиминде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер, белгилүү бир функциялар жана айрым тармакка туташуулар чектелип же өчүрүлөт.\n\n"<annotation id="url">"Кеңири маалымат"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Батареяны үнөмдөгүч режиминде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер, белгилүү бир функциялар жана айрым тармакка туташуулар чектелип же өчүрүлөт."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Трафикти үнөмдөө режиминде айрым колдонмолор маалыматтарды фондо өткөрө алышпайт. Учурда сиз пайдаланып жаткан колдонмо маалыматтарды жөнөтүп/ала алат, бирок адаттагыдан азыраак өткөргөндүктөн, анын айрым функциялары талаптагыдай иштебей коюшу мүмкүн. Мисалы, сүрөттөр басылмайынча жүктөлбөйт."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Трафикти үнөмдөө режимин иштетесизби?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Күйгүзүү"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> колдонмосу учурда жеткиликсиз. Анын жеткиликтүүлүгү <xliff:g id="APP_NAME_1">%2$s</xliff:g> тарабынан башкарылат."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Кеңири маалымат"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Колдонмону иштетүү"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Жумуш колдонмолору күйгүзүлсүнбү?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Жумуш колдонмолоруңузга жана билдирмелериңизге мүмкүнчүлүк алыңыз"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Күйгүзүү"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Колдонмо учурда жеткиликсиз"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> учурда жеткиликсиз"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 667057f3..d8c0ef1 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -611,7 +611,7 @@
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ໄອຄອນລາຍນິ້ວມື"</string>
<string name="permlab_manageFace" msgid="4569549381889283282">"ຈັດການຮາດແວປົດລັອກດ້ວຍໜ້າ"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"ອະນຸຍາດໃຫ້ແອັບເປີດວິທີການຕ່າງໆເພື່ອເພີ່ມ ແລະ ລຶບແມ່ແບບໃບໜ້າສຳລັບການນຳໃຊ້."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ໃຊ້ຮາດແວການປົດລັອກໃບໜ້າ"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ໃຊ້ຮາດແວການປົດລັອກດ້ວຍໜ້າ"</string>
<string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"ອະນຸຍາດໃຫ້ແອັບໃຊ້ຮາດແວການປົດລັອກດ້ວຍໜ້າເພື່ອພິສູດຢືນຢັນ"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ປົດລັອກດ້ວຍໜ້າ"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ລົງທະບຽນໃບໜ້າຂອງທ່ານຄືນໃໝ່"</string>
@@ -1869,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ຖືກອັບໂຫລດໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ຖືກລຶບອອກໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ຕົກລົງ"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ຕົວປະຢັດແບັດເຕີຣີຈະເປີດໃຊ້ຮູບແບບສີສັນມືດ ແລະ ຈຳກັດ ຫຼື ປິດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກທາງພາບຈຳນວນໜຶ່ງ, ຄຸນສົມບັດບາງຢ່າງ ແລະ ການເຊື່ອມຕໍ່ເຄືອຂ່າຍບາງອັນ.\n\n"<annotation id="url">"ສຶກສາເພີ່ມເຕີມ"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"ຕົວປະຢັດແບັດເຕີຣີຈະເປີດໃຊ້ຮູບແບບສີສັນມືດ ແລະ ຈຳກັດ ຫຼື ປິດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກທາງພາບຈຳນວນໜຶ່ງ, ຄຸນສົມບັດບາງຢ່າງ ແລະ ການເຊື່ອມຕໍ່ເຄືອຂ່າຍບາງອັນ."</string>
<string name="data_saver_description" msgid="4995164271550590517">"ເພື່ອຊ່ວຍຫຼຸດຜ່ອນການນຳໃຊ້ຂໍ້ມູນ, ຕົວປະຢັດອິນເຕີເນັດຈະປ້ອງກັນບໍ່ໃຫ້ບາງແອັບສົ່ງ ຫຼື ຮັບຂໍ້ມູນໃນພື້ນຫຼັງ. ແອັບໃດໜຶ່ງທີ່ທ່ານກຳລັງໃຊ້ຢູ່ຈະສາມາດເຂົ້າເຖິງຂໍ້ມູນໄດ້ ແຕ່ອາດເຂົ້າເຖິງໄດ້ຖີ່ໜ້ອຍລົງ. ນີ້ອາດໝາຍຄວາມວ່າ ຮູບພາບຕ່າງໆອາດບໍ່ສະແດງຈົນກວ່າທ່ານຈະແຕະໃສ່ກ່ອນ."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ເປີດຕົວປະຢັດອິນເຕີເນັດບໍ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ເປີດໃຊ້"</string>
@@ -1975,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ບໍ່ສາມາດໃຊ້ໄດ້ໃນຕອນນີ້. ມັນຖືກຈັດການໂດຍ <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"ສຶກສາເພີ່ມເຕີມ"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ຍົກເລີກການຢຸດແອັບຊົ່ວຄາວ"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"ເປີດໃຊ້ແອັບບ່ອນເຮັດວຽກບໍ?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"ຮັບສິດເຂົ້າເຖິງແອັບບ່ອນເຮັດວຽກ ແລະ ການແຈ້ງເຕືອນຂອງທ່ານ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ເປີດ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ແອັບບໍ່ສາມາດໃຊ້ໄດ້"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່ສາມາດໃຊ້ໄດ້ໃນຕອນນີ້."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 75facc1..e7b019f 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1757,8 +1757,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Naudoti spartųjį klavišą"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Spalvų inversija"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Spalvų taisymas"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Vienos rankos režimas"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Itin blanku"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ įjungta."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ išjungta."</string>
@@ -1916,10 +1915,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atnaujino administratorius"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Ištrynė administratorius"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Gerai"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Akumuliatoriaus tausojimo priemonė įjungia tamsiąją temą ir apriboja arba išjungia veiklą fone, kai kuriuos vaizdinius efektus, tam tikras funkcijas bei kai kuriuos tinklo ryšius.\n\n"<annotation id="url">"Sužinokite daugiau"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Akumuliatoriaus tausojimo priemonė įjungia tamsiąją temą ir apriboja arba išjungia veiklą fone, kai kuriuos vaizdinius efektus, tam tikras funkcijas bei kai kuriuos tinklo ryšius."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Kad padėtų sumažinti duomenų naudojimą, Duomenų taupymo priemonė neleidžia kai kurioms programoms siųsti ar gauti duomenų fone. Šiuo metu naudojama programa gali pasiekti duomenis, bet tai bus daroma rečiau. Tai gali reikšti, kad, pvz., vaizdai nebus pateikiami, jei jų nepaliesite."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Įj. Duomenų taupymo priemonę?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Įjungti"</string>
@@ -2040,10 +2037,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Programa „<xliff:g id="APP_NAME_0">%1$s</xliff:g>“ šiuo metu nepasiekiama. Tai tvarkoma naudojant programą „<xliff:g id="APP_NAME_1">%2$s</xliff:g>“."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Sužinoti daugiau"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Atšaukti programos pristabdymą"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Įjungti darbo programas?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Pasiekite darbo programas ir pranešimus"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Įjungti"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Programa nepasiekiama."</string>
<string name="app_blocked_message" msgid="542972921087873023">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ šiuo metu nepasiekiama."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index dbbf427..429a8b0 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1025,8 +1025,7 @@
<string name="text_copied" msgid="2531420577879738860">"Teksts ir kopēts uz starpliktuvi."</string>
<string name="copied" msgid="4675902854553014676">"Nokopēts"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Lietotnē <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> tika ielīmēti dati no lietotnes <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>."</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Lietotne <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ielīmēja datus no starpliktuves."</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ielīmēja jūsu nokopēto tekstu"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ielīmēja jūsu nokopēto attēlu"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ielīmēja jūsu nokopēto saturu"</string>
@@ -1280,14 +1279,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Notiek lietotnes <xliff:g id="APPNAME">%1$s</xliff:g> sagatavošana."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Notiek lietotņu palaišana."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Tiek pabeigta sāknēšana."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vai izslēgt ekrānu?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Iestatot pirksta nospiedumu, jūs nospiedāt barošanas pogu.\n\nTādējādi parasti tiek izslēgts ekrāns."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Izslēgt"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Atcelt"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> darbojas"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Pieskarieties, lai atgrieztos spēlē"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Spēles izvēlēšanās"</string>
@@ -1740,8 +1735,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Izmantot saīsni"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Krāsu inversija"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Krāsu korekcija"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Vienas rokas režīms"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Papildu aptumšošana"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika ieslēgts."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika izslēgts."</string>
@@ -1898,10 +1892,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atjaunināja administrators"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Dzēsa administrators"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Labi"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Akumulatora enerģijas taupīšanas režīmā tiek ieslēgts tumšais motīvs un tiek ierobežotas vai izslēgtas darbības fonā, daži vizuālie efekti, noteiktas funkcijas un noteikti tīkla savienojumi.\n\n"<annotation id="url">"Uzzināt vairāk"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Akumulatora enerģijas taupīšanas režīmā tiek ieslēgts tumšais motīvs un tiek ierobežotas vai izslēgtas darbības fonā, daži vizuālie efekti, noteiktas funkcijas un noteikti tīkla savienojumi."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Lai samazinātu datu lietojumu, datu lietojuma samazinātājs neļauj dažām lietotnēm fonā nosūtīt vai saņemt datus. Lietotne, kuru pašlaik izmantojat, var piekļūt datiem, bet, iespējams, piekļūs tiem retāk (piemēram, attēli tiks parādīti tikai tad, kad tiem pieskarsieties)."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vai ieslēgt datu lietojuma samazinātāju?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ieslēgt"</string>
@@ -2013,10 +2005,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> pašlaik nav pieejama. Šo darbību pārvalda <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Uzzināt vairāk"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Atsākt lietotnes darbību"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Vai ieslēgt darba lietotnes?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Iegūstiet piekļuvi darba lietotnēm un paziņojumiem"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Ieslēgt"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Lietotne nav pieejama"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pašlaik nav pieejama."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 876f522..41ce969 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Обидете се повторно"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Обидете се повторно"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Отклучи за сите функции и податоци"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Максималниот број обиди на отклучување со лице е надминат"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Максималниот број обиди на отклучување со лик е надминат"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Нема SIM картичка"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Во таблетот нема SIM картичка."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Нема SIM-картичка во вашиот уред Android TV."</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи кратенка"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија на бои"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција на бои"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим со една рака"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнително затемнување"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е вклучена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е исклучена."</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирано од администраторот"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Избришано од администраторот"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Во ред"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"„Штедачот на батерија“ вклучува темна тема и ја ограничува или исклучува активноста во заднина, некои визуелни ефекти, одредени функции и некои мрежни врски.\n\n"<annotation id="url">"Дознајте повеќе"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"„Штедачот на батерија“ вклучува темна тема и ја ограничува или исклучува активноста во заднина, некои визуелнни ефекти, одредени функции и некои мрежни врски."</string>
<string name="data_saver_description" msgid="4995164271550590517">"За да се намали користењето интернет, „Штедачот на интернет“ спречува дел од апликациите да испраќаат или да примаат податоци во заднина. Одредена апликација што ја користите ќе може да користи интернет, но можеби тоа ќе го прави поретко. Ова значи, на пример, дека сликите нема да се прикажуваат додека не ги допрете."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Да се вклучи „Штедач на интернет“?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Вклучи"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Апликацијата <xliff:g id="APP_NAME_0">%1$s</xliff:g> не е достапна во моментов. Со ова управува <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Дознај повеќе"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Прекини ја паузата"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Да се вклучат работни апликации?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Добијте пристап до вашите работни апликации и известувања"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Вклучи"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Апликацијата не е достапна"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> не е достапна во моментов."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 5448be5..715cd8d 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -611,12 +611,12 @@
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ഫിംഗർപ്രിന്റ് ഐക്കൺ"</string>
<string name="permlab_manageFace" msgid="4569549381889283282">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഹാർഡ്വെയർ മാനേജ് ചെയ്യുക"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"ഉപയോഗിക്കാനായി, മുഖത്തിന്റെ ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഹാർഡ്വെയർ ഉപയോഗിക്കുക"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"പരിശോധിച്ചുറപ്പിക്കാൻ മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഹാർഡ്വെയർ ഉപയോഗിക്കാൻ അനുവദിക്കുന്നു"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക്"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ഫെയ്സ് അൺലോക്ക് ഹാർഡ്വെയർ ഉപയോഗിക്കുക"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"പരിശോധിച്ചുറപ്പിക്കാൻ ഫെയ്സ് അൺലോക്ക് ഹാർഡ്വെയർ ഉപയോഗിക്കാൻ അനുവദിക്കുന്നു"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ഫെയ്സ് അൺലോക്ക്"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"നിങ്ങളുടെ മുഖം വീണ്ടും എൻറോൾ ചെയ്യൂ"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"തിരിച്ചറിയൽ മെച്ചപ്പെടുത്താൻ, നിങ്ങളുടെ മുഖം ദയവായി വീണ്ടും എൻറോൾ ചെയ്യൂ"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് സജ്ജീകരിക്കുക"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"ഫെയ്സ് അൺലോക്ക് സജ്ജീകരിക്കുക"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ഫോണിലേക്ക് നോക്കി അത് അൺലോക്ക് ചെയ്യുക"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"അൺലോക്ക് ചെയ്യുന്നതിനുള്ള കൂടുതൽ വഴികൾ സജ്ജീകരിക്കുക"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ഫിംഗർപ്രിന്റ് ചേർക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"മുഖം പരിശോധിക്കാൻ കഴിയില്ല. ഹാർഡ്വെയർ ലഭ്യമല്ല."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് വീണ്ടും പരീക്ഷിക്കൂ"</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"ഫെയ്സ് അൺലോക്ക് വീണ്ടും പരീക്ഷിക്കൂ"</string>
<string name="face_error_no_space" msgid="5649264057026021723">"പുതിയ മുഖ ഡാറ്റ സംഭരിക്കാനാകില്ല. ആദ്യം പഴയത് ഇല്ലാതാക്കുക."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"മുഖത്തിന്റെ പ്രവർത്തനം റദ്ദാക്കി."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"ഉപയോക്താവ് മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് റദ്ദാക്കി"</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"ഉപയോക്താവ് ഫെയ്സ് അൺലോക്ക് റദ്ദാക്കി"</string>
<string name="face_error_lockout" msgid="7864408714994529437">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"വളരെയധികം ശ്രമങ്ങൾ. മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"വളരെയധികം ശ്രമങ്ങൾ. ഫെയ്സ് അൺലോക്ക് പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"മുഖം പരിശോധിക്കാൻ കഴിയില്ല. വീണ്ടും ശ്രമിക്കൂ."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് സജ്ജീകരിച്ചില്ല."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഈ ഉപകരണം പിന്തുണയ്ക്കുന്നില്ല."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"ഫെയ്സ് അൺലോക്ക് സജ്ജീകരിച്ചില്ല."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"ഫെയ്സ് അൺലോക്ക് ഈ ഉപകരണം പിന്തുണയ്ക്കുന്നില്ല."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി."</string>
<string name="face_name_template" msgid="3877037340223318119">"മുഖം <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഉപയോഗിക്കുക"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"മുഖം അല്ലെങ്കിൽ സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ഫെയ്സ് അൺലോക്ക് ഉപയോഗിക്കുക"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ഫെയ്സ് അല്ലെങ്കിൽ സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"തുടരാൻ നിങ്ങളുടെ മുഖം ഉപയോഗിക്കുക"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"തുടരാൻ നിങ്ങളുടെ മുഖം അല്ലെങ്കിൽ സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string>
<string-array name="face_error_vendor">
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"അൺലോക്ക് ഏരിയ വിപുലീകരിക്കുക."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"സ്ലൈഡ് അൺലോക്ക്."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"പാറ്റേൺ അൺലോക്ക്."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക്."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"ഫെയ്സ് അൺലോക്ക്."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"പിൻ അൺലോക്ക്."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"സിം പിൻ അൺലോക്ക്."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"സിം Puk അൺലോക്ക്."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"ടെക്സ്റ്റ് ക്ലിപ്ബോർഡിലേക്ക് പകർത്തി."</string>
<string name="copied" msgid="4675902854553014676">"പകർത്തി"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> എന്നതിൽ നിന്ന് <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ഒട്ടിച്ചു"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> നിങ്ങളുടെ ക്ലിപ്പ്ബോർഡിൽ നിന്ന് ഒട്ടിച്ചു"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> നിങ്ങൾ പകർത്തിയ ടെക്സ്റ്റ് ഒട്ടിച്ചു"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> നിങ്ങൾ പകർത്തിയ ഒരു ചിത്രം ഒട്ടിച്ചു"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> നിങ്ങൾ പകർത്തിയ ഉള്ളടക്കം ഒട്ടിച്ചു"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> തയ്യാറാക്കുന്നു."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"അപ്ലിക്കേഷനുകൾ ആരംഭിക്കുന്നു."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ബൂട്ട് ചെയ്യൽ പൂർത്തിയാകുന്നു."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"സ്ക്രീൻ ഓഫാക്കണോ?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ഫിംഗർപ്രിന്റ് സജ്ജീകരിച്ച് കൊണ്ടിരുന്നപ്പോൾ നിങ്ങൾ പവർ ബട്ടൺ അമർത്തി.\n\nഇത് സാധാരണയായി സ്ക്രീൻ ഓഫാകുന്നതിന് കാരണമാകും."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ഓഫാക്കുക"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"റദ്ദാക്കുക"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> പ്രവർത്തിക്കുന്നു"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ഗെയിമിലേക്ക് മടങ്ങാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ഗെയിം തിരഞ്ഞെടുക്കുക"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"കുറുക്കുവഴി ഉപയോഗിക്കുക"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"വർണ്ണ വിപര്യയം"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"നിറം ക്രമീകരിക്കൽ"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ഒറ്റക്കൈ മോഡ്"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"കൂടുതൽ ഡിം ചെയ്യൽ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"വോളിയം കീകൾ പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓണാക്കി."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"വോളിയം കീകൾ അമർത്തിപ്പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫാക്കി."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"നിങ്ങളുടെ അഡ്മിൻ അപ്ഡേറ്റ് ചെയ്യുന്നത്"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"നിങ്ങളുടെ അഡ്മിൻ ഇല്ലാതാക്കുന്നത്"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ശരി"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ബാറ്ററി ലാഭിക്കൽ ഡാർക്ക് തീം ഓണാക്കുന്നു, പശ്ചാത്തല ആക്റ്റിവിറ്റിയും ചില വിഷ്വൽ ഇഫക്റ്റുകളും ചില ഫീച്ചറുകളും ചില നെറ്റ്വർക്ക് കണക്ഷനുകളും അത് പരിമിതപ്പെടുത്തുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു.\n\n"<annotation id="url">"കൂടുതലറിയുക"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"ബാറ്ററി ലാഭിക്കൽ ഡാർക്ക് തീം ഓണാക്കുന്നു, പശ്ചാത്തല ആക്റ്റിവിറ്റിയും ചില വിഷ്വൽ ഇഫക്റ്റുകളും ചില ഫീച്ചറുകളും ചില നെറ്റ്വർക്ക് കണക്ഷനുകളും അത് പരിമിതപ്പെടുത്തുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു."</string>
<string name="data_saver_description" msgid="4995164271550590517">"ഡാറ്റാ ഉപയോഗം കുറയ്ക്കാൻ സഹായിക്കുന്നതിനായി പശ്ചാത്തലത്തിൽ ഡാറ്റ അയയ്ക്കുകയോ സ്വീകരിക്കുകയോ ചെയ്യുന്നതിൽ നിന്ന് ചില ആപ്പുകളെ ഡാറ്റാ സേവർ തടയുന്നു. നിങ്ങൾ നിലവിൽ ഉപയോഗിക്കുന്ന ഒരു ആപ്പിന് ഡാറ്റ ആക്സസ് ചെയ്യാനാകും, എന്നാൽ വല്ലപ്പോഴും മാത്രമെ സംഭവിക്കുന്നുള്ളു. ഇതിനർത്ഥം, ഉദാഹരണമായി നിങ്ങൾ ടാപ്പ് ചെയ്യുന്നത് വരെ ചിത്രങ്ങൾ പ്രദർശിപ്പിക്കുകയില്ല എന്നാണ്."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ഡാറ്റ സേവർ ഓണാക്കണോ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ഓണാക്കുക"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ഇപ്പോൾ ലഭ്യമല്ല. <xliff:g id="APP_NAME_1">%2$s</xliff:g> ആണ് ഇത് മാനേജ് ചെയ്യുന്നത്."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"കൂടുതലറിയുക"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ആപ്പ് പുനഃരാംഭിക്കുക"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"ഔദ്യോഗിക ആപ്പുകൾ ഓണാക്കണോ?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"നിങ്ങളുടെ ഔദ്യോഗിക ആപ്പുകളിലേക്കും അറിയിപ്പുകളിലേക്കും ആക്സസ് നേടുക"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ഓണാക്കുക"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ആപ്പ് ലഭ്യമല്ല"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ഇപ്പോൾ ലഭ്യമല്ല."</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index ab08384..6053a1c 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Хурууны хээний дүрс"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"царайгаар тайлах техник хангамжийг удирдах"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"царайгаар түгжээ тайлах техник хангамжийг удирдах"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Аппад царайны загварыг ашиглахын тулд нэмэх эсвэл устгах аргыг идэвхжүүлэхийг зөвшөөрдөг."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"царайгаар тайлах техник хангамж ашиглах"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Аппад царайгаар тайлах техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Царайгаар тайлах"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"царайгаар түгжээ тайлах техник хангамж ашиглах"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Аппад царайгаар түгжээ тайлах техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Царайгаар түгжээ тайлах"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Царайгаа дахин бүртгүүлнэ үү"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Танилтыг сайжруулахын тулд царайгаа дахин бүртгүүлнэ үү"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Царайгаар тайлах онцлогийг тохируулна уу"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Царайгаар түгжээ тайлах онцлогийг тохируулна уу"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Утас руугаа харж түгжээг нь тайлна уу"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Түгжээ тайлах илүү олон арга тохируулна уу"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Хурууны хээ нэмэхийн тулд товшино уу"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Царайг бататгаж чадсангүй. Техник хангамж боломжгүй байна."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Царайгаар тайлахыг дахин оролдоно уу."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Царайгаар түгжээ тайлахыг дахин оролдоно уу."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Царайн шинэ өгөгдлийг хадгалж чадсангүй. Эхлээд хуучин өгөгдлийг устгана уу."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Царайны үйл ажиллагааг цуцаллаа."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Хэрэглэгч царайгаар тайлахыг цуцалсан."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Хэрэглэгч царайгаар түгжээ тайлахыг цуцалсан."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Хэт олон удаа оролдлоо. Дараа дахин оролдоно уу."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Хэтэрхий олон удаа оролдлоо. Царайгаар тайлахыг идэвхгүй болголоо."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Хэтэрхий олон удаа оролдлоо. Царайгаар түгжээ тайлахыг идэвхгүй болголоо."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Царайг бататгаж чадсангүй. Дахин оролдоно уу."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Та царайгаар тайлахыг тохируулаагүй байна."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Царайгаар тайлахыг энэ төхөөрөмж дээр дэмждэггүй."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Та царайгаар түгжээ тайлахыг тохируулаагүй байна."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Царайгаар түгжээ тайлахыг энэ төхөөрөмж дээр дэмждэггүй."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Мэдрэгчийг түр хугацаанд идэвхгүй болгосон."</string>
<string name="face_name_template" msgid="3877037340223318119">"Царай <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Царайгаар тайлахыг ашиглах"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Царай эсвэл дэлгэцийн түгжээ ашиглах"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Царайгаар түгжээ тайлахыг ашиглах"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Царайгаар түгжээ тайлах эсвэл дэлгэцийн түгжээ ашиглах"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Үргэлжлүүлэхийн тулд царайгаа ашиглана уу"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Үргэлжлүүлэхийн тулд царай эсвэл дэлгэцийн түгжээгээ ашиглана уу"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Дахин оролдох"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Дахин оролдох"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Бүх онцлог, өгөгдлийн түгжээг тайлах"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Нүүрээр түгжээ тайлах оролдлогын тоо дээд хэмжээнээс хэтэрсэн"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Царайгаар түгжээ тайлах оролдлогын тоо дээд хэмжээнээс хэтэрсэн"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM карт байхгүй"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Таблет SIM картгүй."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Таны Android TV төхөөрөмжид SIM карт алга."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Түгжээгүй хэсгийг өргөсгөх."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Тайлах гулсуулалт."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Тайлах хээ."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Царайгаар тайлах"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Царайгаар түгжээ тайлах"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Тайлах пин."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Sim-н пин кодыг тайлна уу."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Sim-н Puk кодыг тайлна уу."</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Товчлол ашиглах"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Өнгө хувиргалт"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Өнгөний засвар"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Нэг гарын горим"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Хэт бүүдгэр"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаалаа."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраалаа."</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Таны админ шинэчилсэн"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Таны админ устгасан"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Батарей хэмнэгч нь Бараан загварыг асааж, дэвсгэрийн үйл ажиллагаа, зарим визуал эффект, тодорхой онцлогууд болон зарим сүлжээний холболтыг хязгаарлах эсвэл унтраана.\n\n"<annotation id="url">"Нэмэлт мэдээлэл авах"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Батарей хэмнэгч нь Бараан загварыг асааж, дэвсгэрийн үйл ажиллагаа, зарим визуал эффект, тодорхой онцлогууд болон зарим сүлжээний холболтыг хязгаарлах эсвэл унтраана."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Дата ашиглалтыг багасгахын тулд дата хэмнэгч нь ар талд ажиллаж буй зарим апп-н өгөгдлийг илгээх болон авахаас сэргийлдэг. Таны одоогийн ашиглаж буй апп нь өгөгдөлд хандах боломжтой хэдий ч тогтмол хандахгүй. Энэ нь жишээлбэл зургийг товших хүртэл харагдахгүй гэсэн үг юм."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Дата хэмнэгчийг асаах уу?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Асаах"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> одоогоор боломжгүй байна. Үүнийг <xliff:g id="APP_NAME_1">%2$s</xliff:g>-р удирддаг."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Дэлгэрэнгүй үзэх"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Аппыг түр зогсоохоо болих"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Ажлын аппуудыг асаах уу?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Ажлын аппууд болон мэдэгдлүүддээ хандах эрх аваарай"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Асаах"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Апп боломжгүй байна"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> яг одоо боломжгүй байна."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 2e8d6f5..a4c877f 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"पुन्हा प्रयत्न करा"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"पुन्हा प्रयत्न करा"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"सर्व वैशिष्ट्ये आणि डेटासाठी अनलॉक करा"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"कमाल चेहरा अनलॉक प्रयत्न ओलांडले"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"कमाल फेस अनलॉक प्रयत्न ओलांडले"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"सिम कार्ड नाही"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"टॅब्लेटमध्ये सिम कार्ड नाही."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"तुमच्या Android TV डिव्हाइसमध्ये सिम कार्ड नाही."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"अनलॉक क्षेत्र विस्तृत करा."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"स्लाइड अनलॉक."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"पॅटर्न अनलॉक."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"चेहरा अनलॉक."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"फेस अनलॉक."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"पिन अनलॉक."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"सिम पिन अनलॉक करा"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"सिम PUK अनलॉक करा"</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"मजकूर क्लिपबोर्डवर कॉपी केला."</string>
<string name="copied" msgid="4675902854553014676">"कॉपी केले"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> वरून <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> पेस्ट केले"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ने तुमच्या क्लिपबोर्डवरून पेस्ट केले"</string>
<string name="pasted_text" msgid="4298871641549173733">"तुम्ही कॉपी केलेला मजकूर <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ने पेस्ट केला"</string>
<string name="pasted_image" msgid="4729097394781491022">"तुम्ही कॉपी केलेली इमेज <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ने पेस्ट केली"</string>
<string name="pasted_content" msgid="646276353060777131">"तुम्ही कॉपी केलेला आशय <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ने पेस्ट केला"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> तयार करत आहे."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"अॅप्स सुरू करत आहे."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बूट समाप्त होत आहे."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"स्क्रीन बंद करायची आहे का?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"तुम्ही तुमची फिंगरप्रिंट सेट करत असताना पॉवर बटण दाबले.\n\nयामुळे सहसा तुमची स्क्रीन बंद होते."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"बंद करा"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"रद्द करा"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"रन होणारे <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"गेमवर परत जाण्यासाठी टॅप करा"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"गेम निवडा"</string>
@@ -1612,7 +1607,7 @@
<string name="activity_chooser_view_see_all" msgid="3917045206812726099">"सर्व पहा"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"ॲक्टिव्हिटी निवडा"</string>
<string name="share_action_provider_share_with" msgid="1904096863622941880">"यांच्यासह शेअर करा"</string>
- <string name="sending" msgid="206925243621664438">"पाठवित आहे..."</string>
+ <string name="sending" msgid="206925243621664438">"पाठवत आहे..."</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"ब्राउझर लाँच करायचा?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"कॉल स्वीकारायचा?"</string>
<string name="activity_resolver_use_always" msgid="5575222334666843269">"नेहमी"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट वापरा"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रंगांची उलटापालट"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रंग सुधारणा"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एकहाती मोड"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"आणखी डिम"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू केला आहे."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केले आहे."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"आपल्या प्रशासकाने अपडेट केले"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"आपल्या प्रशासकाने हटवले"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ओके"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"बॅटरी सेव्हर गडद थीम सुरू करते आणि बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट, ठरावीक वैशिष्ट्ये व काही नेटवर्क कनेक्शन मर्यादित किंवा बंद करते.\n\n"<annotation id="url">"अधिक जाणून घ्या"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"बॅटरी सेव्हर गडद थीम सुरू करते आणि बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट, ठरावीक वैशिष्ट्ये व काही नेटवर्क कनेक्शन मर्यादित किंवा बंद करते."</string>
<string name="data_saver_description" msgid="4995164271550590517">"डेटाचा वापर कमी करण्यात मदत करण्यासाठी काही अॅप्सना बॅकग्राउंडमध्ये डेटा पाठवण्यास किंवा मिळवण्यास डेटा सर्व्हर प्रतिबंध करतो. तुम्ही सध्या वापरत असलेले अॅप डेटा अॅक्सेस करू शकते, पण तसे खूप कमी वेळा होते. याचाच अर्थ असा की, तुम्ही इमेजवर टॅप करेपर्यंत त्या डिस्प्ले होणार नाहीत असे होऊ शकते."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा सेव्हर सुरू करायचे?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"सुरू करा"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> आत्ता उपलब्ध नाही. हे <xliff:g id="APP_NAME_1">%2$s</xliff:g> कडून व्यवस्थापित केले जाते."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"अधिक जाणून घ्या"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"अॅप उघडा"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"कार्य अॅप्स सुरू करायची का?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"तुमची कार्य ॲप्स आणि सूचना यांचा अॅक्सेस मिळवा"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"सुरू करा"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ॲप उपलब्ध नाही"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> आता उपलब्ध नाही."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index d154582..a95b077 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon cap jari"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"urus perkakasan wajah buka kunci"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"urus perkakasan buka kunci wajah"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Membenarkan apl menggunakan kaedah untuk menambahkan dan memadamkan templat wajah untuk digunakan."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"gunakan perkakasan wajah buka kunci"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Membenarkan apl menggunakan perkakasan wajah buka kunci untuk pengesahan"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Wajah buka kunci"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"gunakan perkakasan buka kunci wajah"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Membenarkan apl menggunakan perkakasan buka kunci wajah untuk pengesahan"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Buka kunci wajah"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Daftarkan semula wajah anda"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Untuk meningkatkan pengecaman, sila daftarkan semula wajah anda"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Sediakan wajah buka kunci"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Sediakan buka kunci wajah"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci telefon anda dengan melihat telefon anda"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Sediakan lebih banyak cara untuk membuka kunci"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ketik untuk menambahkan cap jari"</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Tdk dpt sahkan wajah. Perkakasan tidak tersedia."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Cuba wajah buka kunci sekali lagi."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Cuba buka kunci wajah sekali lagi."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Tdk dpt menyimpan data wajah baharu. Padamkan yg lama dahulu."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Pengendalian wajah dibatalkan."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Wajah buka kunci dibatalkan oleh pengguna."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Buka kunci wajah dibatalkan oleh pengguna."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Terlalu banyak percubaan. Cuba sebentar lagi."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Terlalu banyak percubaan. Wajah buka kunci dilumpuhkan."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Terlalu banyak percubaan. Buka kunci wajah dilumpuhkan."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Tidak dapat mengesahkan wajah. Cuba lagi."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Anda belum menyediakan wajah buka kunci."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Wajah buka kunci tidak disokong pada peranti ini."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Anda belum menyediakan buka kunci wajah."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Buka kunci wajah tidak disokong pada peranti ini."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Penderia dilumpuhkan sementara."</string>
<string name="face_name_template" msgid="3877037340223318119">"Wajah <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Gunakan wajah buka kunci"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Gunakan buka kunci wajah"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gunakan kunci wajah atau skrin"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Gunakan wajah untuk teruskan"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Gunakan wajah atau kunci skrin anda untuk meneruskan"</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Kembangkan bahagian buka kunci."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Buka kunci luncur."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Buka kunci corak."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Wajah Buka Kunci"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Buka Kunci Wajah"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Buka kunci pin."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Buka kunci Pin Sim."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Buka kunci Puk Sim."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Teks disalin ke papan keratan"</string>
<string name="copied" msgid="4675902854553014676">"Disalin"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ditampalkan daripada <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ditampal daripada papan keratan anda"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> telah menampal teks yang anda salin"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> telah menampal imej yang anda salin"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> telah menampal kandungan yang anda salin"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Menyediakan <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Memulakan apl."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"But akhir."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Matikan skrin?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Semasa menyediakan cap jari anda, anda menekan butang Kuasa.\n\nTindakan ini biasanya mematikan skrin anda."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Matikan"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Batal"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> dijalankan"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ketik untuk kembali ke permainan"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pilih permainan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 2054e47..9ecede5 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -219,7 +219,7 @@
<string name="silent_mode" msgid="8796112363642579333">"အသံတိတ်စနစ်"</string>
<string name="turn_on_radio" msgid="2961717788170634233">"wirelessအားဖွင့်မည်"</string>
<string name="turn_off_radio" msgid="7222573978109933360">"wirelessအားပိတ်မည်"</string>
- <string name="screen_lock" msgid="2072642720826409809">"ဖန်သားပြင် လော့ခ်ချခြင်း"</string>
+ <string name="screen_lock" msgid="2072642720826409809">"ဖန်သားပြင် လော့ခ်ချရန်"</string>
<string name="power_off" msgid="4111692782492232778">"စက်ပိတ်ပါ"</string>
<string name="silent_mode_silent" msgid="5079789070221150912">"ဖုန်းမြည်သံပိတ်ထားသည်"</string>
<string name="silent_mode_vibrate" msgid="8821830448369552678">"တုန်ခါခြင်း ဖုန်းမြည်သံ"</string>
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"လက်ဗွေ သင်္ကေတ"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"မျက်နှာမှတ် သော့ဖွင့်ခြင်း စက်ပစ္စည်းကို စီမံခြင်း"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"မျက်နှာပြလော့ခ်ဖွင့်သည့် စက်ပစ္စည်းကို စီမံခြင်း"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"အသုံးပြုရန်အတွက် မျက်နှာပုံစံထည့်ရန် (သို့) ဖျက်ရန်နည်းလမ်းကို အက်ပ်အား သုံးခွင့်ပြုသည်။"</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"မျက်နှာမှတ် သော့ဖွင့်ခြင်း စက်ပစ္စည်းကို သုံးပါ"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"အထောက်အထားစိစစ်ရန်အတွက် ဤအက်ပ်အား မျက်နှာမှတ်သော့ဖွင့်ခြင်း စက်ပစ္စည်းကိုသုံးခွင့်ပြုသည်"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"မျက်နှာမှတ် သော့ဖွင့်ခြင်း"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"မျက်နှာပြ လော့ခ်ဖွင့်သည့် စက်ပစ္စည်းကို သုံးပါ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"အထောက်အထားစိစစ်ရန်အတွက် ဤအက်ပ်အား မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း စက်ပစ္စည်းကိုသုံးခွင့်ပြုသည်"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"သင့်မျက်နှာကို စာရင်းပြန်သွင်းပါ"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"ပိုမှတ်မိစေရန် သင့်မျက်နှာကို စာရင်းပြန်သွင်းပါ"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"မျက်နှာမှတ် သော့ဖွင့်ခြင်းကို စနစ်ထည့်သွင်းပါ"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း စနစ်ထည့်သွင်းပါ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"သင့်ဖုန်းကိုကြည့်၍ သော့ဖွင့်ပါ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"သော့ဖွင့်ရန် နောက်ထပ်နည်းလမ်းများကို စနစ်ထည့်သွင်းပါ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"လက်ဗွေထည့်ရန် တို့ပါ"</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"မျက်နှာကို အတည်ပြု၍ မရပါ။ ဟာ့ဒ်ဝဲ မရနိုင်ပါ။"</string>
- <string name="face_error_timeout" msgid="522924647742024699">"မျက်နှာမှတ် သော့ဖွင့်ခြင်းကို ထပ်စမ်းကြည့်ပါ။"</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို ထပ်စမ်းကြည့်ပါ။"</string>
<string name="face_error_no_space" msgid="5649264057026021723">"မျက်နှာဒေတာအသစ် သိမ်း၍မရပါ။ အဟောင်းကို အရင်ဖျက်ပါ။"</string>
<string name="face_error_canceled" msgid="2164434737103802131">"မျက်နှာ ဆောင်ရွက်ခြင်းကို ပယ်ဖျက်လိုက်ပါပြီ။"</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"မှတ်နှာမှတ် သော့ဖွင့်ခြင်းကို အသုံးပြုသူက မလုပ်တော့ပါ။"</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"မှတ်နှာပြ လော့ခ်ဖွင့်ခြင်းကို မလုပ်တော့ပါ။"</string>
<string name="face_error_lockout" msgid="7864408714994529437">"အကြိမ်များစွာ စမ်းပြီးပါပြီ။ နောက်မှထပ်စမ်းပါ။"</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"စမ်းသပ်ကြိမ် များနေပြီ။ မျက်နှာမှတ် သော့ဖွင့်ခြင်းကို ပိတ်လိုက်ပါပြီ။"</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"စမ်းသပ်ကြိမ် များနေပြီ။ မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း ပိတ်လိုက်ပါပြီ။"</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"မျက်နှာကို အတည်ပြု၍ မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"မျက်နှာမှတ် သော့ဖွင့်ခြင်းကို ထည့်သွင်းမထားပါ"</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"ဤစက်ပစ္စည်းတွင် မျက်နှာမှတ် သော့ဖွင့်ခြင်းကို သုံး၍မရပါ။"</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း ထည့်သွင်းမထားပါ"</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"ဤစက်ပစ္စည်းတွင် မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို သုံး၍မရပါ။"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်။"</string>
<string name="face_name_template" msgid="3877037340223318119">"မျက်နှာ <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"မျက်နှာမှတ်သော့ဖွင့်ခြင်းကို သုံးခြင်း"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"မျက်နှာပြ လော့ခ်ဖွင့်ရန်"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"မျက်နှာမှတ်သော့ဖွင့်ခြင်း (သို့) ဖန်သားပြင်လော့ခ်ချခြင်းကို သုံးခြင်း"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"ရှေ့ဆက်ရန် သင့်မျက်နှာကို သုံးပါ"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"ရှေ့ဆက်ရန် သင်၏ မျက်နှာ (သို့) ဖန်သားပြင်လော့ခ်ကို သုံးပါ"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ထပ် စမ်းပါ"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"ထပ် စမ်းပါ"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"ဝန်ဆောင်မှုနှင့် ဒေတာအားလုံးအတွက် လော့ခ်ဖွင့်ပါ"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"မျက်မှာမှတ် သော့ဖွင့်ခြင်း ခွင့်ပြုသော အကြိမ်ရေထက် ကျော်လွန်သွားပါပြီ"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"မျက်မှာပြ လော့ခ်ဖွင့်ခြင်း ခွင့်ပြုသော အကြိမ်ရေထက် ကျော်လွန်သွားပါပြီ"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"ဆင်းကဒ် မရှိပါ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"တက်ပလက်ထဲတွင်း ဆင်းကဒ် မရှိပါ"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"သင့် Android TV စက်ပစ္စည်းပေါ်တွင် ဆင်းမ်ကတ်မရှိပါ။"</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"သော့မချထားသာ နယ်ပယ်ကို ချဲ့ပါ"</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"ဘေးတိုက်ပွတ်ဆွဲ၍ သော့ဖွင့်ခြင်း"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"ပုံစံဖြင့် သော့ဖွင့်ခြင်း"</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"မျက်နှာမှတ် သော့ဖွင့်ခြင်း"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"ပင်နံပါတ်ဖြင့် သော့ဖွင့်ခြင်း"</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"ဆင်းမ်ကဒ် ပင်နံပါတ်လော့ခ်ဖွင့်ပါ။"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"ဆင်းမ်ကဒ် Puk လော့ခ်ဖွင့်ပါ။"</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"clipboardထံ စာသားအားကူးယူမည်"</string>
<string name="copied" msgid="4675902854553014676">"မိတ္တူကူးပြီးပါပြီ"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> မှ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> သို့ ကူးထည့်ထားသည်"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က ဒေတာကို သင့်ကလစ်ဘုတ်မှ ကူးထည့်ထားသည်"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က သင်မိတ္တူကူးထားသော စာသားကို ထည့်လိုက်သည်"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က သင်မိတ္တူကူးထားသော ပုံကို ထည့်လိုက်သည်"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က သင်မိတ္တူကူးထားသော အကြောင်းအရာကို ထည့်လိုက်သည်"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> အားပြင်ဆင်နေသည်။"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"အက်ပ်များကို စတင်နေ"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"လုပ်ငန်းစနစ်ထည့်သွင်း၍ ပြန်လည်စတင်ရန် ပြီးပါပြီ"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ဖန်သားပြင်ကို ပိတ်မလား။"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"သင့်လက်ဗွေကို စနစ်ထည့်သွင်းနေစဉ် ဖွင့်ပိတ်ခလုတ်ကို ဖိထားပါ။\n\n၎င်းက အများအားဖြင့် သင့်ဖန်သားပြင်ကို ပိတ်ပါသည်။"</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ပိတ်ရန်"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"မလုပ်တော့"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> က အလုပ်လုပ်နေသည်"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ဂိမ်းသို့ ပြန်သွားရန် တို့ပါ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ဂိမ်းကို ရွေးခြင်း"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"သင်၏ စီမံခန့်ခွဲသူက အပ်ဒိတ်လုပ်ထားသည်"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"သင်၏ စီမံခန့်ခွဲသူက ဖျက်လိုက်ပါပြီ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"‘ဘက်ထရီ အားထိန်း’ က ‘မှောင်သည့် အပြင်အဆင်’ ကို ဖွင့်ပြီး နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့၊ ဝန်ဆောင်မှုအချို့နှင့် ကွန်ရက်ချိတ်ဆက်မှုအချို့တို့ကို ကန့်သတ်သည် သို့မဟုတ် ပိတ်သည်။\n\n"<annotation id="url">"ပိုမိုလေ့လာရန်"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"‘ဘက်ထရီ အားထိန်း’ က ‘မှောင်သည့် အပြင်အဆင်’ ကို ဖွင့်ပြီး နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့၊ ဝန်ဆောင်မှုအချို့နှင့် ကွန်ရက်ချိတ်ဆက်မှုအချို့တို့ကို ကန့်သတ်သည် သို့မဟုတ် ပိတ်သည်။"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ဒေတာအသုံးလျှော့ချနိုင်ရန်အတွက် အက်ပ်များကို နောက်ခံတွင် ဒေတာပို့ခြင်းနှင့် လက်ခံခြင်းမပြုရန် \'ဒေတာချွေတာမှု\' စနစ်က တားဆီးထားပါသည်။ ယခုအက်ပ်ဖြင့် ဒေတာအသုံးပြုနိုင်သော်လည်း အကြိမ်လျှော့၍သုံးရပါမည်။ ဥပမာ၊ သင်က မတို့မချင်း ပုံများပေါ်လာမည် မဟုတ်ပါ။"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ဒေတာချွေတာမှုစနစ် ဖွင့်မလား။"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ဖွင့်ပါ"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ကို လောလောဆယ် မရနိုင်ပါ။ ၎င်းကို <xliff:g id="APP_NAME_1">%2$s</xliff:g> က စီမံထားပါသည်။"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"ပိုမိုလေ့လာရန်"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"အက်ပ်ကို ခဏမရပ်တော့ရန်"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"အလုပ်သုံးအက်ပ်များ ဖွင့်မလား။"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"သင့်အလုပ်သုံးအက်ပ်နှင့် အကြောင်းကြားချက်များသုံးခွင့် ရယူပါ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ဖွင့်ပါ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"အက်ပ်ကို မရနိုင်ပါ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ယခု မရနိုင်ပါ။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index a6f945c..25db688 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -611,7 +611,7 @@
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon for fingeravtrykk"</string>
<string name="permlab_manageFace" msgid="4569549381889283282">"administrere maskinvare for Ansiktslås"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Lar appen bruke metoder for å legge til og slette ansiktmaler for bruk."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"bruke maskinvare for Ansiktslås"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"bruk maskinvare for Ansiktslås"</string>
<string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Lar appen bruke maskinvare for Ansiktslås til autentisering"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Ansiktslås"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Registrer ansiktet ditt på nytt"</string>
@@ -1869,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Oppdatert av administratoren din"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Slettet av administratoren din"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Batterisparing slår på mørkt tema og begrenser eller slår av bakgrunnsaktivitet, enkelte visuelle effekter, noen funksjoner og noen nettverkstilkoblinger.\n\n"<annotation id="url">"Finn ut mer"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Batterisparing slår på mørkt tema og begrenser eller slår av bakgrunnsaktivitet, enkelte visuelle effekter, noen funksjoner og noen nettverkstilkoblinger."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Datasparing hindrer noen apper fra å sende og motta data i bakgrunnen, for å redusere dataforbruket. Aktive apper kan bruke data, men kanskje ikke så mye som ellers – for eksempel vises ikke bilder før du trykker på dem."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vil du slå på Datasparing?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Slå på"</string>
@@ -1975,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er ikke tilgjengelig akkurat nå. Dette administreres av <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Finn ut mer"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Opphev pause for appen"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Vil du slå på jobbapper?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Få tilgang til jobbapper og -varsler"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Slå på"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Appen er ikke tilgjengelig"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ikke tilgjengelig for øyeblikket."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index d986f2c..d94224a 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -476,8 +476,8 @@
<string name="permdesc_callCompanionApp" msgid="8474168926184156261">"एपलाई डिभाइसमा जारी रहेका कलहरू हेर्नुका साथै तिनीहरूलाई गर्ने अनुमति दिनुहोस्। यसमा गरिएका कलहरूको सङ्ख्या र कलहरूको अवस्था जस्ता जानकारी समावेश हुन्छन्।"</string>
<string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"अडियो रेकर्ड गर्ने कार्यमा लगाइएका प्रतिबन्धहरूबाट छुट दिनुहोस्"</string>
<string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"यो एपलाई अडियो रेकर्ड गर्ने कार्यमा लगाइएका प्रतिबन्धहरूबाट छुट दिनुहोस्।"</string>
- <string name="permlab_acceptHandover" msgid="2925523073573116523">"अर्को अनुप्रयोगमा सुरु गरिएको कल जारी राख्नुहोस्"</string>
- <string name="permdesc_acceptHandovers" msgid="7129026180128626870">"यस एपलाई अर्को अनुप्रयोगमा सुरु गरिएको कल जारी राख्ने अनुमति दिन्छ।"</string>
+ <string name="permlab_acceptHandover" msgid="2925523073573116523">"अर्को एपमा सुरु गरिएको कल जारी राख्नुहोस्"</string>
+ <string name="permdesc_acceptHandovers" msgid="7129026180128626870">"यस एपलाई अर्को एपमा सुरु गरिएको कल जारी राख्ने अनुमति दिन्छ।"</string>
<string name="permlab_readPhoneNumbers" msgid="5668704794723365628">"फोन नम्बरहरू पढ्ने"</string>
<string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"उक्त एपलाई यस डिभाइसको फोन नम्बरहरूमाथि पहुँच राख्न दिनुहोस्।"</string>
<string name="permlab_wakeLock" product="automotive" msgid="1904736682319375676">"कारको स्क्रिन सक्रिय राख्नुहोस्"</string>
@@ -646,7 +646,7 @@
<string name="face_error_timeout" msgid="522924647742024699">"फेरि फेस अनलक प्रयोग गरी हेर्नुहोस्।"</string>
<string name="face_error_no_space" msgid="5649264057026021723">"अनुहारसम्बन्धी नयाँ डेटा भण्डारण गर्न सकिएन। पहिले कुनै पुरानो डेटा मेटाउनुहोस्।"</string>
<string name="face_error_canceled" msgid="2164434737103802131">"अनुहार पहिचान रद्द गरियो।"</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"प्रयोगकर्ताले फेस अनलकको कार्य रद्द गर्नुभयो।"</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"प्रयोगकर्ताले फेस अनलक रद्द गर्नुभयो।"</string>
<string name="face_error_lockout" msgid="7864408714994529437">"धेरैपटक प्रयासहरू भए। पछि फेरि प्रयास गर्नुहोस्।"</string>
<string name="face_error_lockout_permanent" msgid="8277853602168960343">"अत्यधिक प्रयासहरू भए। फेस अनलक असक्षम पारियो।"</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"अनुहार पुष्टि गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फेरि प्रयास गर्नुहोस्"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"फेरि प्रयास गर्नुहोस्"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"सबै सुविधाहरू र डेटाका लागि अनलक गर्नुहोस्"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"अत्यधिक मोहडा खोल्ने प्रयासहरू बढी भए।"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"फेस अनलक प्रयोग गरी अनलक गर्ने प्रयास अत्याधिक धेरै भयो"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM कार्ड छैन"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"ट्याब्लेटमा SIM कार्ड छैन।"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"तपाईंको Android टिभी डिभाइसमा SIM कार्ड छैन।"</string>
@@ -1009,7 +1009,7 @@
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"एपलाई तपाईंको Android टिभी डिभाइसमा भण्डार गरिएका ब्राउजरको इतिहास र पुस्तक चिन्हहरू परिमार्जन गर्ने अनुमति दिन्छ। यसले एपलाई ब्राउजरको डेटा मेटाउने वा परिमार्जन गर्ने अनुमति दिन सक्छ। ध्यान दिनुहोस्: तेस्रो पक्षीय ब्राउजर वा वेब ब्राउज गर्ने सुविधा प्रदान गर्ने अन्य एपहरूले यो अनुमति लागू गर्न सक्दैनन्।"</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="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>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"क्लिपबोर्डमा प्रतिलिप गरिएको पाठ।"</string>
<string name="copied" msgid="4675902854553014676">"प्रतिलिपि गरियो"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> मा रहेको डेटा कपी गरी <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> मा पेस्ट गरियो"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ले तपाईंको क्लिपबोर्डमा रहेको जानकारी पेस्ट गरेको छ"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ले तपाईंले कपी गरेको टेक्स्ट पेस्ट गरेको छ"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ले तपाईंले कपी गरेको एउटा फोटो पेस्ट गरेको छ"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ले तपाईंले कपी गरेको सामग्री पेस्ट गरेको छ"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> तयारी गर्दै।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"सुरुवात एपहरू।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बुट पुरा हुँदै।"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"स्क्रिन अफ गर्ने हो?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"आफ्नो फिंगरप्रिन्ट सेटअप गर्दा तपाईंले पावर बटन थिच्नुभयो।\n\nयसो गर्दा सामान्यतया तपाईंको स्क्रिन अफ हुन्छ।"</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"अफ गर्नुहोस्"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"रद्द गर्नुहोस्"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> चलिरहेको छ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"खेलमा फर्कन ट्याप गर्नुहोस्"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"खेल छनौट गर्नुहोस्"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"सर्टकट प्रयोग गर्नुहोस्"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रङ्ग उल्टाउने सुविधा"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रङ्ग सच्याउने सुविधा"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एक हाते मोड"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"अझै मधुरो"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन भयो।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अफ भयो।"</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"तपाईंका प्रशासकले अद्यावधिक गर्नुभएको"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"तपाईंका प्रशासकले मेट्नुभएको"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ठिक छ"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ब्याट्री सेभरले अँध्यारो थिम अन गर्छ र ब्याकग्राउन्डमा हुने क्रियाकलाप, केही भिजुअल इफेक्ट, निश्चित सुविधा र केही नेटवर्क कनेक्सनहरू अफ गर्छ वा सीमित रूपमा मात्र चल्न दिन्छ।\n\n"<annotation id="url">"थप जान्नुहोस्"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"ब्याट्री सेभरले अँध्यारो थिम अन गर्छ र ब्याकग्राउन्डमा हुने क्रियाकलाप, केही भिजुअल इफेक्ट, निश्चित सुविधा र केही नेटवर्क कनेक्सनहरू अफ गर्छ वा सीमित रूपमा मात्र चल्न दिन्छ।"</string>
<string name="data_saver_description" msgid="4995164271550590517">"डेटा सेभरले डेटा खपत कम गर्न केही एपहरूलाई ब्याकग्राउन्डमा डेटा पठाउन वा प्राप्त गर्न दिँदैन। तपाईंले अहिले प्रयोग गरिरहनुभएको एपले सीमित रूपमा मात्र डेटा चलाउन पाउँछ। उदाहरणका लागि, तपाईंले फोटोमा ट्याप गर्नुभयो भने मात्र फोटो देखिन्छ नत्र देखिँदैन।"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा सेभर अन गर्ने हो?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"सक्रिय गर्नुहोस्"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> अहिले उपलब्ध छैन। यो <xliff:g id="APP_NAME_1">%2$s</xliff:g> द्वारा व्यवस्थित छ।"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"थप जान्नुहोस्"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"एपको पज हटाउनुहोस्"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"कामसम्बन्धी एपहरू सक्षम पार्ने हो?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"कामसम्बन्धी एप चलाउने र सूचना प्राप्त गर्ने सुविधा अन गर्नुहोस्"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"सक्रिय गर्नुहोस्"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"एप उपलब्ध छैन"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> अहिले उपलब्ध छैन।"</string>
@@ -2074,7 +2064,7 @@
<string name="slice_more_content" msgid="3377367737876888459">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"या त एपको संस्करण स्तरह्रास गरियो वा यो यस सर्टकटसँग मिल्दो छैन"</string>
<string name="shortcut_restore_not_supported" msgid="4763198938588468400">"अनुप्रयोगले ब्याकअप तथा पुनर्स्थापनालाई समर्थन नगर्ने हुँदा सर्टकट पुनर्स्थापित गर्न सकिएन"</string>
- <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"अनुप्रयोगमा प्रयोग गरिने हस्ताक्षर नमिल्ने हुँदा सर्टकट पुनर्स्थापित गर्न सकिएन"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"एपमा प्रयोग गरिने हस्ताक्षर नमिल्ने हुँदा सर्टकट पुनर्स्थापित गर्न सकिएन"</string>
<string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"सर्टकट पुनर्स्थापित गर्न सकिएन"</string>
<string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"सर्टकट असक्षम पारिएको छ"</string>
<string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"स्थापना रद्द गर्नु…"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 15a0531..6ce5f3c 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -609,11 +609,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ଟିପଚିହ୍ନ ଆଇକନ୍"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"ଫେସ୍ ଅନ୍ଲକ୍ ହାର୍ଡୱେର୍ ପରିଚାଳନା କରନ୍ତୁ"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"ଫେସ୍ ଅନଲକ୍ ହାର୍ଡୱେର୍ ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"ବ୍ୟବହାର ପାଇଁ ଆପ୍କୁ ଫେସିଆଲ୍ ଟେମ୍ପଲେଟ୍ ଯୋଡିବା ଓ ଡିଲିଟ୍ ର ପଦ୍ଧତି ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ଫେସ୍ ଅନ୍ଲକ୍ ହାର୍ଡୱେର୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"ପ୍ରାମାଣିକତା ପାଇଁ ଫେସ୍ ଅନ୍ଲକ୍ ହାର୍ଡୱେର୍ର ବ୍ୟବହାର ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ଫେସ୍ ଅନ୍ଲକ୍"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ଫେସ୍ ଅନଲକ୍ ହାର୍ଡୱେର୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"ପ୍ରମାଣୀକରଣ ପାଇଁ ଫେସ୍ ଅନଲକ୍ ହାର୍ଡୱେର୍ର ବ୍ୟବହାର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ଫେସ୍ ଅନଲକ୍"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ଆପଣଙ୍କର ମୁହଁ ପୁଣି-ଏନ୍ରୋଲ୍ କରନ୍ତୁ"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"ଚିହ୍ନଟକରଣକୁ ଉନ୍ନତ କରିବା ପାଇଁ, ଦୟାକରି ଆପଣଙ୍କର ମୁହଁ ପୁଣି-ଏନ୍ରୋଲ୍ କରନ୍ତୁ।"</string>
<string name="face_setup_notification_title" msgid="550617822603450009">"ଫେସ୍ ଅନଲକ୍ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
@@ -643,15 +643,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"ମୁହଁ ଚିହ୍ନଟ କରିପାରିଲା ନାହିଁ। ହାର୍ଡୱେୟାର୍ ଉପଲବ୍ଧ ନାହିଁ।"</string>
- <string name="face_error_timeout" msgid="522924647742024699">"ଫେସ୍ ଅନ୍ଲକ୍ ପୁଣି ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ"</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"ଫେସ୍ ଅନଲକ୍ ପୁଣି ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ।"</string>
<string name="face_error_no_space" msgid="5649264057026021723">"ନୂଆ ମୁହଁ ଡାଟା ଷ୍ଟୋର୍ ହେବ ନାହିଁ। ପ୍ରଥମେ ପୁରୁଣାକୁ ଡିଲିଟ୍ କରନ୍ତୁ।"</string>
<string name="face_error_canceled" msgid="2164434737103802131">"ଫେସ୍ର ଅପରେଶନ୍ କ୍ୟାନ୍ସଲ୍ ହୋଇଗଲା"</string>
<string name="face_error_user_canceled" msgid="8553045452825849843">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ଦ୍ୱାରା ଫେସ୍ ଅନଲକ୍ ବାତିଲ୍ କରାଯାଇଛି।"</string>
<string name="face_error_lockout" msgid="7864408714994529437">"ବାରମ୍ବାର ଚେଷ୍ଟା। ପରେ ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"ଅତ୍ୟଧିକ ପ୍ରଚେଷ୍ଟା. ଫେସ୍ ଅନ୍ଲକ୍ ଅକ୍ଷମ କରନ୍ତୁ"</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"ଅତ୍ୟଧିକ ପ୍ରଚେଷ୍ଟା। ଫେସ୍ ଅନଲକ୍ ଅକ୍ଷମ ହୋଇଛି।"</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"ମୁହଁ ଚିହ୍ନଟ କରିପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"ଆପଣ ଫେସ୍ ଅନ୍ଲକ୍ ସେଟ୍ ଅପ୍ କରିନାହାଁନ୍ତି"</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"ଏହି ଡିଭାଇସ୍ରେ ଫେସ୍ ଅନ୍ଲକ୍ ସମର୍ଥିତ ନୁହେଁ।"</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"ଆପଣ ଫେସ୍ ଅନଲକ୍ ସେଟ୍ ଅପ୍ କରିନାହାଁନ୍ତି।"</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"ଏହି ଡିଭାଇସରେ ଫେସ୍ ଅନଲକ୍ ସମର୍ଥିତ ନୁହେଁ।"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
<string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g>ଙ୍କ ଫେସ୍"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"ଫେସ୍ ଅନଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"ସମସ୍ତ ସୁବିଧା ତଥା ଡାଟା ପାଇଁ ଅନଲକ୍ କରନ୍ତୁ"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"ମାଲିକର ମୁହଁ ଚିହ୍ନି ଅନଲକ୍ କରିବାର ସର୍ବାଧିକ ଧାର୍ଯ୍ୟ ସୀମା ଅତିକ୍ରମ କଲା"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"ସର୍ବାଧିକ ଫେସ୍ ଅନଲକ୍ ପ୍ରଚେଷ୍ଟା ଅତିକ୍ରମ କରିଛି"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"କୌଣସି SIM କାର୍ଡ ନାହିଁ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"ଟାବଲେଟ୍ରେ କୌଣସି SIM କାର୍ଡ ନାହିଁ।"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ରେ କୌଣସି SIM କାର୍ଡ ନାହିଁ।"</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ଶର୍ଟକଟ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ରଙ୍ଗ ବଦଳାଇବାର ସୁବିଧା"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ଏକ-ହାତ ମୋଡ୍"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ଅତିରିକ୍ତ ଡିମ୍"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ ହୋଇଛି।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ ହୋଇଛି।"</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ଆପଣଙ୍କ ଆଡମିନ୍ ଅପଡେଟ୍ କରିଛନ୍ତି"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ଆପଣଙ୍କ ଆଡମିନ୍ ଡିଲିଟ୍ କରିଛନ୍ତି"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ଠିକ୍ ଅଛି"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ବ୍ୟାଟେରୀ ସେଭର୍ ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ଇଫେକ୍ଟ, କିଛି ଫିଚର୍ ଏବଂ କିଛି ନେଟୱାର୍କ ସଂଯୋଗକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ।\n\n"<annotation id="url">"ଅଧିକ ଜାଣନ୍ତୁ"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"ବ୍ୟାଟେରୀ ସେଭର୍ ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ଇଫେକ୍ଟ, କିଛି ଫିଚର୍ ଏବଂ କିଛି ନେଟୱାର୍କ ସଂଯୋଗକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ।"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ଡାଟା ବ୍ୟବହାର କମ୍ କରିବାରେ ସାହାଯ୍ୟ କରିବାକୁ, ଡାଟା ସେଭର୍ ବ୍ୟାକ୍ଗ୍ରାଉଣ୍ଡରେ ଡାଟା ପଠାଇବା କିମ୍ବା ପ୍ରାପ୍ତ କରିବାକୁ କିଛି ଆପ୍କୁ ବାରଣ କରେ। ଆପଣ ବର୍ତ୍ତମାନ ବ୍ୟବହାର କରୁଥିବା ଆପ୍, ଡାଟା ଆକ୍ସେସ୍ କରିପାରେ, କିନ୍ତୁ ଏହା କମ୍ ଥର କରିପାରେ। ଏହାର ଅର୍ଥ ହୋଇପାରେ ଯେମିତି ଆପଣ ଇମେଜଗୁଡ଼ିକୁ ଟାପ୍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ସେଗୁଡ଼ିକ ଡିସପ୍ଲେ ହୁଏ ନାହିଁ।"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ଡାଟା ସେଭର୍ ଚାଲୁ କରିବେ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ଚାଲୁ କରନ୍ତୁ"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"ବର୍ତ୍ତମାନ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ଉପଲବ୍ଧ ନାହିଁ। ଏହା <xliff:g id="APP_NAME_1">%2$s</xliff:g> ଦ୍ଵାରା ପରିଚାଳିତ ହେଉଛି।"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ଆପ୍ ଅନପଜ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"ୱାର୍କ ଆପଗୁଡ଼ିକୁ ଚାଲୁ କରିବେ?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"ଆପଣଙ୍କ ୱାର୍କ ଆପ୍ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଆକ୍ସେସ୍ ପାଆନ୍ତୁ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ଚାଲୁ କରନ୍ତୁ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ଆପ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବର୍ତ୍ତମାନ ଉପଲବ୍ଧ ନାହିଁ।"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 50e1f98..ba4730c 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਤੀਕ"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"ਚਿਹਰਾ ਅਣਲਾਕ ਹਾਰਡਵੇਅਰ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"ਫ਼ੇਸ ਅਣਲਾਕ ਹਾਰਡਵੇਅਰ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"ਐਪ ਨੂੰ ਵਰਤਣ ਲਈ ਚਿਹਰਾ ਟੈਮਪਲੇਟ ਸ਼ਾਮਲ ਕਰਨ ਜਾਂ ਮਿਟਾਉਣ ਦੀਆਂ ਵਿਧੀਆਂ ਦੀ ਬੇਨਤੀ ਕਰਨ ਦਿੰਦੀ ਹੈ।"</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ਚਿਹਰਾ ਅਣਲਾਕ ਹਾਰਡਵੇਅਰ ਵਰਤੋ"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"ਐਪ ਨੂੰ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਚਿਹਰਾ ਅਣਲਾਕ ਹਾਰਡਵੇਅਰ ਵਰਤਣ ਦਿੰਦੀ ਹੈ"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ਚਿਹਰਾ ਅਣਲਾਕ"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ਫ਼ੇਸ ਅਣਲਾਕ ਹਾਰਡਵੇਅਰ ਵਰਤੋ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"ਐਪ ਨੂੰ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਫ਼ੇਸ ਅਣਲਾਕ ਹਾਰਡਵੇਅਰ ਵਰਤਣ ਦਿੰਦੀ ਹੈ"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ਫ਼ੇਸ ਅਣਲਾਕ"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ਆਪਣਾ ਚਿਹਰਾ ਮੁੜ-ਦਰਜ ਕਰੋ"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"ਪਛਾਣ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਚਿਹਰੇ ਨੂੰ ਮੁੜ-ਦਰਜ ਕਰੋ"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"ਚਿਹਰਾ ਅਣਲਾਕ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"ਫ਼ੇਸ ਅਣਲਾਕ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ਆਪਣੇ ਫ਼ੋਨ ਵੱਲ ਦੇਖ ਕੇ ਇਸਨੂੰ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ਅਣਲਾਕ ਕਰਨ ਦੇ ਹੋਰ ਤਰੀਕਿਆਂ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"ਚਿਹਰੇ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਹੋ ਸਕੀ। ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ।"</string>
- <string name="face_error_timeout" msgid="522924647742024699">"ਚਿਹਰਾ ਅਣਲਾਕ ਦੁਬਾਰਾ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"ਫ਼ੇਸ ਅਣਲਾਕ ਦੁਬਾਰਾ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
<string name="face_error_no_space" msgid="5649264057026021723">"ਨਵਾਂ ਚਿਹਰਾ ਡਾਟਾ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਪਹਿਲਾਂ ਪੁਰਾਣਾ ਹਟਾਓ।"</string>
<string name="face_error_canceled" msgid="2164434737103802131">"ਚਿਹਰਾ ਪਛਾਣਨ ਦੀ ਪ੍ਰਕਿਰਿਆ ਰੱਦ ਕੀਤੀ ਗਈ।"</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"ਵਰਤੋਂਕਾਰ ਨੇ ਚਿਹਰਾ ਅਣਲਾਕ ਰੱਦ ਕੀਤਾ।"</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"ਵਰਤੋਂਕਾਰ ਨੇ ਫ਼ੇਸ ਅਣਲਾਕ ਰੱਦ ਕੀਤਾ।"</string>
<string name="face_error_lockout" msgid="7864408714994529437">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਚਿਹਰਾ ਅਣਲਾਕ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਫ਼ੇਸ ਅਣਲਾਕ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"ਚਿਹਰੇ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"ਤੁਸੀਂ ਚਿਹਰਾ ਅਣਲਾਕ ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਹੈ।"</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਚਿਹਰਾ ਅਣਲਾਕ ਦੀ ਸੁਵਿਧਾ ਨਹੀਂ ਹੈ।"</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"ਤੁਸੀਂ ਫ਼ੇਸ ਅਣਲਾਕ ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਹੈ।"</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫ਼ੇਸ ਅਣਲਾਕ ਦੀ ਸੁਵਿਧਾ ਨਹੀਂ ਹੈ।"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"ਸੈਂਸਰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="face_name_template" msgid="3877037340223318119">"ਚਿਹਰਾ <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"ਚਿਹਰਾ ਅਣਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ਚਿਹਰਾ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ਫ਼ੇਸ ਅਣਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ਫ਼ੇਸ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣੇ ਚਿਹਰੇ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣਾ ਚਿਹਰਾ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"ਸਾਰੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਅਤੇ ਡਾਟੇ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"ਅਧਿਕਤਮ ਚਿਹਰਾ ਅਣਲਾਕ ਕੋਸ਼ਿਸ਼ਾਂ ਵਧੀਆਂ"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"ਫ਼ੇਸ ਅਣਲਾਕ ਦੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ ਸੀਮਾ ਤੋਂ ਪਾਰ ਹੋ ਗਈਆਂ"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"ਕੋਈ ਸਿਮ ਕਾਰਡ ਨਹੀਂ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"ਟੈਬਲੈੱਟ ਵਿੱਚ ਕੋਈ ਸਿਮ ਕਾਰਡ ਨਹੀਂ ਹੈ।"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"ਤੁਹਾਡੇ Android TV ਡੀਵਾਈਸ ਵਿੱਚ ਕੋਈ ਸਿਮ ਕਾਰਡ ਮੌਜੂਦ ਨਹੀਂ।"</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"ਅਣਲਾਕ ਖੇਤਰ ਦਾ ਵਿਸਤਾਰ ਕਰੋ।"</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"ਅਣਲਾਕ ਸਲਾਈਡ ਕਰੋ।"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"ਪੈਟਰਨ ਅਣਲਾਕ।"</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"ਚਿਹਰਾ ਅਣਲਾਕ।"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"ਫ਼ੇਸ ਅਣਲਾਕ।"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"ਪਿੰਨ ਅਣਲਾਕ।"</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"ਸਿਮ ਪਿੰਨ ਅਣਲਾਕ।"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"ਸਿਮ Puk ਅਣਲਾਕ।"</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"ਟੈਕਸਟ ਕਲਿਪਬੋਰਡ ਤੇ ਕਾਪੀ ਕੀਤਾ।"</string>
<string name="copied" msgid="4675902854553014676">"ਕਾਪੀ ਕੀਤੀ ਗਈ"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> ਤੋਂ ਕਾਪੀ ਕੀਤੇ ਡਾਟੇ ਨੂੰ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ਵਿੱਚ ਪੇਸਟ ਕੀਤਾ ਗਿਆ"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ਨੇ ਤੁਹਾਡੇ ਕਲਿੱਪਬੋਰਡ ਤੋਂ ਪੇਸਟ ਕੀਤਾ"</string>
<string name="pasted_text" msgid="4298871641549173733">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਪੀ ਕੀਤੀ ਗਈ ਲਿਖਤ ਨੂੰ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ਨੇ ਪੇਸਟ ਕੀਤਾ"</string>
<string name="pasted_image" msgid="4729097394781491022">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਪੀ ਕੀਤੇ ਗਏ ਚਿੱਤਰ ਨੂੰ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ਨੇ ਪੇਸਟ ਕੀਤਾ"</string>
<string name="pasted_content" msgid="646276353060777131">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਪੀ ਕੀਤੀ ਗਈ ਸਮੱਗਰੀ ਨੂੰ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ਨੇ ਪੇਸਟ ਕੀਤਾ"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> ਤਿਆਰ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ਐਪਸ ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ਬੂਟ ਪੂਰਾ ਕਰ ਰਿਹਾ ਹੈ।"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ਕੀ ਸਕ੍ਰੀਨ ਬੰਦ ਕਰਨੀ ਹੈ?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨ ਵੇਲੇ, ਤੁਸੀਂ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦਬਾਇਆ ਹੈ।\n\nਇਹ ਆਮ ਤੌਰ \'ਤੇ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ਬੰਦ ਕਰੋ"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ਰੱਦ ਕਰੋ"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ਚੱਲ ਰਿਹਾ ਹੈ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ਗੇਮ \'ਤੇ ਵਾਪਸ ਜਾਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ਗੇਮ ਚੁਣੋ"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਮਿਟਾਇਆ ਗਿਆ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ਠੀਕ ਹੈ"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ਬੈਟਰੀ ਸੇਵਰ ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ, ਕੁਝ ਖਾਸ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਅਤੇ ਕੁਝ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨਾਂ ਨੂੰ ਸੀਮਤ ਜਾਂ ਬੰਦ ਕਰਦਾ ਹੈ।\n\n"<annotation id="url">"ਹੋਰ ਜਾਣੋ"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"ਬੈਟਰੀ ਸੇਵਰ ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ, ਕੁਝ ਖਾਸ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਅਤੇ ਕੁਝ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨਾਂ ਨੂੰ ਸੀਮਤ ਜਾਂ ਬੰਦ ਕਰਦਾ ਹੈ।"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ਡਾਟਾ ਵਰਤੋਂ ਘਟਾਉਣ ਵਿੱਚ ਮਦਦ ਲਈ, ਡਾਟਾ ਸੇਵਰ ਕੁਝ ਐਪਾਂ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਡਾਟਾ ਭੇਜਣ ਜਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਤੋਂ ਰੋਕਦਾ ਹੈ। ਤੁਹਾਡੇ ਵੱਲੋਂ ਵਰਤਮਾਨ ਤੌਰ \'ਤੇ ਵਰਤੀ ਜਾ ਰਹੀ ਐਪ ਡਾਟਾ \'ਤੇ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ, ਪਰ ਉਹ ਇੰਝ ਕਦੇ-ਕਦਾਈਂ ਕਰ ਸਕਦੀ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਸ ਦਾ ਮਤਲਬ ਇਹ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਚਿੱਤਰ ਤਦ ਤੱਕ ਨਹੀਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕੀਤੇ ਜਾਂਦੇ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਉਹਨਾਂ \'ਤੇ ਟੈਪ ਨਹੀਂ ਕਰਦੇ।"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ਕੀ ਡਾਟਾ ਸੇਵਰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ਚਾਲੂ ਕਰੋ"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ਐਪ ਫਿਲਹਾਲ ਉਪਲਬਧ ਨਹੀਂ ਹੈ। ਇਸਦਾ ਪ੍ਰਬੰਧਨ <xliff:g id="APP_NAME_1">%2$s</xliff:g> ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"ਹੋਰ ਜਾਣੋ"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ਐਪ ਤੋਂ ਰੋਕ ਹਟਾਓ"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਚਾਲੂ ਕਰਨੀਆਂ ਹਨ?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"ਆਪਣੀਆਂ ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ਚਾਲੂ ਕਰੋ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਇਸ ਵੇਲੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index b77be3d..0c143e8 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -649,7 +649,7 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Nie można zweryfikować twarzy. Sprzęt niedostępny."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Spróbuj rozpoznania twarzy ponownie."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Spróbuj rozpoznawania twarzy ponownie."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Nie można przechowywać nowych danych twarzy. Usuń stare."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Analiza twarzy została anulowana."</string>
<string name="face_error_user_canceled" msgid="8553045452825849843">"Użytkownik anulował rozpoznawanie twarzy."</string>
@@ -1915,10 +1915,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Zaktualizowany przez administratora"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Usunięty przez administratora"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne, pewne funkcje oraz wybrane połączenia sieciowe.\n\n"<annotation id="url">"Więcej informacji"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne, pewne funkcje oraz wybrane połączenia sieciowe."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Oszczędzanie danych uniemożliwia niektórym aplikacjom wysyłanie i odbieranie danych w tle, zmniejszając w ten sposób ich użycie. Aplikacja, z której w tej chwili korzystasz, może uzyskiwać dostęp do danych, ale rzadziej. Może to powodować, że obrazy będą się wyświetlać dopiero po kliknięciu."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Włączyć Oszczędzanie danych?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Włącz"</string>
@@ -2039,10 +2037,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacja <xliff:g id="APP_NAME_0">%1$s</xliff:g> nie jest teraz dostępna. Zarządza tym aplikacja <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Więcej informacji"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Wznów działanie aplikacji"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Włączyć aplikacje służbowe?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Uzyskaj dostęp do aplikacji służbowych i powiadomień"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Włącz"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacja jest niedostępna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest obecnie niedostępna."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index bc0342a..8f24a7c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tente novamente"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Tente novamente"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Desbloqueio para todos os recursos e dados"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"O número máximo de tentativas de Desbloqueio por reconhecimento facial foi excedido"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"O número máximo de tentativas de desbloqueio por reconhecimento facial foi excedido"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Sem chip"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Não há um chip no tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Nenhum chip no seu dispositivo Android TV."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index db561c9..3e29cb0 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1869,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu gestor"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado pelo seu gestor"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"A Poupança de bateria ativa o tema escuro e limita ou desativa a atividade em segundo plano, alguns efeitos visuais, determinadas funcionalidades e algumas ligações de rede.\n\n"<annotation id="url">"Saiba mais"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"A Poupança de bateria ativa o tema escuro e limita ou desativa a atividade em segundo plano, alguns efeitos visuais, determinadas funcionalidades e algumas ligações de rede."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir a utilização de dados, a Poupança de dados impede que algumas aplicações enviem ou recebam dados em segundo plano. Uma determinada app que esteja a utilizar atualmente pode aceder aos dados, mas é possível que o faça com menos frequência. Isto pode significar, por exemplo, que as imagens não são apresentadas até que toque nas mesmas."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Pretende ativar a Poupança de dados?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string>
@@ -1975,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> não está disponível neste momento. A app <xliff:g id="APP_NAME_1">%2$s</xliff:g> gere esta definição."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Saiba mais"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Retomar app"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Ativar as apps de trabalho?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Obtenha acesso às suas apps de trabalho e notificações"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Ativar"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"A app não está disponível"</string>
<string name="app_blocked_message" msgid="542972921087873023">"De momento, a app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index bc0342a..8f24a7c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tente novamente"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Tente novamente"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Desbloqueio para todos os recursos e dados"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"O número máximo de tentativas de Desbloqueio por reconhecimento facial foi excedido"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"O número máximo de tentativas de desbloqueio por reconhecimento facial foi excedido"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Sem chip"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Não há um chip no tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Nenhum chip no seu dispositivo Android TV."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 022e1ca..a202de6 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -658,7 +658,7 @@
<string name="face_error_security_update_required" msgid="5076017208528750161">"Senzorul este dezactivat temporar."</string>
<string name="face_name_template" msgid="3877037340223318119">"Chip <xliff:g id="FACEID">%d</xliff:g>"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"Folosiți deblocarea facială"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Folosiți blocarea ecranului sau blocarea facială"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Folosiți deblocarea facială sau ecranul de blocare"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Folosiți-vă chipul ca să continuați"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Folosiți-vă chipul sau blocarea ecranului pentru a continua"</string>
<string-array name="face_error_vendor">
@@ -1025,8 +1025,7 @@
<string name="text_copied" msgid="2531420577879738860">"Text copiat în clipboard."</string>
<string name="copied" msgid="4675902854553014676">"Copiat"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat date din <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat din clipboard"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat textul copiat"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat o imagine copiată"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat conținutul copiat"</string>
@@ -1280,14 +1279,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Se pregătește <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Se pornesc aplicațiile."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Se finalizează pornirea."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Dezactivați ecranul?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ați apăsat butonul de pornire în timpul configurării amprentei.\n\nDe obicei, această acțiune dezactivează ecranul."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Dezactivați"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Anulați"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Rulează <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Atingeți pentru a reveni la joc"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Alegeți jocul"</string>
@@ -1740,8 +1735,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizați comanda rapidă"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversarea culorilor"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corecția culorii"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modul cu o mână"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminozitate redusă suplimentar"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -1898,10 +1892,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizat de administratorul dvs."</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Șters de administratorul dvs."</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Economisirea bateriei activează tema întunecată și restricționează sau dezactivează activitatea în fundal, unele efecte vizuale, alte funcții și câteva conexiuni de rețea.\n\n"<annotation id="url">"Aflați mai multe"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Economisirea bateriei activează tema întunecată și restricționează sau dezactivează activitatea în fundal, unele efecte vizuale, alte funcții și câteva conexiuni la rețea."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Pentru a contribui la reducerea utilizării de date, Economizorul de date împiedică unele aplicații să trimită sau să primească date în fundal. O aplicație pe care o folosiți poate accesa datele, însă mai rar. Aceasta poate însemna, de exemplu, că imaginile se afișează numai după ce le atingeți."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activați Economizorul de date?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activați"</string>
@@ -2013,10 +2005,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Momentan, aplicația <xliff:g id="APP_NAME_0">%1$s</xliff:g> nu este disponibilă. Aceasta este gestionată de <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Aflați mai multe"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Anulați întreruperea aplicației"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Activați aplicațiile pentru lucru?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Obțineți acces la aplicațiile pentru lucru și notificări"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activați"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplicația nu este disponibilă"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu este disponibilă momentan."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 86a0c0d..9fe0f7e 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -618,7 +618,7 @@
<string name="permlab_manageFace" msgid="4569549381889283282">"Управление аппаратным обеспечением для функции \"Фейсконтроль\""</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Приложение сможет добавлять и удалять шаблоны лиц."</string>
<string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"Использование аппаратного обеспечения для функции \"Фейсконтроль\""</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Приложение сможет использовать для аутентификации аппаратное обеспечение Фейсконтроля."</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Приложение сможет использовать для аутентификации аппаратное обеспечение фейсконтроля."</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Фейсконтроль"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Зарегистрируйте лицо ещё раз"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Чтобы улучшить распознавание лица, зарегистрируйте его ещё раз"</string>
@@ -649,7 +649,7 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Не удалось распознать лицо. Сканер недоступен."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Попробуйте воспользоваться функцией ещё раз."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Попробуйте воспользоваться фейсконтролем ещё раз."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Недостаточно места. Удалите старые данные для распознавания."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Распознавание отменено"</string>
<string name="face_error_user_canceled" msgid="8553045452825849843">"Фейсконтроль: операция отменена пользователем."</string>
@@ -893,7 +893,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Повторите попытку"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Повторите попытку"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Разблок. для доступа ко всем функциям и данным"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Все попытки войти с помощью Фейсконтроля использованы"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Все попытки войти с помощью фейсконтроля использованы"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Нет SIM-карты"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"SIM-карта не установлена."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"В устройстве Android TV нет SIM-карты."</string>
@@ -1028,8 +1028,7 @@
<string name="text_copied" msgid="2531420577879738860">"Текст скопирован в буфер обмена."</string>
<string name="copied" msgid="4675902854553014676">"Скопировано"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Данные из приложения \"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>\" вставлены в приложение \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\"."</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Приложение \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\" вставило данные из буфера обмена."</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: скопированный текст вставлен"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: скопированное изображение вставлено"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: скопированный контент вставлен"</string>
@@ -1300,14 +1299,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Подготовка приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"..."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск приложений."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Окончание загрузки..."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Отключить экран?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Во время настройки отпечатка пальца вы нажали кнопку питания.\n\nОбычно это действие приводит к отключению экрана."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Отключить"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Отмена"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Приложение <xliff:g id="APP">%1$s</xliff:g> запущено"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Нажмите, чтобы вернуться в игру."</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Выберите игру"</string>
@@ -1762,8 +1757,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Использовать быстрое включение"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверсия цветов"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Коррекция цвета"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим управления одной рукой"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнительное уменьшение яркости"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" включена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" отключена."</string>
@@ -1921,10 +1915,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Обновлено администратором"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Удалено администратором"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"В режиме энергосбережения включается тёмная тема, ограничиваются или отключаются фоновые процессы, некоторые визуальные эффекты, определенные функции и ряд сетевых подключений.\n\n"<annotation id="url">"Подробнее…"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"В режиме энергосбережения включается тёмная тема, ограничиваются или отключаются фоновые процессы, некоторые визуальные эффекты, определенные функции и ряд сетевых подключений."</string>
<string name="data_saver_description" msgid="4995164271550590517">"В режиме экономии трафика фоновая передача данных для некоторых приложений отключена. Приложение, которым вы пользуетесь, может получать и отправлять данные, но реже, чем обычно. Например, изображения могут не загружаться, пока вы не нажмете на них."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Включить экономию трафика?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Включить"</string>
@@ -2045,10 +2037,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Приложение \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" недоступно. Его работу ограничивает приложение \"<xliff:g id="APP_NAME_1">%2$s</xliff:g>\"."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Подробнее"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Возобновить работу приложения"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Включить рабочие приложения?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Вы получите доступ к рабочим приложениям и уведомлениям"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Включить"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Приложение недоступно"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" сейчас недоступно."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 5d38e46..e5e0367 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ඇඟිලි සලකුණු නිරූපකය"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"මුහුණු අඟුලු ඇරීමේ දෘඪාංග කළමනා කරන්න"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"මුහුණෙන් අගුළු හැරීම දෘඪාංග කළමනා කරන්න"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"මුහුණු අච්චු එකතු කිරීමට සහ ඉවත් කිරීමට අදාළ ක්රම භාවිතය සඳහා මෙම යෙදුමට ඉඩ දෙයි."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"මුහුණු අඟුලු ඇරීමේ දෘඪාංග භෘවිත කරන්න"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"සත්යාපනය සඳහා මුහුණු අඟුලු ඇරීමේ දෘඪාංග භාවිත කිරීමට යෙදුමට ඉඩ දෙයි"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"මුහුණු අඟුලු ඇරීම"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"මුහුණෙන් අගුළු හැරීමේ දෘඪාංග භෘවිත කරන්න"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"සත්යාපනය සඳහා මුහුණෙන් අගුළු හැරීමේ දෘඪාංග භාවිත කිරීමට යෙදුමට ඉඩ දෙයි"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"මුහුණෙන් අගුළු හැරීම"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ඔබේ මුහුණ යළි ලියාපදිංචි කරන්න"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"හඳුනා ගැනීම වැඩිදියුණු කිරීමට, ඔබේ මුහුණ යළි-ලියාපදිංචි කරන්න"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"මුහුණු අඟුලු හැරීම පිහිටුවන්න"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"මුහුණෙන් අගුළු හැරීම පිහිටුවන්න"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ඔබගේ දුරකථනය දෙස බැලීමෙන් එහි අගුලු හරින්න"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"අගුලු හැරීමට තවත් ක්රම සකසන්න"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ඇඟිලි සලකුණක් එක් කිරීමට තට්ටු කරන්න"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"මුහුණ සත්යාපනය කළ නොහැක. දෘඩාංගය නොමැත."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"නැවතත් මුහුණු අඟුලු ඇරීම උත්සාහ කරන්න."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"නැවතත් මුහුණෙන් අගුළු හැරීම උත්සාහ කරන්න."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"නව මුහුණු දත්ත ගබඩා කළ නොහැක. පළමුව පැරණි එකක් මකන්න."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"මුහුණු මෙහෙයුම අවලංගු කරන ලදී."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"පරිශීලකයා මුහුණු අඟුලු ඇරීම අවලංගු කර ඇත."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"පරිශීලකයා මුහුණෙන් අගුළු හැරීම අවලංගු කර ඇත."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"උත්සාහයන් ඉතා වැඩි ගණනකි. පසුව නැවත උත්සාහ කරන්න."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"ප්රයත්න ගණන වැඩියි. මුහුණු අඟුලු ඇරීම අබලයි."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"ප්රයත්න ගණන වැඩියි. මුහුණෙන් අගුළු හැරීම අබලයි."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"මුහුණ සත්යාපන කළ නොහැක. නැවත උත්සාහ කරන්න."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"ඔබ මුහුණු අඟුලු ඇරීම සකසා නැත"</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"මෙම උපාංගයෙහි මුහුණු අඟුලු ඇරීමට සහය නොදැක්වේ"</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"ඔබ මුහුණෙන් අගුළු හැරීම සකසා නැත"</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"මෙම උපාංගයෙහි මුහුණෙන් අගුළු හැරීම සහය නොදැක්වේ"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"සංවේදකය තාවකාලිකව අබල කර ඇත."</string>
<string name="face_name_template" msgid="3877037340223318119">"මුහුණු <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"මුහුණු අගුලු හැරීම භාවිත කරන්න"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"මුහුණු අගුලු හැරීම හෝ තිර අගුල භාවිත කරන්න"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"මුහුණෙන් අගුළු හැරීම භාවිත කරන්න"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"මුහුණෙන් අගුළු හැරීම හෝ තිර අගුල භාවිත කරන්න"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"ඉදිරියට යාමට ඔබගේ මුහුණ භාවිත කරන්න"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"ඉදිරියට යාමට ඔබගේ මුහුණු හෝ තිර අගුල භාවිත කරන්න"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"නැවත උත්සාහ කරන්න"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"නැවත උත්සාහ කරන්න"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"සියලු විශේෂාංග සහ දත්ත අනවහිර කරන්න"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"මුහුණ භාවිතයෙන් අඟුළු හැරීමේ උපරිම ප්රයන්තයන් ගණන ඉක්මවා ඇත"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"මුහුණෙන් අගුළු හැරීමේ උපරිම ප්රයන්තයන් ගණන ඉක්මවා ඇත"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM පත නැත"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"ටැබ්ලටයේ SIM පත නොමැත."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"ඔබගේ Android TV උපාංගයේ SIM කාඩ්පතක් නොමැත."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"අගුළු නොදැමූ ප්රදේශය පුළුල් කරන්න."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"සර්පණ අගුළු ඇරීම."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"රටා අගුළු ඇරීම."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"මුහුණ භාවිතයෙන් අඟුළු හැරීම."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"මුහුණෙන් අගුළු හැරීම."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"PIN අගුළු ඇරීම."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Sim Pin අගුලු දැමීම."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Sim Puk අගුලු දැමීම."</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"කෙටිමඟ භාවිතා කරන්න"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"වර්ණ අපවර්තනය"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"වර්ණ නිවැරදි කිරීම"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"තනි අත් ප්රකාරය"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"තවත් අඳුරු"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාත්මකයි."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාවිරහිතයි."</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ඔබගේ පරිපාලක මඟින් යාවත්කාලීන කර ඇත"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ඔබගේ පරිපාලක මඟින් මකා දමා ඇත"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"හරි"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"බැටරි සුරැකුම අඳුරු තේමාව ක්රියාත්මක කර පසුබිම් ක්රියාකාරකම්, සමහර දෘශ්ය ප්රයෝග, යම් විශේෂාංග සහ සමහර ජාල සම්බන්ධතා සීමා හෝ ක්රියාවිරහිත කරයි.\n\n"<annotation id="url">"තව දැන ගන්න"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"බැටරි සුරැකුම අඳුරු තේමාව ක්රියාත්මක කර පසුබිම් ක්රියාකාරකම්, සමහර දෘශ්ය ප්රයෝග, යම් විශේෂාංග සහ සමහර ජාල සම්බන්ධතා සීමා හෝ ක්රියාවිරහිත කරයි."</string>
<string name="data_saver_description" msgid="4995164271550590517">"දත්ත භාවිතය අඩු කිරීමට උදවු වීමට, දත්ත සුරැකුම සමහර යෙදුම් පසුබිමින් දත්ත යැවීම සහ ලබා ගැනීම වළක්වයි. ඔබ දැනට භාවිත කරන යෙදුමකට දත්ත වෙත පිවිසීමට හැකිය, නමුත් එසේ කරන්නේ කලාතුරකින් විය හැකිය. මෙයින් අදහස් වන්නේ, උදාහරණයක් ලෙස, එම රූප ඔබ ඒවාට තට්ටු කරන තෙක් සංදර්ශනය නොවන බවය."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"දත්ත සුරැකුම ක්රියාත්මක කරන්නද?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ක්රියාත්මක කරන්න"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> මේ අවස්ථාවේදී ලබා ගත නොහැකිය. මෙය <xliff:g id="APP_NAME_1">%2$s</xliff:g> මගින් කළමනාකරණය කෙරේ."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"තව දැන ගන්න"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"යෙදුම විරාම කිරීම ඉවත් කරන්න"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"කාර්යාල යෙදු. ක්රියා. කරන්නද?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"ඔබගේ කාර්යාල යෙදුම් සහ දැනුම්දීම් වෙත ප්රවේශය ලබා ගන්න"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ක්රියාත්මක කරන්න"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"යෙදුම ලබා ගත නොහැකිය"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> මේ දැන් ලබා ගත නොහැකිය."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index ac5483a..556b590 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -617,8 +617,8 @@
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona odtlačku prsta"</string>
<string name="permlab_manageFace" msgid="4569549381889283282">"spravovať hardvér odomknutia tvárou"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Umožňuje aplikácii vyvolať metódy, ktoré pridávajú a odstraňujú šablóny tvárí."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"používať hardvér Odomknutia tvárou"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Umožňuje aplikácii používať na overenie totožnosti hardvér Odomknutia tvárou"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"používať hardvér odomknutia tvárou"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Umožňuje aplikácii používať na overenie totožnosti hardvér odomknutia tvárou"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Odomknutie tvárou"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Znova zaregistrujte svoju tvár"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Znova zaregistrujte svoju tvár, aby sa zlepšilo rozpoznávanie"</string>
@@ -1757,8 +1757,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použiť skratku"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzia farieb"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Úprava farieb"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Režim jednej ruky"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Veľmi tmavé"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vypnutá."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7dce148..b18afc3 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1028,8 +1028,7 @@
<string name="text_copied" msgid="2531420577879738860">"Besedilo, kopirano v odložišče."</string>
<string name="copied" msgid="4675902854553014676">"Kopirano"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prilepila iz aplikacije <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>."</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prilepila iz odložišča."</string>
<string name="pasted_text" msgid="4298871641549173733">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prilepila besedilo iz odložišča."</string>
<string name="pasted_image" msgid="4729097394781491022">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prilepila sliko iz odložišča."</string>
<string name="pasted_content" msgid="646276353060777131">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prilepila vsebino iz odložišča."</string>
@@ -1300,14 +1299,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Pripravljanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Zagon aplikacij."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Dokončevanje zagona."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Želite izklopiti zaslon?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pri nastavljanju prstnega odtisa ste pritisnili gumb za vklop.\n\nS tem običajno izklopite zaslon."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Izklopi"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Prekliči"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> se izvaja"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dotaknite se za vrnitev v igro"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Izberite igro"</string>
@@ -1762,8 +1757,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Uporabi bližnjico"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija barv"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Popravljanje barv"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enoročni način"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Zelo zatemnjeno"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vklopljena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je izklopljena."</string>
@@ -1921,10 +1915,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Posodobil skrbnik"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisal skrbnik"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"V redu"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Funkcija varčevanja z energijo baterije vklopi temno temo ter omeji ali izklopi dejavnost v ozadju, nekatere vizualne učinke, določene funkcije in nekatere omrežne povezave.\n\n"<annotation id="url">"Več o tem"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Funkcija varčevanja z energijo baterije vklopi temno temo ter omeji ali izklopi dejavnost v ozadju, nekatere vizualne učinke, določene funkcije in nekatere omrežne povezave."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Zaradi zmanjševanja prenesene količine podatkov funkcija varčevanja s podatki nekaterim aplikacijam preprečuje, da bi v ozadju pošiljale ali prejemale podatke. Aplikacija, ki jo trenutno uporabljate, lahko prenaša podatke, vendar to morda počne manj pogosto. To na primer pomeni, da se slike ne prikažejo, dokler se jih ne dotaknete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vklop varčevanja s podatki?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Vklopi"</string>
@@ -2045,10 +2037,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> trenutno ni na voljo. To upravlja aplikacija <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Več o tem"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Prekliči začasno zaustavitev aplikacije"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Vklop delovnih aplikacij?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Omogočanje dostopa do delovnih aplikacij in obvestil za delovni profil"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Vklop"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija ni na voljo"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno ni na voljo."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index d895f74..911dbc1 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -609,10 +609,10 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona e gjurmës së gishtit"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"menaxho harduerin për shkyçjen me fytyrën"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"menaxho harduerin për shkyçjen me fytyrë"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Lejon aplikacionin të aktivizojë mënyra për shtim e fshirje të shablloneve të përdorura."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"përdor harduerin e shkyçjes me fytyrën"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Lejon aplikacionin të përdorë harduerin e shkyçjes me fytyrën për procesin e vërtetimit"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"përdor harduerin e shkyçjes me fytyrë"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Lejon aplikacionin të përdorë harduerin e shkyçjes me fytyrë për procesin e vërtetimit"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Shkyçja me fytyrë"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Regjistro përsëri fytyrën tënde"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Për të përmirësuar njohjen, regjistro përsëri fytyrën tënde"</string>
@@ -643,7 +643,7 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Fytyra s\'mund të verifikohet. Hardueri nuk ofrohet."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Provo përsëri shkyçjen me fytyrën."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Provo përsëri shkyçjen me fytyrë."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"S\'mund të ruhen të dhëna të reja fytyre. Fshi një të vjetër në fillim."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Veprimi me fytyrën u anulua."</string>
<string name="face_error_user_canceled" msgid="8553045452825849843">"Shkyçja me fytyrë u anulua nga përdoruesi."</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Provo sërish"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Provo sërish"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Shkyçe për të gjitha funksionet dhe të dhënat"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Tentativat maksimale të \"Shkyçjes me fytyrë\" u tejkaluan"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Përpjektet maksimale të \"Shkyçjes me fytyrë\" u tejkaluan"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Nuk ka kartë SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Nuk ka kartë SIM në tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Nuk ka kartë SIM në pajisjen tënde Android TV."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Zgjero zonën e shkyçjes."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Rrëshqit shkyçjen."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Shkyçje me motiv."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Shkyçje me fytyrë."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Shkyçja me fytyrë."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Shkyçje me PIN."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Shkyçja e kartës SIM me kodin PIN"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Shkyçja e kartës SIM me kodin PUK"</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Përdor shkurtoren"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Anasjellja e ngjyrës"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korrigjimi i ngjyrës"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modaliteti i përdorimit me një dorë"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Shumë më i zbehtë"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i aktivizuar."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. U çaktivizua \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 3512945..44644fd 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1892,10 +1892,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирао је администратор"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Избрисао је администратор"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Потврди"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Уштеда батерије укључује Тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте, одређене функције и мрежне везе.\n\n"<annotation id="url">"Сазнајте више"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Уштеда батерије укључује тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте, одређене функције и неке мрежне везе."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Да би се смањила потрошња података, Уштеда података спречава неке апликације да шаљу или примају податке у позадини. Апликација коју тренутно користите може да приступа подацима, али ће то чинити ређе. На пример, слике се неће приказивати док их не додирнете."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Желите да укључите Уштеду података?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Укључи"</string>
@@ -2007,10 +2005,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Апликација <xliff:g id="APP_NAME_0">%1$s</xliff:g> тренутно није доступна. <xliff:g id="APP_NAME_1">%2$s</xliff:g> управља доступношћу."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Сазнајте више"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Опозови паузирање апликације"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Укључити пословне апликације?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Приступајте пословним апликацијама и обавештењима"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Укључи"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Апликација није доступна"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> тренутно није доступна."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 71fcb7d..c6df5f9 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Försök igen"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Försök igen"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Lås upp för alla funktioner och all data"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Du har försökt låsa upp med Ansiktslås för många gånger"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Du har försökt låsa upp med ansiktslås för många gånger"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Inget SIM-kort"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Inget SIM-kort i surfplattan."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Inget SIM-kort i Android TV-enheten."</string>
@@ -1713,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Använd kortkommandot"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverterade färger"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Färgkorrigering"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhandsläge"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extradimmat"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string>
@@ -1870,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Administratören uppdaterade paketet"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Administratören raderade paketet"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"I batterisparläget aktiveras mörkt tema medan bakgrundsaktivitet, vissa visuella effekter och funktioner samt vissa nätverksanslutningar begränsas eller inaktiveras.\n\n"<annotation id="url">"Läs mer"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"I batterisparläget aktiveras mörkt tema medan bakgrundsaktivitet, vissa visuella effekter och funktioner samt vissa nätverksanslutningar begränsas eller inaktiveras."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Med Databesparing kan du minska dataanvändningen genom att hindra en del appar från att skicka eller ta emot data i bakgrunden. Appar som du använder kan komma åt data, men det sker kanske inte lika ofta. Detta innebär t.ex. att bilder inte visas förrän du trycker på dem."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vill du aktivera Databesparing?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivera"</string>
@@ -1976,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> är inte tillgänglig just nu. Detta hanteras av <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Läs mer"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Återuppta app"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Vill du aktivera jobbappar?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Få åtkomst till jobbappar och aviseringar"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivera"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Appen är inte tillgänglig"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> är inte tillgängligt just nu."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index cf22492..baca154 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Jaribu tena"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Jaribu tena"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Fungua kifaa ili upate data na vipengele vyote"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Umepitisha idadi ya juu ya mara ambazo unaweza kujaribu Kufungua kwa Uso"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Umepitisha idadi ya juu ya mara ambazo unaweza kujaribu kufungua kwa uso"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Hakuna SIM kadi"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Hakuna SIM kadi katika kompyuta ndogo."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Hakuna SIM kadi kwenye kifaa chako cha Android TV."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Maandishi yamenakiliwa kwenye ubao wa kunakili."</string>
<string name="copied" msgid="4675902854553014676">"Umenakili"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika kutoka <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika kutoka ubao wako wa kunakili"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika maandishi uliyonakili"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika picha uliyonakili"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika maudhui uliyonakili"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Inaandaa <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Programu zinaanza"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Inamaliza kuwasha."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ungependa kuzima skrini?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ulipokuwa ukiweka alama ya kidole chako, ulibonyeza Kitufe cha kuwasha/kuzima.\n\nKwa kawaida, hatua hii huzima skrini yako."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Zima"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Ghairi"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> inaendelea"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Gusa ili urudi kwenye mchezo"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Chagua mchezo"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tumia Njia ya Mkato"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ugeuzaji rangi"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Usahihishaji wa rangi"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Hali ya kutumia kwa mkono mmoja"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Kipunguza mwangaza zaidi"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Vitufe vya sauti vilivyoshikiliwa. Umewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Vitufe vya sauti vimeshikiliwa. Umezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Imesasishwa na msimamizi wako"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Imefutwa na msimamizi wako"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Sawa"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Kiokoa Betri huwasha Mandhari meusi na kudhibiti au kuzima shughuli za chinichini, baadhi ya madoido yanayoonekana, vipengele fulani na baadhi ya miunganisho ya mtandao.\n\n"<annotation id="url">"Pata maelezo zaidi"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Kiokoa Betri huwasha Mandhari meusi na kudhibiti au kuzima shughuli za chinichini, baadhi ya madoido yanayoonekana, vipengele fulani na baadhi ya miunganisho ya mtandao."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Ili kusaidia kupunguza matumizi ya data, Kiokoa Data huzuia baadhi ya programu kupokea na kutuma data chinichini. Programu ambayo unatumia sasa inaweza kufikia data, lakini si kila wakati. Kwa mfano, haitaonyesha picha hadi utakapozifungua."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ungependa Kuwasha Kiokoa Data?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Washa"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> haipatikani kwa sasa. Inasimamiwa na <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Pata maelezo zaidi"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Acha kusimamisha programu"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Utawasha programu za kazini?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Pata uwezo wa kufikia arifa na programu zako za kazini"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Washa"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Programu haipatikani"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> haipatikani hivi sasa."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 7d853ee..1f43483 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"கைரேகை ஐகான்"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"\'முகம் காட்டித் திறத்தலுக்கான\' வன்பொருளை நிர்வகித்தல்"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"\'முகம் காட்டித் திறத்தல்\' அம்சத்துக்கான வன்பொருளை நிர்வகித்தல்"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"உபயோகிப்பதற்காக முக டெம்ப்ளேட்டுகளை சேர்க்கும்/நீக்கும் முறைகளை இயக்க, ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"’முகம் காட்டித் திறத்தல்’ அம்சத்திற்கான வன்பொருளைப் பயன்படுத்துதல்"</string>
<string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"அடையாளம் காண \'முகம் காட்டித் திறத்தலுக்கான\' வன்பொருளைப் பயன்படுத்த ஆப்ஸை அனுமதிக்கும்"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"முகம் காட்டித் திறத்தல்"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"முகத்தை மீண்டும் பதிவுசெய்யவும்"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"அடையாளத்தை மேம்படுத்த முகத்தை மீண்டும் பதிவுசெய்யவும்"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"முகம் காட்டித் திறத்தலை அமையுங்கள்"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"முகம் காட்டித் திறத்தல் அம்சத்தை அமையுங்கள்"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"மொபைலைப் பார்ப்பதன் மூலம் அதைத் திறக்கலாம்"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"திறக்க, மேலும் பல வழிகளை அமையுங்கள்"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"கைரேகையைச் சேர்க்கத் தட்டுங்கள்"</string>
@@ -646,16 +646,16 @@
<string name="face_error_timeout" msgid="522924647742024699">"\'முகம் காட்டித் திறத்தலை\' மீண்டும் முயலவும்."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"புதிய முகங்களைச் சேர்க்க இயலவில்லை. பழையது ஒன்றை நீக்கவும்."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"முக அங்கீகாரச் செயல்பாடு ரத்துசெய்யப்பட்டது."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"\'முகம் காட்டித் திறத்தல்\' பயனரால் ரத்துசெய்யப்பட்டது."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"பயனர் \'முகம் காட்டித் திறத்தல்\' அம்சத்தை ரத்துசெய்தார்."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"பலமுறை முயன்றுவிட்டீர்கள். பிறகு முயலவும்."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"பலமுறை முயன்றுவிட்டீர்கள். \'முகம் காட்டித் திறத்தல்’ முடக்கப்பட்டது."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"பலமுறை முயன்றுவிட்டீர்கள். \'முகம் காட்டித் திறத்தல்’ அம்சம் முடக்கப்பட்டது."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"முகத்தைச் சரிபார்க்க இயலவில்லை. மீண்டும் முயலவும்."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"’முகம் காட்டித் திறத்தலை’ நீங்கள் அமைக்கவில்லை."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"‘முகம் காட்டித் திறத்தல்’ அம்சத்தை நீங்கள் அமைக்கவில்லை."</string>
<string name="face_error_hw_not_present" msgid="1070600921591729944">"இந்த சாதனத்தில் ’முகம் காட்டித் திறத்தல்’ ஆதரிக்கப்படவில்லை."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது."</string>
<string name="face_name_template" msgid="3877037340223318119">"முகம் <xliff:g id="FACEID">%d</xliff:g>"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"முகம் காட்டித் திறத்தலை உபயோகி"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"முகத்தையோ திரைப் பூட்டையோ பயன்படுத்து"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"முகம் காட்டித் திறத்தல் / திரைப் பூட்டைப் பயன்படுத்து"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"தொடர்வதற்கு உங்கள் முகத்தைப் பயன்படுத்துங்கள்"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"தொடர, உங்கள் முகத்தையோ திரைப் பூட்டையோ பயன்படுத்துங்கள்"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"மீண்டும் முயற்சிக்கவும்"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"மீண்டும் முயற்சிக்கவும்"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"எல்லா அம்சங்கள் & தரவை பெற, சாதனத்தை திறக்கவும்"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"முகம் திறப்பதற்கான அதிகபட்ச முயற்சிகள் கடந்தன"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"முகம் காட்டித் திறத்தல் அம்சத்தை அதிகமுறை பயன்படுத்துவிட்டீர்கள்"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"சிம் கார்டு இல்லை"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"டேப்லெட்டில் சிம் கார்டு இல்லை."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Android TVயில் SIM கார்டு இல்லை."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"உரை கிளிப்போர்டிற்கு நகலெடுக்கப்பட்டது."</string>
<string name="copied" msgid="4675902854553014676">"நகலெடுக்கப்பட்டது"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> ஆப்ஸிலிருந்து <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ஒட்டப்பட்டது"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"கிளிப்போர்டில் இருந்து <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ஒட்டப்பட்டது"</string>
<string name="pasted_text" msgid="4298871641549173733">"நீங்கள் நகலெடுத்த உரையை <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒட்டியது"</string>
<string name="pasted_image" msgid="4729097394781491022">"நீங்கள் நகலெடுத்த படத்தை <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒட்டியது"</string>
<string name="pasted_content" msgid="646276353060777131">"நீங்கள் நகலெடுத்த உள்ளடக்கத்தை <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒட்டியது"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ஐத் தயார்செய்கிறது."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ஆப்ஸ் தொடங்கப்படுகின்றன."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"துவக்குதலை முடிக்கிறது."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"திரையை ஆஃப் செய்யவா?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"கைரேகையை அமைக்கும்போது பவர் பட்டனை அழுத்திவிட்டீர்கள்.\n\nஇது திரையை ஆஃப் செய்துவிடும்."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ஆஃப் செய்"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ரத்துசெய்"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> இயங்குகிறது"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"கேமிற்குச் செல்ல, தட்டவும்"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"கேமைத் தேர்வுசெய்க"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ஷார்ட்கட்டைப் பயன்படுத்து"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"நிற நேரெதிர் மாற்றம்"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"நிறத் திருத்தம்"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ஒற்றைக் கைப் பயன்முறை"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"மிகக் குறைவான வெளிச்சம்"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆன் செய்யப்பட்டது."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆஃப் செய்யப்பட்டது."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"உங்கள் நிர்வாகி புதுப்பித்துள்ளார்"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"உங்கள் நிர்வாகி நீக்கியுள்ளார்"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"சரி"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"பேட்டரி சேமிப்பான் டார்க் தீமினை ஆன் செய்து பின்னணிச் செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், குறிப்பிட்ட அம்சங்கள், சில நெட்வொர்க் இணைப்புகள் ஆகியவற்றைக் கட்டுப்படுத்தும் அல்லது ஆஃப் செய்யும்.\n\n"<annotation id="url">"மேலும் அறிக"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"பேட்டரி சேமிப்பான் டார்க் தீமினை ஆன் செய்து பின்னணிச் செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், குறிப்பிட்ட அம்சங்கள், சில நெட்வொர்க் இணைப்புகள் ஆகியவற்றைக் கட்டுப்படுத்தும் அல்லது ஆஃப் செய்யும்."</string>
<string name="data_saver_description" msgid="4995164271550590517">"டேட்டா உபயோகத்தைக் குறைப்பதற்கு உதவ, பின்புலத்தில் டேட்டாவை அனுப்புவது அல்லது பெறுவதிலிருந்து சில ஆப்ஸை டேட்டா சேமிப்பான் தடுக்கும். தற்போது பயன்படுத்தும் ஆப்ஸானது எப்போதாவது டேட்டாவை அணுகலாம். எடுத்துக்காட்டாக, படங்களை நீங்கள் தட்டும் வரை அவை காட்டப்படாது."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"டேட்டா சேமிப்பானை இயக்கவா?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"இயக்கு"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"இப்போது <xliff:g id="APP_NAME_0">%1$s</xliff:g> ஆப்ஸை உபயோகிக்க இயலாது. இதை <xliff:g id="APP_NAME_1">%2$s</xliff:g> நிர்வகிக்கிறது."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"மேலும் அறிக"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ஆப்ஸ் இயக்கு"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"பணி ஆப்ஸை இயக்கவா?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"உங்கள் பணி ஆப்ஸுக்கும் அறிவிப்புகளுக்குமான அணுகலைப் பெறுங்கள்"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"இயக்கு"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"இந்த ஆப்ஸ் இப்போது கிடைப்பதில்லை"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் இப்போது கிடைப்பதில்லை."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 13b3e2cd..8444eac 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -609,10 +609,10 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"వేలిముద్ర చిహ్నం"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"ముఖంతో అన్లాక్ చేయగల హార్డ్వేర్ నిర్వహణ"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"ఫేస్ అన్లాక్ చేయగల హార్డ్వేర్ను మేనేజ్ చేయడం"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"వినియోగం కోసం ముఖ టెంప్లేట్లను జోడించే మరియు తొలగించే పద్ధతులను అమలు చేయడానికి యాప్ను అనుమతిస్తుంది."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ముఖంతో అన్లాక్ చేయగల హార్డ్వేర్ వినియోగం"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"ప్రమాణీకరణ కోసం ముఖంతో అన్లాక్ చేయగల హార్డ్వేర్ను ఉపయోగించడానికి యాప్ను అనుమతిస్తుంది"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"ఫేస్ అన్లాక్ హార్డ్వేర్ను ఉపయోగించడం"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"ప్రామాణీకరణ కోసం ఫేస్తో అన్లాక్ చేయగల హార్డ్వేర్ ఉపయోగానికి యాప్ను అనుమతిస్తుంది"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"ఫేస్ అన్లాక్"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"మీ ముఖాన్ని తిరిగి నమోదు చేయండి"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"గుర్తింపును మెరుగుపరచడానికి, దయచేసి మీ ముఖంను తిరిగి నమోదు చేసుకోండి"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"ముఖం ధృవీకరించలేరు. హార్డ్వేర్ అందుబాటులో లేదు."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"ముఖంతో అన్లాక్ను మళ్లీ ప్రయత్నించండి."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"ఫేస్ అన్లాక్ను మళ్లీ ప్రయత్నించండి."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"కొత్త ముఖం డేటాను నిల్వ చేయడం కాదు. మొదట పాతది తొలిగించండి."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"ముఖ కార్యకలాపం రద్దయింది."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"ముఖంతో అన్లాక్ను వినియోగదారు రద్దు చేశారు."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"ఫేస్ అన్లాక్ను యూజర్ రద్దు చేశారు."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"అనేకసార్లు ప్రయత్నించారు. ముఖంతో అన్లాక్ నిలిపివేయబడింది."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"చాలా సార్లు ప్రయత్నించారు. ఫేస్ అన్లాక్ డిజేబుల్ చేయబడింది."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"ముఖం ధృవీకరించలేకపోయింది. మళ్లీ ప్రయత్నించండి."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"మీరు ముఖంతో అన్లాక్ను సెటప్ చేయలేదు."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"ఈ పరికరంలో ముఖంతో అన్లాక్ను ఉపయోగించడానికి మద్దతు లేదు."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"మీరు ఫేస్ అన్లాక్ను సెటప్ చేయలేదు."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"ఈ పరికరంలో ఫేస్ అన్లాక్ను ఉపయోగించడానికి సపోర్ట్ లేదు."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ చేయబడింది."</string>
<string name="face_name_template" msgid="3877037340223318119">"ముఖ <xliff:g id="FACEID">%d</xliff:g>"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"ఫేస్ అన్లాక్ను ఉపయోగించండి"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ముఖం లేదా స్క్రీన్ లాక్ను ఉపయోగించండి"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ఫేస్ లేదా స్క్రీన్ లాక్ను ఉపయోగించండి"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"కొనసాగించడానికి మీ ముఖాన్ని ఉపయోగించండి"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"కొనసాగించడానికి మీ ముఖం లేదా స్క్రీన్ లాక్ను ఉపయోగించండి"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"మళ్లీ ప్రయత్నించండి"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"మళ్లీ ప్రయత్నించండి"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"అన్ని లక్షణాలు మరియు డేటా కోసం అన్లాక్ చేయండి"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"ముఖంతో అన్లాక్ ప్రయత్నాల గరిష్ట పరిమితి మించిపోయారు"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"ఫేస్ అన్లాక్ ప్రయత్నాల గరిష్ఠ పరిమితిని మించిపోయారు"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"సిమ్ కార్డు లేదు"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"టాబ్లెట్లో సిమ్ కార్డు లేదు."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"మీ Android TV పరికరంలో SIM కార్డ్ లేదు."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"వచనం క్లిప్బోర్డ్కు కాపీ చేయబడింది."</string>
<string name="copied" msgid="4675902854553014676">"కాపీ చేయబడింది"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> నుండి పేస్ట్ చేయబడింది"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"మీ క్లిప్బోర్డ్ నుండి <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> పేస్ట్ చేయబడింది"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> మీరు కాపీ చేసిన టెక్స్ట్ను పేస్ట్ చేసింది"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> మీరు కాపీ చేసిన ఇమేజ్ను పేస్ట్ చేసింది"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> మీరు కాపీ చేసిన కంటెంట్ను పేస్ట్ చేసింది"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ని సిద్ధం చేస్తోంది."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"అనువర్తనాలను ప్రారంభిస్తోంది."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"బూట్ను ముగిస్తోంది."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"స్క్రీన్ను ఆఫ్ చేయాలా?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"మీ వేలిముద్రను సెటప్ చేస్తున్నప్పుడు, మీరు పవర్ బటన్ను నొక్కారు.\n\nఇది సాధారణంగా మీ స్క్రీన్ను ఆఫ్ చేస్తుంది."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ఆఫ్ చేయండి"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"రద్దు చేయండి"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> అమలవుతోంది"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"గేమ్కి తిరిగి రావడానికి నొక్కండి"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"గేమ్ను ఎంచుకోండి"</string>
@@ -1566,8 +1561,8 @@
<string name="keyboardview_keycode_enter" msgid="168054869339091055">"Enter"</string>
<string name="activitychooserview_choose_application" msgid="3500574466367891463">"యాప్ను ఎంచుకోండి"</string>
<string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>ని ప్రారంభించడం సాధ్యపడలేదు"</string>
- <string name="shareactionprovider_share_with" msgid="2753089758467748982">"వీటితో భాగస్వామ్యం చేయండి"</string>
- <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>తో భాగస్వామ్యం చేయండి"</string>
+ <string name="shareactionprovider_share_with" msgid="2753089758467748982">"వీటితో షేర్ చేయండి"</string>
+ <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>తో షేర్ చేయండి"</string>
<string name="content_description_sliding_handle" msgid="982510275422590757">"స్లైడింగ్ హ్యాండిల్. తాకి, ఆపై నొక్కి ఉంచండి."</string>
<string name="description_target_unlock_tablet" msgid="7431571180065859551">"అన్లాక్ చేయడానికి స్వైప్ చేయండి."</string>
<string name="action_bar_home_description" msgid="1501655419158631974">"హోమ్కు నావిగేట్ చేయండి"</string>
@@ -1611,7 +1606,7 @@
<string name="sha1_fingerprint" msgid="2339915142825390774">"SHA-1 వేలిముద్ర:"</string>
<string name="activity_chooser_view_see_all" msgid="3917045206812726099">"అన్నీ చూడండి"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"కార్యాచరణను ఎంచుకోండి"</string>
- <string name="share_action_provider_share_with" msgid="1904096863622941880">"వీటితో భాగస్వామ్యం చేయండి"</string>
+ <string name="share_action_provider_share_with" msgid="1904096863622941880">"వీటితో షేర్ చేయండి"</string>
<string name="sending" msgid="206925243621664438">"పంపుతోంది..."</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"బ్రౌజర్ను ప్రారంభించాలా?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"కాల్ను ఆమోదించాలా?"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"సత్వరమార్గాన్ని ఉపయోగించు"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"కలర్ మార్పిడి"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"కలర్ సరిచేయడం"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"వన్-హ్యాండెడ్ మోడ్"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"కాంతిని మరింత డిమ్ చేయడం"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"మీ నిర్వాహకులు నవీకరించారు"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"మీ నిర్వాహకులు తొలగించారు"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"సరే"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్లు, నిర్దిష్ట ఫీచర్లు, ఇంకా కొన్ని నెట్వర్క్ కనెక్షన్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది.\n\n"<annotation id="url">"మరింత తెలుసుకోండి"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్లు, నిర్దిష్ట ఫీచర్లు, ఇంకా కొన్ని నెట్వర్క్ కనెక్షన్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</string>
<string name="data_saver_description" msgid="4995164271550590517">"డేటా వినియోగాన్ని తగ్గించడంలో డేటా సేవర్ సహాయకరంగా ఉంటుంది. బ్యాక్గ్రౌండ్లో కొన్ని యాప్లు డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తోన్న యాప్, డేటాను యాక్సెస్ చేయగలదు. కానీ తక్కువ సార్లు మాత్రమే అలా చేయవచ్చు. ఉదాహరణకు, మీరు నొక్కే వరకు ఫోటోలు ప్రదర్శించబడవు."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"డేటా సేవర్ను ఆన్ చేయాలా?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ఆన్ చేయి"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ప్రస్తుతం అందుబాటులో లేదు. ఇది <xliff:g id="APP_NAME_1">%2$s</xliff:g> ద్వారా నిర్వహించబడుతుంది."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"మరింత తెలుసుకోండి"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"యాప్పై వున్న పాజ్ను తొలగించండి"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"వర్క్ యాప్లను ఆన్ చేయాలా?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"మీ వర్క్ యాప్లు, నోటిఫికేషన్లకు యాక్సెస్ను పొందండి"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ఆన్ చేయి"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"యాప్ అందుబాటులో లేదు"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రస్తుతం అందుబాటులో లేదు."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 663fc84..cc616f0 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"คัดลอกข้อความไปยังคลิปบอร์ด"</string>
<string name="copied" msgid="4675902854553014676">"คัดลอกแล้ว"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"วาง <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> จาก <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> แล้ว"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"วาง <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> จากคลิปบอร์ดแล้ว"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> วางข้อความที่คุณคัดลอก"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> วางรูปภาพที่คุณคัดลอก"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> วางเนื้อหาที่คุณคัดลอก"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"กำลังเตรียม <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"กำลังเริ่มต้นแอปพลิเคชัน"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"เสร็จสิ้นการบูต"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ปิดหน้าจอใช่ไหม"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ขณะตั้งค่าลายนิ้วมือคุณจะต้องกดปุ่มเปิด/ปิด\n\nซึ่งโดยปกติจะเป็นการปิดหน้าจอด้วย"</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ปิด"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ยกเลิก"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> กำลังทำงาน"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"แตะเพื่อกลับไปที่เกม"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"เลือกเกม"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"อัปเดตโดยผู้ดูแลระบบ"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ลบโดยผู้ดูแลระบบ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ตกลง"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"โหมดประหยัดแบตเตอรี่จะเปิดธีมมืดและจำกัดหรือปิดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง ฟีเจอร์บางส่วน และการเชื่อมต่อบางเครือข่าย\n\n"<annotation id="url">"ดูข้อมูลเพิ่มเติม"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"โหมดประหยัดแบตเตอรี่จะเปิดธีมมืดและจำกัดหรือปิดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง ฟีเจอร์บางส่วน และการเชื่อมต่อบางเครือข่าย"</string>
<string name="data_saver_description" msgid="4995164271550590517">"เพื่อช่วยลดปริมาณการใช้อินเทอร์เน็ต โปรแกรมประหยัดอินเทอร์เน็ตจะช่วยป้องกันไม่ให้บางแอปส่งหรือรับข้อมูลโดยการใช้อินเทอร์เน็ตอยู่เบื้องหลัง แอปที่คุณกำลังใช้งานสามารถเข้าถึงอินเทอร์เน็ตได้ แต่อาจไม่บ่อยเท่าเดิม ตัวอย่างเช่น ภาพต่างๆ จะไม่แสดงจนกว่าคุณจะแตะที่ภาพเหล่านั้น"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"เปิดการประหยัดอินเทอร์เน็ตไหม"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"เปิด"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"เปิด <xliff:g id="APP_NAME_0">%1$s</xliff:g> ไม่ได้ในขณะนี้ แอปนี้จัดการโดย <xliff:g id="APP_NAME_1">%2$s</xliff:g>"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"ดูข้อมูลเพิ่มเติม"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ยกเลิกการหยุดแอปชั่วคราว"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"เปิดแอปงานใช่ไหม"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"รับสิทธิ์เข้าถึงแอปงานและการแจ้งเตือนต่างๆ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"เปิด"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"แอปไม่พร้อมใช้งาน"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่พร้อมใช้งานในขณะนี้"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index e26ce1c..f3d5fc4 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icon ng fingerprint"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"pamahalaan ang hardware ng face unlock"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"pamahalaan ang hardware ng pag-unlock gamit ang mukha"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Pumapayag na mag-invoke ang app ng paraang magdagdag at mag-delete ng template ng mukha."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"gamitin ang hardware ng face unlock"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Pinapayagan ang app na gamitin ang hardware ng face unlock para sa pag-authenticate"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Face unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"gamitin ang hardware ng Pag-unlock Gamit ang Mukha"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Pinapayagan ang app na gamitin ang hardware ng Pag-unlock Gamit ang Mukha para sa pag-authenticate"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Pag-unlock Gamit ang Mukha"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"I-enroll ulit ang iyong mukha"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Para mapahusay ang pagkilala, paki-enroll ulit ang iyong mukha"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"I-set up ang face unlock"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"I-set up ang pag-unlock gamit ang mukha"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"I-unlock ang iyong telepono sa pamamagitan ng pagtingin dito"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Mag-set up ng higit pang paraan para mag-unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"I-tap para magdagdag ng fingerprint"</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Di ma-verify ang mukha. Di available ang hardware."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Subukan ulit ang face unlock."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Subukan ulit ang pag-unlock gamit ang mukha."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Hindi ma-store ang data ng mukha. Mag-delete muna ng iba."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Nakansela ang operation kaugnay ng mukha."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Kinansela ng user ang face unlock."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Kinansela ng user ang pag-unlock gamit ang mukha."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Masyadong maraming pagsubok. Subukang muli mamaya."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Masyadong maraming pagsubok. Na-disable ang face unlock."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Masyadong maraming pagsubok. Na-disable ang pag-unlock gamit ang mukha."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Hindi ma-verify ang mukha. Subukang muli."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Hindi mo pa nase-set up ang face unlock."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"Hindi sinusuportahan ang face unlock sa device na ito."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Hindi mo pa nase-set up ang pag-unlock gamit ang mukha."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Hindi sinusuportahan ang pag-unlock gamit ang mukha sa device na ito."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Pansamantalang na-disable ang sensor."</string>
<string name="face_name_template" msgid="3877037340223318119">"Mukha <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Gumamit ng face unlock"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Gumamit ng pag-unlock gamit ang mukha"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gumamit ng mukha o lock ng screen"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Gamitin ang iyong mukha para magpatuloy"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Gamitin ang iyong mukha o lock ng screen para magpatuloy"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Subukang muli"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Subukang muli"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"I-unlock para sa lahat ng feature at data"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Nalagpasan na ang maximum na mga pagtatangka sa Face Unlock"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Nalagpasan na ang maximum na mga pagtatangka sa Pag-unlock Gamit ang Mukha"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Walang SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Walang SIM card sa tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Walang SIM card sa iyong Android TV device."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Palakihin ang bahagi ng pag-unlock."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Pag-unlock ng slide."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Pag-unlock ng pattern."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Face unlock."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Pag-unlock gamit ang mukha."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Pag-unlock ng pin."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Pag-unlock ng Pin ng Sim."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Pag-unlock ng Puk ng Sim."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Nakopya ang teksto sa clipboard."</string>
<string name="copied" msgid="4675902854553014676">"Nakopya"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Na-paste ang <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> mula sa <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Na-paste ang <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> mula sa iyong clipboard"</string>
<string name="pasted_text" msgid="4298871641549173733">"Nag-paste ang <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ng text na nakopya mo"</string>
<string name="pasted_image" msgid="4729097394781491022">"Nag-paste ang <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ng larawang nakopya mo"</string>
<string name="pasted_content" msgid="646276353060777131">"Nag-paste ang <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ng content na nakopya mo"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Ihinahanda ang <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Sinisimulan ang apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Pagtatapos ng pag-boot."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"I-off ang screen?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Habang sine-set up ang iyong fingerprint, pinindot mo ang Power button.\n\nKaraniwan nitong ino-off ang iyong screen."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"I-off"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Kanselahin"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Tumatakbo ang <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Mag-tap upang bumalik sa laro"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pumili ng laro"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gamitin ang Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Pag-invert ng Kulay"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Pagwawasto ng Kulay"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-Hand mode"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pinindot nang matagal ang volume keys. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Na-update ng iyong admin"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Na-delete ng iyong admin"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Ino-on ng Pantipid ng Baterya ang Madilim na tema at nililimitahan o ino-off nito ang aktibidad sa background, ilang visual effect, ilang partikular na feature, at ilang koneksyon sa network.\n\n"<annotation id="url">"Matuto pa"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Ino-on ng Pantipid ng Baterya ang Madilim na tema at nililimitahan o ino-off nito ang aktibidad sa background, ilang visual effect, ilang partikular na feature, at ilang koneksyon sa network."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Upang makatulong na mabawasan ang paggamit ng data, pinipigilan ng Data Saver ang ilang app na magpadala o makatanggap ng data sa background. Maaaring mag-access ng data ang isang app na ginagamit mo sa kasalukuyan, ngunit mas bihira na nito magagawa iyon. Halimbawa, maaaring hindi lumabas ang mga larawan hangga\'t hindi mo nata-tap ang mga ito."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"I-on ang Data Saver?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"I-on"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Hindi available ang <xliff:g id="APP_NAME_0">%1$s</xliff:g> sa ngayon. Pinamamahalaan ito ng <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Matuto pa"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"I-unpause ang app"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"I-on ang app para sa trabaho?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Makakuha ng access sa iyong mga app para sa trabaho at notification"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"I-on"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Hindi available ang app"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Hindi available sa ngayon ang <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 9c4a4d4..8c1561c 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Kilit açma alanını genişletin."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Kaydırarak kilit açma."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Desenle kilit açma."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Yüzle kilit açma."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Yüzle tanıma kilidi."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Pin koduyla kilit açma."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM PIN kilidini açın."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM PUK kilidini açın."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Metin panoya kopyalandı."</string>
<string name="copied" msgid="4675902854553014676">"Kopyalandı"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> uygulaması <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> kaynağından yapıştırdı"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>, panonuzdakini yapıştırdı"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>, kopyaladığınız metni yapıştırdı"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>, kopyaladığınız resmi yapıştırdı"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>, kopyaladığınız içeriği yapıştırdı"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> hazırlanıyor."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Uygulamalar başlatılıyor"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Açılış tamamlanıyor."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ekran kapatılsın mı?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Parmak izinizi ayarlarken Güç düğmesine bastınız.\n\nBu hareket genellikle ekranınızın kapanmasına neden olur."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Kapat"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"İptal"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Oyuna geri dönmek için dokunun"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Oyun seçin"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kısayolu Kullan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Rengi Ters Çevirme"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Renk Düzeltme"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Tek El modu"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra loş"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kapatıldı."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Yöneticiniz tarafından güncellendi"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Yöneticiniz tarafından silindi"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Tamam"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Pil Tasarrufu, Koyu temayı açıp arka plan etkinliğini, bazı görsel efektleri, belirli özellikleri ve bazı ağ bağlantılarını sınırlandırır veya kapatır.\n\n"<annotation id="url">"Daha fazla bilgi"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Pil Tasarrufu, Koyu temayı açıp arka plan etkinliğini, bazı görsel efektleri, belirli özellikleri ve bazı ağ bağlantılarını sınırlandırır veya kapatır."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Veri kullanımını azaltmaya yardımcı olması için Veri Tasarrufu, bazı uygulamaların arka planda veri göndermesini veya almasını engeller. Kullanmakta olduğunuz bir uygulama veri bağlantısına erişebilir, ancak bunu daha seyrek yapabilir. Bu durumda örneğin, siz resimlere dokunmadan resimler görüntülenmez."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Veri Tasarrufu açılsın mı?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aç"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> uygulaması şu anda kullanılamıyor. Uygulamanın kullanım durumu <xliff:g id="APP_NAME_1">%2$s</xliff:g> tarafından yönetiliyor."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Daha fazla bilgi"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Uygulamanın duraklatmasını kaldır"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"İş uygulamaları açılsın mı?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"İş uygulamalarınıza ve bildirimlerinize erişin"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aç"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Uygulama kullanılamıyor"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulaması şu anda kullanılamıyor."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index bfac264..1b0fc49 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -615,10 +615,10 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок відбитка пальця"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"керувати апаратним забезпечення для Фейсконтролю"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"керувати апаратним забезпечення фейсконтролю"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Додаток може активувати способи додавання й видалення шаблонів облич."</string>
<string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"використовувати апаратне забезпечення для Фейсконтролю"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Додаток може використовувати апаратне забезпечення для Фейсконтролю"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Додаток зможе використовувати для автентифікації апаратне забезпечення фейсконтролю"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Фейсконтроль"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Повторно проскануйте обличчя"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Повторно проскануйте обличчя для ефективнішого розпізнавання"</string>
@@ -649,15 +649,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Не вдається перевірити обличчя. Апаратне забезпечення недоступне."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Скористайтеся Фейсконтролем ще раз."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Спробуйте скористатися фейсконтролем ще раз."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Не вдається зберегти нові дані про обличчя. Видаліть старі."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Дію з обличчям скасовано."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"Користувач скасував Фейсконтроль."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Фейсконтроль: користувач скасував операцію."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Забагато спроб. Повторіть пізніше."</string>
<string name="face_error_lockout_permanent" msgid="8277853602168960343">"Забагато спроб. Фейсконтроль вимкнено."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Не вдається перевірити обличчя. Повторіть спробу."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Ви не налаштували Фейсконтроль"</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"На цьому пристрої не підтримується Фейсконтроль."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Ви не налаштували фейсконтроль"</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"На цьому пристрої не підтримується фейсконтроль."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Датчик тимчасово вимкнено."</string>
<string name="face_name_template" msgid="3877037340223318119">"Обличчя <xliff:g id="FACEID">%d</xliff:g>"</string>
<string name="face_app_setting_name" msgid="8130135875458467243">"Доступ через фейсконтроль"</string>
@@ -1028,8 +1028,7 @@
<string name="text_copied" msgid="2531420577879738860">"Текст скопійов. в буф. обм."</string>
<string name="copied" msgid="4675902854553014676">"Скопійовано"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Дані з додатка <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> вставлено в <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Дані з буфера обміну вставлено в додатку <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
<string name="pasted_text" msgid="4298871641549173733">"Скопійований текст вставлено в додатку <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
<string name="pasted_image" msgid="4729097394781491022">"Скопійоване зображення вставлено в додатку <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
<string name="pasted_content" msgid="646276353060777131">"Скопійований контент вставлено в додатку <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
@@ -1300,14 +1299,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Підготовка додатка <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск програм."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завершення завантаження."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Вимкнути екран?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Налаштовуючи відбиток пальця, ви натиснули кнопку живлення.\n\nЗазвичай після цього вимикається екран."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Вимкнути"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Скасувати"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Працює <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Торкніться, щоб повернутися в гру"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Виберіть гру"</string>
@@ -1762,8 +1757,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Використовувати ярлик"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія кольорів"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекція кольорів"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим керування однією рукою"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Додаткове зменшення яскравості"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> увімкнено."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> вимкнено."</string>
@@ -1921,10 +1915,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Оновлено адміністратором"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Видалено адміністратором"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"У режимі енергозбереження вмикається Темна тема й обмежуються чи вимикаються дії у фоновому режимі, а також деякі візуальні ефекти, функції та з’єднання з мережами.\n\n"<annotation id="url">"Докладніше"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"У режимі енергозбереження вмикається Темна тема й обмежуються чи вимикаються дії у фоновому режимі, а також деякі візуальні ефекти, функції та з’єднання з мережами."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Щоб зменшити використання трафіку, функція \"Заощадження трафіку\" не дозволяє деяким додаткам надсилати чи отримувати дані у фоновому режимі. Поточний додаток зможе отримувати доступ до таких даних, але рідше. Наприклад, зображення не відображатиметься, доки ви не торкнетеся його."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Увімкнути заощадження трафіку?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Увімкнути"</string>
@@ -2045,10 +2037,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"Додаток <xliff:g id="APP_NAME_0">%1$s</xliff:g> зараз недоступний. Керує додаток <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Докладніше"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Відновити доступ до додатка"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Увімкнути робочі додатки?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Отримайте доступ до своїх робочих додатків і сповіщень"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Увімкнути"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Додаток недоступний"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> зараз недоступний."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index fedb557..f50ee1d 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"فنگر پرنٹ آئیکن"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"چہرے کے ذریعے غیر مقفل کرنے والے ہارڈ ویئر کا نظم کریں"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"فیس اَنلاک والے ہارڈ ویئر کا نظم کریں"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"ایپ کو چہرے کی تمثیلات شامل اور حذف کرنے کے طریقوں کو کالعدم قرار دینے کی اجازت دیتا ہے۔"</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"چہرے کے ذریعے غیر مقفل کرنے والا ہارڈ ویئر استعمال کریں"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"ایپ کو تصدیق کے لیے چہرے کے ذریعے غیر مقفل کرنے کا ہارڈ ویئر استعمال کرنے کی اجازت دیتی ہے"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"چہرے کے ذریعے غیر مقفل کریں"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"فیس اَنلاک والا ہارڈ ویئر استعمال کریں"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"ایپ کو تصدیق کے لیے فیس اَنلاک کا ہارڈ ویئر استعمال کرنے کی اجازت دیتی ہے"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"فیس اَنلاک"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"اپنے چہرے کو دوبارہ مندرج کریں"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"شناخت کو بہتر بنانے کے لیے براہ کرم اپنے چہرے کو دوبارہ مندرج کریں"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"چہرے کے ذریعے غیر مقفل کرنا سیٹ اپ کریں"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"فیس اَنلاک سیٹ اپ کریں"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"اپنے فون کی طرف دیکھ کر اسے غیر مقفل کریں"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"غیر مقفل کرنے کے مزید طریقے سیٹ اپ کریں"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"فنگر پرنٹ شامل کرنے کیلئے تھپتھپائیں"</string>
@@ -643,19 +643,19 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"چہرے کی توثیق نہیں کی جا سکی۔ ہارڈ ویئر دستیاب نہیں ہے۔"</string>
- <string name="face_error_timeout" msgid="522924647742024699">"چہرے کے ذریعے غیر مقفل کرنے کو دوبارہ آزمائیں۔"</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"فیس اَنلاک کو دوبارہ آزمائیں۔"</string>
<string name="face_error_no_space" msgid="5649264057026021723">"چہرے کا نیا ڈیٹا اسٹور نہیں کر سکتے۔ پہلے پرانا حذف کریں۔"</string>
<string name="face_error_canceled" msgid="2164434737103802131">"چہرے پر ہونے والی کارروائی منسوخ ہو گئی۔"</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"صارف نے چہرے کے ذریعے غیر مقفل کرنے کو منسوخ کر دیا۔"</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"صارف نے فیس اَنلاک کو منسوخ کر دیا۔"</string>
<string name="face_error_lockout" msgid="7864408714994529437">"کافی زیادہ کوششیں کی گئیں۔ دوبارہ کوشش کریں۔"</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"کافی زیادہ کوششیں۔ چہرے کے ذریعے غیر مقفل کرنا غیر فعال کر دیا گیا۔"</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"کافی زیادہ کوششیں۔ فیس اَنلاک غیر فعال کر دیا گیا۔"</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"چہرے کی توثیق نہیں کی جا سکی۔ پھر آزمائيں۔"</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"آپ نے بذریعہ چہرہ غیر مقفل کرنے کو سیٹ نہیں کیا ہے۔"</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"اس آلہ پر چہرے کے ذریعے غیر مقفل کرنا تعاون یافتہ نہیں ہے۔"</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"آپ نے فیس اَنلاک کو سیٹ نہیں کیا ہے۔"</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"اس آلہ پر فیس اَنلاک تعاون یافتہ نہیں ہے۔"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"سینسر عارضی طور غیر فعال ہے۔"</string>
<string name="face_name_template" msgid="3877037340223318119">"چہرہ <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"چہرے کے ذریعے غیر مقفل کرنا استعمال کریں"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"چہرہ یا اسکرین لاک استعمال کریں"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"فیس اَنلاک استعمال کریں"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"فیس یا اسکرین لاک استعمال کریں"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"جاری رکھنے کے لیے اپنے چہرے کا استعمال کریں"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"جاری رکھنے کے لیے اپنے چہرے یا اسکرین لاک کا استعمال کریں"</string>
<string-array name="face_error_vendor">
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"دوبارہ کوشش کریں"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"دوبارہ کوشش کریں"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"تمام خصوصیات اور ڈیٹا کیلئے غیر مقفل کریں"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"چہرہ کے ذریعے غیر مقفل کریں کی زیادہ سے زیادہ کوششوں سے تجاوز کرگیا"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"فیس اَنلاک کی زیادہ سے زیادہ کوششوں سے تجاوز کرگیا"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"کوئی SIM کارڈ نہیں ہے"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"ٹیبلیٹ میں کوئی SIM کارڈ نہیں ہے۔"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"آپ کے Android TV آلہ میں SIM کارڈ نہیں ہے۔"</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"غیر مقفل کرنے والے علاقے کو پھیلائیں۔"</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"سلائیڈ کے ذریعے غیر مقفل کریں۔"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"پیٹرن کے ذریعے غیر مقفل کریں۔"</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"چہرے کے ذریعے غیر مقفل کریں۔"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"فیس اَنلاک۔"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"پن کے ذریعے غیر مقفل کریں۔"</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Sim پن غیر مقفل۔"</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Sim Puk غیر مقفل۔"</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"متن کو کلپ بورڈ پر کاپی کیا گیا۔"</string>
<string name="copied" msgid="4675902854553014676">"کاپی ہو گیا"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> سے <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> میں پیسٹ کیا گیا"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کے کلپ بورڈ سے پپیسٹ کر دیا"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کا کاپی کردہ ٹیکسٹ پیسٹ کر دیا"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کی کاپی کردہ ایک تصویر پیسٹ کر دی"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کا کاپی کردہ مواد پیسٹ کر دیا"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> تیار ہو رہی ہے۔"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ایپس شروع ہو رہی ہیں۔"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"بوٹ مکمل ہو رہا ہے۔"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"اسکرین آف کریں؟"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"اپنی فنگر پرنٹ سیٹ اپ کرنے کے دوران آپ نے پاور بٹن دبایا۔\n\n یہ عام طور پر آپ کی اسکرین کو آف کرتی ہے۔"</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"آف کریں"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"منسوخ کریں"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> چل رہی ہے"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"گیم پر واپس جانے کے لیے تھپتھپائیں"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"گیم کا انتخاب کریں"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"شارٹ کٹ استعمال کریں"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"رنگوں کی تقلیب"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"رنگ کی تصحیح"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ایک ہاتھ کی وضع"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"اضافی دھندلا"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آن ہے۔"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آف ہے۔"</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"آپ کے منتظم کے ذریعے اپ ڈیٹ کیا گیا"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"آپ کے منتظم کے ذریعے حذف کیا گیا"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ٹھیک ہے"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"بیٹری سیور گہری تھیم کو آن کرتی ہے اور پس منظر کی سرگرمی، کچھ بصری اثرات، مخصوص خصوصیات اور کچھ نیٹ ورک کنکشنز کو محدود یا آف کرتی ہے۔\n\n"<annotation id="url">"مزید جانیں"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"بیٹری سیور گہری تھیم کو آن کرتی ہے اور پس منظر کی سرگرمی، کچھ بصری اثرات، مخصوص خصوصیات اور کچھ نیٹ ورک کنکشنز کو محدود یا آف کرتی ہے۔"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ڈیٹا کے استعمال کو کم کرنے میں مدد کیلئے، ڈیٹا سیور پس منظر میں کچھ ایپس کو ڈیٹا بھیجنے یا موصول کرنے سے روکتی ہے۔ آپ جو ایپ فی الحال استعمال کر رہے ہیں وہ ڈیٹا تک رسائی کر سکتی ہے مگر ہو سکتا ہے ایسا اکثر نہ ہو۔ اس کا مطلب مثال کے طور پر یہ ہو سکتا ہے کہ تصاویر تھپتھپانے تک ظاہر نہ ہوں۔"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ڈیٹا سیور آن کریں؟"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"آن کریں"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ابھی دستیاب نہیں ہے۔ یہ <xliff:g id="APP_NAME_1">%2$s</xliff:g> کے زیر انتظام ہے۔"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"مزید جانیں"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ایپ کو غیر موقوف کریں"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"ورک ایپس آن کریں؟"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"اپنی ورک ایپس اور اطلاعات تک رسائی حاصل کریں"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"آن کریں"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ایپ دستیاب نہیں ہے"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ابھی دستیاب نہیں ہے۔"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 8320aaf..d536056 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Qaytadan urining"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Qaytadan urining"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Barcha funksiya va ma’lumotlar uchun qulfdan chiqaring"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Yuzni tanitib qulfni ochishga urinish miqdoridan oshib ketdi"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Yuz bilan ochishga urinish miqdoridan oshib ketdi"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM karta solinmagan"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Planshetingizda SIM karta yo‘q."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Android TV qurilmangizda SIM karta topilmadi."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Qulfni ochish maydonini kengaytirish."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Qulfni silab ochish"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Grafik kalit bilan ochish."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Qulfni yuzni tanitib ochish"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Yuz bilan ochish."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Pin qulfini ochish."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM kartani PIN kod bilan ochish."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM kartani PUK kod bilan ochish."</string>
@@ -1869,8 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Administrator tomonidan yangilangan"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Administrator tomonidan o‘chirilgan"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Quvvat tejash funksiyasi Tungi mavzuni va cheklovlarni yoqadi va fondagi harakatlar, vizual effektlar, ayrim funksiyalar va tarmoq aloqalari kabi boshqa funksiyalarni faolsizlantiradi yoki cheklaydi.\n\n"<annotation id="url">"Batafsil"</annotation></string>
- <string name="battery_saver_description" msgid="8518809702138617167">"Quvvat tejash funksiyasi Tungi mavzuni va cheklovlarni yoqadi va fondagi harakatlar, vizual effektlar, ayrim funksiyalar va tarmoq aloqalari kabi boshqa funksiyalarni faolsizlantiradi yoki cheklaydi."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Quvvat tejash funksiyasi Tungi mavzuni va cheklovlarni yoqadi hamda fondagi harakatlar, vizual effektlar, ayrim funksiyalar va tarmoq aloqalari kabi boshqa funksiyalarni faolsizlantiradi yoki cheklaydi.\n\n"<annotation id="url">"Batafsil"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Quvvat tejash funksiyasi Tungi mavzuni va cheklovlarni yoqadi hamda fondagi harakatlar, vizual effektlar, ayrim funksiyalar va tarmoq aloqalari kabi boshqa funksiyalarni faolsizlantiradi yoki cheklaydi."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Trafik tejash rejimida ayrim ilovalar uchun orqa fonda internetdan foydalanish imkoniyati cheklanadi. Siz ishlatayotgan ilova zaruratga qarab internet-trafik sarflashi mumkin, biroq cheklangan miqdorda. Masalan, rasmlar ustiga bosmaguningizcha ular yuklanmaydi."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Trafik tejash yoqilsinmi?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Yoqish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9ddb3a6..6841ef5 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -609,9 +609,9 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Biểu tượng vân tay"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"quản lý phần cứng mở khóa bằng khuôn mặt"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"quản lý phần cứng của tính năng mở khóa bằng khuôn mặt"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Cho phép ứng dụng gọi ra các phương pháp để thêm và xóa mẫu khuôn mặt sử dụng."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"sử dụng phần cứng mở khóa bằng khuôn mặt"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"sử dụng phần cứng của tính năng mở khóa bằng khuôn mặt"</string>
<string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Cho phép ứng dụng dùng phần cứng mở khóa bằng khuôn mặt để tiến hành xác thực"</string>
<string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Mở khóa bằng khuôn mặt"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Đăng ký lại khuôn mặt của bạn"</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Đã sao chép văn bản vào bảng nhớ tạm thời."</string>
<string name="copied" msgid="4675902854553014676">"Đã sao chép"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán dữ liệu từ <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán dữ liệu từ bảng nhớ tạm của bạn"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán văn bản mà bạn sao chép"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán một hình ảnh mà bạn sao chép"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán nội dung mà bạn sao chép"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Đang chuẩn bị <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Khởi động ứng dụng."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Hoàn tất khởi động."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Tắt màn hình?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Trong khi thiết lập vân tay, bạn đã nhấn vào nút Nguồn.\n\nThông thường, thao tác này sẽ tắt màn hình."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Tắt"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Hủy"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> đang hoạt động"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Nhấn để quay lại trò chơi"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Chọn trò chơi"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sử dụng phím tắt"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Đảo màu"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Chỉnh màu"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Chế độ một tay"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Siêu tối"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã bật."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã tắt."</string>
@@ -1875,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Do quản trị viên của bạn cập nhật"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Do quản trị viên của bạn xóa"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Trình tiết kiệm pin sẽ bật Giao diện tối, đồng thời hạn chế hoặc tắt hoạt động chạy trong nền, một số hiệu ứng hình ảnh, các tính năng nhất định và một số kết nối mạng.\n\n"<annotation id="url">"Tìm hiểu thêm"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Trình tiết kiệm pin sẽ bật Giao diện tối, đồng thời hạn chế hoặc tắt hoạt động chạy trong nền, một số hiệu ứng hình ảnh, các tính năng nhất định, và một số kết nối mạng."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Để giúp giảm mức sử dụng dữ liệu, Trình tiết kiệm dữ liệu sẽ chặn một số ứng dụng gửi hoặc nhận dữ liệu trong nền. Ứng dụng mà bạn hiện sử dụng có thể dùng dữ liệu nhưng tần suất sẽ giảm. Ví dụ: hình ảnh sẽ không hiển thị cho đến khi bạn nhấn vào hình ảnh đó."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Bật Trình tiết kiệm dữ liệu?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Bật"</string>
@@ -1981,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> hiện không sử dụng được. Chính sách này do <xliff:g id="APP_NAME_1">%2$s</xliff:g> quản lý."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"Tìm hiểu thêm"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Mở lại ứng dụng"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"Bật các ứng dụng công việc?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"Bạn sẽ có quyền truy cập vào các ứng dụng công việc và thông báo"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Bật"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Ứng dụng này không dùng được"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hiện không dùng được."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 50324d8..51735b9 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"文本已复制到剪贴板。"</string>
<string name="copied" msgid="4675902854553014676">"已复制"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>已粘贴从<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>复制的内容"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"已将剪贴板中的内容粘贴到<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>已粘贴您复制的文字"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>已粘贴您复制的图片"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>已粘贴您复制的内容"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"正在准备升级<xliff:g id="APPNAME">%1$s</xliff:g>。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在启动应用。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"即将完成启动。"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要关闭屏幕吗?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"您在设置指纹时按了电源按钮。\n\n此操作通常会关闭屏幕。"</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"关闭"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"点按即可返回游戏"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"选择游戏"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理员更新"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"已由您的管理员删除"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"确定"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"在省电模式下,系统会启用深色主题,并限制或关闭后台活动、某些视觉效果、特定功能和部分网络连接。\n\n"<annotation id="url">"了解详情"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"在省电模式下,系统会启用深色主题,并限制或关闭后台活动、某些视觉效果、特定功能和部分网络连接。"</string>
<string name="data_saver_description" msgid="4995164271550590517">"为了减少流量消耗,流量节省程序会阻止某些应用在后台收发数据。您当前使用的应用可以收发数据,但频率可能会降低。举例而言,这可能意味着图片只有在您点按之后才会显示。"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"要开启流量节省程序吗?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"开启"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>目前无法使用。该应用是由<xliff:g id="APP_NAME_1">%2$s</xliff:g>所管理。"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"了解详情"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"取消暂停应用"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"要开启工作应用访问权限吗?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"获取工作应用和通知的访问权限"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"开启"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"应用无法使用"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前无法使用。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 993b1bc..44bbb2e 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"文字已複製到剪貼簿。"</string>
<string name="copied" msgid="4675902854553014676">"已複製"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> 已貼上從 <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> 複製的資料"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"已將剪貼簿內容貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
<string name="pasted_text" msgid="4298871641549173733">"您複製的文字已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
<string name="pasted_image" msgid="4729097394781491022">"您複製的圖片已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
<string name="pasted_content" msgid="646276353060777131">"您複製的內容已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"正在準備 <xliff:g id="APPNAME">%1$s</xliff:g>。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在啟動應用程式。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"啟動完成。"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要關閉螢幕嗎?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"您在設定指紋時按了開關按鈕。\n\n螢幕通常會因此而關閉。"</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"關閉"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"正在執行 <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"輕按即可返回遊戲"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"選擇遊戲"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理員更新"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"已由您的管理員刪除"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"好"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"「省電模式」會開啟深色主題背景,並限制或關閉背景活動、部分視覺效果、特定功能和部分網絡連線。\n\n"<annotation id="url">"瞭解詳情"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"「省電模式」會開啟深色主題背景,並限制或關閉背景活動、部分視覺效果、特定功能和部分網絡連線。"</string>
<string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。您正在使用的應用程式可存取資料,但次數可能會減少。例如,圖片可能需要輕按才會顯示。"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟「數據節省模式」嗎?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"目前無法使用 <xliff:g id="APP_NAME_0">%1$s</xliff:g>。此應用程式是由「<xliff:g id="APP_NAME_1">%2$s</xliff:g>」管理。"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"瞭解詳情"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"取消暫停應用程式"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"要開啟工作應用程式存取權嗎?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"開啟工作應用程式和通知的存取權"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"開啟"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"無法使用應用程式"</string>
<string name="app_blocked_message" msgid="542972921087873023">"目前無法使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index a45f981..51a8ef2 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"文字已複製到剪貼簿。"</string>
<string name="copied" msgid="4675902854553014676">"已複製"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」已貼上從「<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>」複製的資料"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"已將剪貼簿內容貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
<string name="pasted_text" msgid="4298871641549173733">"你複製的文字已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
<string name="pasted_image" msgid="4729097394781491022">"你複製的圖片已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
<string name="pasted_content" msgid="646276353060777131">"你複製的內容已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"正在準備升級「<xliff:g id="APPNAME">%1$s</xliff:g>」。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在啟動應用程式。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"啟動完成。"</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要關閉螢幕嗎?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"設定指紋時必須按下電源鍵。\n\n這項操作通常會將螢幕關閉。"</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"關閉"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> 執行中"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"輕觸即可返回遊戲"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"選擇遊戲"</string>
@@ -1874,10 +1869,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"已由你的管理員更新"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"已由你的管理員刪除"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) -->
- <skip />
- <!-- no translation found for battery_saver_description (8518809702138617167) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"省電模式會開啟深色主題,並限制或關閉背景活動、某些視覺效果、特定功能和部分網路連線。\n\n"<annotation id="url">"瞭解詳情"</annotation></string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"省電模式會開啟深色主題,並限制或關閉背景活動、某些視覺效果、特定功能和部分網路連線。"</string>
<string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你目前使用的應用程式可以存取資料,但存取頻率可能不如平時高。舉例來說,圖片可能不會自動顯示,在你輕觸後才會顯示。"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟數據節省模式嗎?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string>
@@ -1980,10 +1973,8 @@
<string name="app_suspended_default_message" msgid="6451215678552004172">"目前無法使用「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」。這項設定是由「<xliff:g id="APP_NAME_1">%2$s</xliff:g>」管理。"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"瞭解詳情"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"取消暫停應用程式"</string>
- <!-- no translation found for work_mode_off_title (961171256005852058) -->
- <skip />
- <!-- no translation found for work_mode_off_message (7319580997683623309) -->
- <skip />
+ <string name="work_mode_off_title" msgid="961171256005852058">"要開啟工作應用程式存取權嗎?"</string>
+ <string name="work_mode_off_message" msgid="7319580997683623309">"開啟工作應用程式和通知的存取權"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"開啟"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"應用程式無法使用"</string>
<string name="app_blocked_message" msgid="542972921087873023">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」目前無法使用。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index b983f11..a857f9a9 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -609,14 +609,14 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Isithonjana sezigxivizo zeminwe"</string>
- <string name="permlab_manageFace" msgid="4569549381889283282">"phatha izingxenyekazi zekhompuyutha ze-face unlock"</string>
+ <string name="permlab_manageFace" msgid="4569549381889283282">"phatha izingxenyekazi zekhompyutha zokuvula ngobuso"</string>
<string name="permdesc_manageFace" msgid="6204569688492710471">"Ivumela uhlelo lokusebenza ukuthi luhoxise izindlela zokungeza nokususa amathempulethi obuso azosetshenziswa."</string>
- <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"sebenzisa izingxenyekazi zekhompuyutha ze-face unlock"</string>
- <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Ivumela uhlelo lokusebenza ukuthi lusebenzise izingxenyekazi zekhompuyutha ze-face unlock ukuze kufakazelwe ubuqiniso"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"I-Face unlock"</string>
+ <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"sebenzisa izingxenyekazi zekhompuyutha zokuvula ngobuso"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"Ivumela i-app isebenzise izingxenyekazi zekhompyutha zokuvula ngobuso ukuze kuqinisekiswe"</string>
+ <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"Ukuvula ngobuso"</string>
<string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Phinda ubhalise ubuso bakho"</string>
<string name="face_recalibrate_notification_content" msgid="892757485125249962">"Ukuze uthuthukise ukubonwa, sicela uphinde ubhalise ubuso bakho"</string>
- <string name="face_setup_notification_title" msgid="550617822603450009">"Setha i-face unlock"</string>
+ <string name="face_setup_notification_title" msgid="550617822603450009">"Setha ukuvula ngobuso"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Vula ifoni yakho ngokuyibheka"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Setha izindlela eziningi zokuvula"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Thepha ukuze ungeze izigxivizo zomunwe"</string>
@@ -643,18 +643,18 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Ayikwazi ukuqinisekisa ubuso. Izingxenyekazi zekhompyutha azitholakali."</string>
- <string name="face_error_timeout" msgid="522924647742024699">"Zama i-face unlock futhi."</string>
+ <string name="face_error_timeout" msgid="522924647742024699">"Zama ukuvula ngobuso futhi."</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Ayikwazi ukulondoloza idatha yobuso. Susa endala."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Umsebenzi wobuso ukhanselwe."</string>
- <string name="face_error_user_canceled" msgid="8553045452825849843">"I-face unlock ikhanselwe umsebenzisi."</string>
+ <string name="face_error_user_canceled" msgid="8553045452825849843">"Ukuvula ngobuso kukhanselwe umsebenzisi."</string>
<string name="face_error_lockout" msgid="7864408714994529437">"Imizamo eminingi kakhulu. Zama futhi emuva kwesikhathi."</string>
- <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Imizamo eminingi kakhulu. I-Face unlock ikhutshaziwe."</string>
+ <string name="face_error_lockout_permanent" msgid="8277853602168960343">"Imizamo eminingi kakhulu. Ukuvula ngobuso kukhutshaziwe."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Ayikwazi ukuqinisekisa ubuso. Zama futhi."</string>
- <string name="face_error_not_enrolled" msgid="7369928733504691611">"Awukakasethi i-face unlock."</string>
- <string name="face_error_hw_not_present" msgid="1070600921591729944">"I-face unlock ayisekelwe kule divayisi."</string>
+ <string name="face_error_not_enrolled" msgid="7369928733504691611">"Awukakusethi ukuvula ngobuso."</string>
+ <string name="face_error_hw_not_present" msgid="1070600921591729944">"Ukuvula ngobuso kusekelwe kule divayisi."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Inzwa ikhutshazwe okwesikhashana."</string>
<string name="face_name_template" msgid="3877037340223318119">"Ubuso be-<xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="8130135875458467243">"Sebenzisa i-face unlock"</string>
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Sebenzisa ukuvula ngobuso"</string>
<string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Sebenzisa i-face lock noma ukukhiya isikrini"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Sebenzisa ubuso bakho ukuze uqhubeke"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Sebenzisa ubuso bakho noma ukukhiya isikrini ukuze uqhubeke"</string>
@@ -887,7 +887,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Zama futhi"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Zama futhi"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Vulela zonke izici nedatha"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Ukuzama Kokuvula Ubuso Okuningi kudluliwe"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Ukuzama Kokuvula ngobuso sekweqe umkhawulo"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Alikho ikhadi le-SIM."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Alikho ikhadi le-SIM efonini."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Ayikho i-SIM card kudivayisi yakho ye-Android TV."</string>
@@ -957,7 +957,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Nwebisa indawo yokuvula."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Ukuvula ngokuslayida."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Ukuvula ngephethini."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Vula ngobuso"</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"Ukuvula ngobuso"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Ukuvula ngephinikhodi."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Ukuvulwa kwephinikhodi ye-Sim."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Ukuvulwa kwe-puk ye-Sim."</string>
@@ -1022,8 +1022,7 @@
<string name="text_copied" msgid="2531420577879738860">"Umbhalo ukopishwe ebhodini lokunamathisela."</string>
<string name="copied" msgid="4675902854553014676">"Kukopishiwe"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"I-<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> inamathiselwe kusuka ku-<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <!-- no translation found for pasted_from_clipboard (7355790625710831847) -->
- <skip />
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"I-<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> inamathiselwe ebhodini lakho lokunamathisela"</string>
<string name="pasted_text" msgid="4298871641549173733">"U-<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> unamathisele umbhalo owukopishile"</string>
<string name="pasted_image" msgid="4729097394781491022">"U-<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> unamathisele isithombe osikopishile"</string>
<string name="pasted_content" msgid="646276353060777131">"U-<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> unamathisele okuqukethwe okukopishile"</string>
@@ -1260,14 +1259,10 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Ukulungisela i-<xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Qalisa izinhlelo zokusebenza."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Qedela ukuqala kabusha."</string>
- <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) -->
- <skip />
- <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) -->
- <skip />
+ <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vala isikrini?"</string>
+ <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Lapho usetha izigxivizo zeminwe yakho, ucindezele Inkinobho yamandla.\n\nLokhu kuvame ukuvala isikrini sakho."</string>
+ <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Vala"</string>
+ <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Khansela"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> iyasebenza"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Thepha ukuze ubuyele emuva kwigeyimu"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Khetha igeyimu"</string>
@@ -1718,8 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sebenzisa isinqamuleli"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ukuguqulwa kombala"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Ukulungiswa kombala"</string>
- <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) -->
- <skip />
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Imodi yesandla esisodwa"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ukufiphaza okwengeziwe"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivuliwe."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivaliwe."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f0c43ff..e975938 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9568,11 +9568,4 @@
<attr name="iconfactoryBadgeSize" format="dimension"/>
<!-- Perceptual luminance of a color, in accessibility friendly color space. From 0 to 100. -->
<attr name="lStar" format="float"/>
-
- <declare-styleable name="DisplayHashingService">
- <!-- The interval required between display hash requests. Requests made faster than this
- delay will be throttled."
- @hide @SystemApi -->
- <attr name="durationBetweenRequestsMillis" format="integer" />
- </declare-styleable>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c5f4fd1..ef0dc37 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1954,6 +1954,13 @@
See the <a href="{@docRoot}about/versions/12/backup-restore">Changes in backup and restore</a>
document for the format of the XML file.-->
<attr name="dataExtractionRules" format="reference"/>
+
+ <!-- @hide Request exemption from the foreground service restrictions introduced in S
+ (https://developer.android.com/about/versions/12/foreground-services)
+ Note the framework <b>ignores</b> this attribute at this time. Once apps target S or above,
+ there's no way to be exempted (without using a privileged permission).
+ -->
+ <attr name="requestForegroundServiceExemption" format="boolean" />
</declare-styleable>
<!-- An attribution is a logical part of an app and is identified by a tag.
@@ -2586,6 +2593,12 @@
<!-- The code for this component is located in the given split.
<p>NOTE: This is only applicable to instant app. -->
<attr name="splitName" />
+ <!-- Set of attribution tags that should be automatically applied to this component.
+ <p>
+ Each instance of this ContentProvider will be automatically configured with
+ Context.createAttributionContext() using the first attribution tag
+ contained here. -->
+ <attr name="attributionTags" />
</declare-styleable>
<!-- Attributes that can be supplied in an AndroidManifest.xml
@@ -2721,6 +2734,12 @@
<attr name="useAppZygote" format="boolean" />
<!-- If this is a foreground service, specify its category. -->
<attr name="foregroundServiceType" />
+ <!-- Set of attribution tags that should be automatically applied to this component.
+ <p>
+ Each instance of this Service will be automatically configured with
+ Context.createAttributionContext() using the first attribution tag
+ contained here. -->
+ <attr name="attributionTags" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
@@ -2758,6 +2777,12 @@
<attr name="exported" />
<attr name="singleUser" />
<attr name="directBootAware" />
+ <!-- Set of attribution tags that should be automatically applied to this component.
+ <p>
+ Each instance of this BroadcastReceiver will be automatically configured with
+ Context.createAttributionContext() using the first attribution tag
+ contained here. -->
+ <attr name="attributionTags" />
</declare-styleable>
<!-- The <code>activity</code> tag declares an
@@ -2885,10 +2910,12 @@
<p> See {@link android.content.pm.ActivityInfo#FLAG_PREFER_MINIMAL_POST_PROCESSING} -->
<attr name="preferMinimalPostProcessing" format="boolean"/>
- <!-- Specify the attributionTags to be used if a permission is required due to
- {@link android.content.Context#sendBroadcast(Intent, String)} being used.
- Multiple tags can be specified separated by '|'. -->
- <attr name="attributionTags"/>
+ <!-- Set of attribution tags that should be automatically applied to this component.
+ <p>
+ Each instance of this Activity will be automatically configured with
+ Context.createAttributionContext() using the first attribution tag
+ contained here. -->
+ <attr name="attributionTags" />
<!-- Specifies whether a home sound effect should be played if the home app moves to
front after an activity with this flag set to <code>true</code>.
<p>The default value of this attribute is <code>true</code>.
@@ -2935,6 +2962,7 @@
<attr name="enabled" />
<attr name="exported" />
<attr name="parentActivityName" />
+ <attr name="attributionTags" />
</declare-styleable>
<!-- The <code>meta-data</code> tag is used to attach additional
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index aa0d23b..415f330 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -27,6 +27,8 @@
<!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the
icons in the status bar that are not notifications. -->
<string-array name="config_statusBarIcons">
+ <item><xliff:g id="id">@string/status_bar_no_calling</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_call_strength</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_headset</xliff:g></item>
@@ -58,8 +60,6 @@
<item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_no_calling</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_call_strength</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_sensors_off</xliff:g></item>
</string-array>
@@ -3936,6 +3936,9 @@
<!-- Handle volume keys directly in Window Manager without passing them to the foreground app -->
<bool name="config_handleVolumeKeysInWindowManager">false</bool>
+ <!-- Rely or not on hardcoded aliased streams table within AudioService -->
+ <bool name="config_handleVolumeAliasesUsingVolumeGroups">false</bool>
+
<!-- Volume level of in-call notification tone playback [0..1] -->
<item name="config_inCallNotificationVolume" format="float" type="dimen">.10</item>
@@ -4796,8 +4799,9 @@
<!-- Whether to select voice/data/sms preference without user confirmation -->
<bool name="config_voice_data_sms_auto_fallback">false</bool>
- <!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. -->
- <bool name="config_enableOneHandedKeyguard">false</bool>
+ <!-- Whether to enable dynamic keyguard positioning for wide screen devices (e.g. only using
+ half of the screen, to be accessible using only one hand). -->
+ <bool name="config_enableDynamicKeyguardPositioning">false</bool>
<!-- Whether to allow the caching of the SIM PIN for verification after unattended reboot -->
<bool name="config_allow_pin_storage_for_unattended_reboot">true</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4f90a17..d334306 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -224,14 +224,14 @@
<!-- The margin on the end of the top-line content views (accommodates the expander) -->
<dimen name="notification_heading_margin_end">56dp</dimen>
- <!-- The height of the notification action list -->
+ <!-- The total height of the notification action list -->
<dimen name="notification_action_list_height">60dp</dimen>
<!-- The margin of the notification action list at the top -->
<dimen name="notification_action_list_margin_top">0dp</dimen>
- <!-- The height of the notification action list -->
- <dimen name="notification_action_emphasized_height">48dp</dimen>
+ <!-- The visual height of the emphasized notification action -->
+ <dimen name="notification_action_emphasized_height">36dp</dimen>
<!-- The padding of the actions in non-conversation layout. For conversations, the analogous
value is calculated in ConversationLayout#updateActionListPadding() -->
@@ -252,7 +252,7 @@
<dimen name="notification_actions_icon_drawable_size">20dp</dimen>
<!-- The corner radius if the emphasized action buttons in a notification -->
- <dimen name="notification_action_button_radius">8dp</dimen>
+ <dimen name="notification_action_button_radius">18dp</dimen>
<!-- Size of the stroke with for the emphasized notification button style -->
<dimen name="emphasized_button_stroke_width">1dp</dimen>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index c3b35c8..c4838b8 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -206,6 +206,12 @@
<!-- A tag used to store the margin end for this view when the right icon is gone -->
<item type="id" name="tag_margin_end_when_icon_visible" />
+ <!-- A tag used on the notification @id/left_icon to indicate that this view should be pupulated with the drawable from @id/right_icon when visible. -->
+ <item type="id" name="tag_uses_right_icon_drawable" />
+
+ <!-- A tag used on notification @id/right_icon to indicate that this view should remain visible even when the @id/left_icon is shown. -->
+ <item type="id" name="tag_keep_when_showing_left_icon" />
+
<!-- Marks the "copy to clipboard" button in the ChooserActivity -->
<item type="id" name="chooser_copy_button" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 500a9da..d347b93 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3064,6 +3064,7 @@
<public name="hotwordDetectionService" />
<public name="previewLayout" />
<public name="clipToOutline" />
+ <public name="__removed3" />
<public name="knownCerts" />
<public name="windowBackgroundBlurRadius"/>
<public name="windowSplashScreenBackground"/>
@@ -3095,10 +3096,10 @@
<!-- @hide @SystemApi -->
<public name="playHomeTransitionSound" />
<public name="lStar" />
- <!-- @hide @SystemApi -->
- <public name="durationBetweenRequestsMillis" />
<public name="showInInputMethodPicker" />
<public name="effectColor" />
+ <!-- @hide @TestApi -->
+ <public name="requestForegroundServiceExemption" />
</staging-public-group>
<staging-public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b836c4c..8f40974 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5571,7 +5571,7 @@
<!-- Label of notification action button to learn more about the enhanced notifications [CHAR LIMIT=20] -->
<string name="nas_upgrade_notification_learn_more_action">Learn more</string>
<!-- Content of notification learn more dialog about the enhanced notifications [CHAR LIMIT=NONE] -->
- <string name="nas_upgrade_notification_learn_more_content">Enhanced notifications replaced Android Adaptive Notifications in Android 12. This feature shows suggested actions and replies, and organizes your notifications.\n\nEnhanced notifications can access notification content, including personal information like contact names and messages. This feature can also dismiss or respond to notifications, such as answering phone calls and controlling Do Not Disturb.</string>
+ <string name="nas_upgrade_notification_learn_more_content">Enhanced notifications replaced Android Adaptive Notifications in Android 12. This feature shows suggested actions and replies, and organizes your notifications.\n\nEnhanced notifications can access notification content, including personal information like contact names and messages. This feature can also dismiss or respond to notifications, such as answering phone calls, and control Do Not Disturb.</string>
<!-- Dynamic mode battery saver strings -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d8620a7..f2328567 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3167,6 +3167,8 @@
<java-symbol type="dimen" name="notification_action_disabled_alpha" />
<java-symbol type="id" name="tag_margin_end_when_icon_visible" />
<java-symbol type="id" name="tag_margin_end_when_icon_gone" />
+ <java-symbol type="id" name="tag_uses_right_icon_drawable" />
+ <java-symbol type="id" name="tag_keep_when_showing_left_icon" />
<!-- Override Wake Key Behavior When Screen is Off -->
<java-symbol type="bool" name="config_wakeOnDpadKeyPress" />
@@ -3630,6 +3632,7 @@
<java-symbol type="array" name="config_allowedSecureInstantAppSettings" />
<java-symbol type="bool" name="config_handleVolumeKeysInWindowManager" />
+ <java-symbol type="bool" name="config_handleVolumeAliasesUsingVolumeGroups" />
<java-symbol type="dimen" name="config_inCallNotificationVolume" />
<java-symbol type="string" name="config_inCallNotificationSound" />
<java-symbol type="integer" name="config_autoGroupAtCount" />
@@ -4257,7 +4260,7 @@
<java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
- <java-symbol type="bool" name="config_enableOneHandedKeyguard" />
+ <java-symbol type="bool" name="config_enableDynamicKeyguardPositioning" />
<java-symbol type="attr" name="colorAccentPrimary" />
<java-symbol type="attr" name="colorAccentSecondary" />
diff --git a/core/tests/coretests/res/color/color_with_lstar.xml b/core/tests/coretests/res/color/color_with_lstar.xml
new file mode 100644
index 0000000..dcc3d6d
--- /dev/null
+++ b/core/tests/coretests/res/color/color_with_lstar.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#ff0000" android:lStar="50" />
+</selector>
diff --git a/core/tests/coretests/res/values/colors.xml b/core/tests/coretests/res/values/colors.xml
index f01af84..029aa0d 100644
--- a/core/tests/coretests/res/values/colors.xml
+++ b/core/tests/coretests/res/values/colors.xml
@@ -25,5 +25,6 @@
<drawable name="yellow">#ffffff00</drawable>
<color name="testcolor1">#ff00ff00</color>
<color name="testcolor2">#ffff0000</color>
+ <color name="testcolor3">#fff00000</color>
<color name="failColor">#ff0000ff</color>
</resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
index 9e88373..a613e77 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
@@ -16,18 +16,12 @@
package android.app.appsearch;
-
import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
-import com.google.common.collect.ImmutableList;
-
import org.junit.Test;
-import java.util.List;
-import java.util.Map;
-
public class SearchSpecTest {
@Test
@@ -63,32 +57,4 @@
assertThat(bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD))
.isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
}
-
- @Test
- public void testGetProjectionTypePropertyMasks() {
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
- .addProjection("TypeA", ImmutableList.of("field1", "field2.subfield2"))
- .addProjection("TypeB", ImmutableList.of("field7"))
- .addProjection("TypeC", ImmutableList.of())
- .build();
-
- Map<String, List<String>> typePropertyPathMap = searchSpec.getProjections();
- assertThat(typePropertyPathMap.keySet()).containsExactly("TypeA", "TypeB", "TypeC");
- assertThat(typePropertyPathMap.get("TypeA")).containsExactly("field1", "field2.subfield2");
- assertThat(typePropertyPathMap.get("TypeB")).containsExactly("field7");
- assertThat(typePropertyPathMap.get("TypeC")).isEmpty();
- }
-
- @Test
- public void testGetRankingStrategy() {
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
- .setRankingStrategy(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE)
- .build();
- assertThat(searchSpec.getRankingStrategy())
- .isEqualTo(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE);
- }
}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
deleted file mode 100644
index 677f4bd..0000000
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.appsearch;
-
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.expectThrows;
-
-import android.util.ArrayMap;
-
-import org.junit.Test;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class SetSchemaRequestTest {
-
- private static Collection<String> getSchemaTypesFromSetSchemaRequest(SetSchemaRequest request) {
- HashSet<String> schemaTypes = new HashSet<>();
- for (AppSearchSchema schema : request.getSchemas()) {
- schemaTypes.add(schema.getSchemaType());
- }
- return schemaTypes;
- }
-
- @Test
- public void testInvalidSchemaReferences_fromDisplayedBySystem() {
- IllegalArgumentException expected =
- expectThrows(
- IllegalArgumentException.class,
- () ->
- new SetSchemaRequest.Builder()
- .setSchemaTypeDisplayedBySystem("InvalidSchema", false)
- .build());
- assertThat(expected).hasMessageThat().contains("referenced, but were not added");
- }
-
- @Test
- public void testInvalidSchemaReferences_fromPackageVisibility() {
- IllegalArgumentException expected =
- expectThrows(
- IllegalArgumentException.class,
- () ->
- new SetSchemaRequest.Builder()
- .setSchemaTypeVisibilityForPackage(
- "InvalidSchema",
- /*visible=*/ true,
- new PackageIdentifier(
- "com.foo.package",
- /*sha256Certificate=*/ new byte[] {}))
- .build());
- assertThat(expected).hasMessageThat().contains("referenced, but were not added");
- }
-
- @Test
- public void testSetSchemaTypeDisplayedBySystem_displayed() {
- AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
-
- // By default, the schema is displayed.
- SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
- assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty();
-
- request =
- new SetSchemaRequest.Builder()
- .addSchemas(schema)
- .setSchemaTypeDisplayedBySystem("Schema", true)
- .build();
- assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty();
- }
-
- @Test
- public void testSetSchemaTypeDisplayedBySystem_notDisplayed() {
- AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
- SetSchemaRequest request =
- new SetSchemaRequest.Builder()
- .addSchemas(schema)
- .setSchemaTypeDisplayedBySystem("Schema", false)
- .build();
- assertThat(request.getSchemasNotDisplayedBySystem()).containsExactly("Schema");
- }
-
- @Test
- public void testSchemaTypeVisibilityForPackage_visible() {
- AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
-
- // By default, the schema is not visible.
- SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
- assertThat(request.getSchemasVisibleToPackages()).isEmpty();
-
- PackageIdentifier packageIdentifier =
- new PackageIdentifier("com.package.foo", new byte[] {100});
- Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>();
- expectedVisibleToPackagesMap.put("Schema", Collections.singleton(packageIdentifier));
-
- request =
- new SetSchemaRequest.Builder()
- .addSchemas(schema)
- .setSchemaTypeVisibilityForPackage(
- "Schema", /*visible=*/ true, packageIdentifier)
- .build();
- assertThat(request.getSchemasVisibleToPackages())
- .containsExactlyEntriesIn(expectedVisibleToPackagesMap);
- }
-
- @Test
- public void testSchemaTypeVisibilityForPackage_notVisible() {
- AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
-
- SetSchemaRequest request =
- new SetSchemaRequest.Builder()
- .addSchemas(schema)
- .setSchemaTypeVisibilityForPackage(
- "Schema",
- /*visible=*/ false,
- new PackageIdentifier(
- "com.package.foo", /*sha256Certificate=*/ new byte[] {}))
- .build();
- assertThat(request.getSchemasVisibleToPackages()).isEmpty();
- }
-
- @Test
- public void testSchemaTypeVisibilityForPackage_deduped() throws Exception {
- AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
-
- PackageIdentifier packageIdentifier =
- new PackageIdentifier("com.package.foo", new byte[] {100});
- Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>();
- expectedVisibleToPackagesMap.put("Schema", Collections.singleton(packageIdentifier));
-
- SetSchemaRequest request =
- new SetSchemaRequest.Builder()
- .addSchemas(schema)
- // Set it visible for "Schema"
- .setSchemaTypeVisibilityForPackage(
- "Schema", /*visible=*/ true, packageIdentifier)
- // Set it visible for "Schema" again, which should be a no-op
- .setSchemaTypeVisibilityForPackage(
- "Schema", /*visible=*/ true, packageIdentifier)
- .build();
- assertThat(request.getSchemasVisibleToPackages())
- .containsExactlyEntriesIn(expectedVisibleToPackagesMap);
- }
-
- @Test
- public void testSchemaTypeVisibilityForPackage_removed() throws Exception {
- AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
-
- SetSchemaRequest request =
- new SetSchemaRequest.Builder()
- .addSchemas(schema)
- // First set it as visible
- .setSchemaTypeVisibilityForPackage(
- "Schema",
- /*visible=*/ true,
- new PackageIdentifier(
- "com.package.foo", /*sha256Certificate=*/ new byte[] {100}))
- // Then make it not visible
- .setSchemaTypeVisibilityForPackage(
- "Schema",
- /*visible=*/ false,
- new PackageIdentifier(
- "com.package.foo", /*sha256Certificate=*/ new byte[] {100}))
- .build();
-
- // Nothing should be visible.
- assertThat(request.getSchemasVisibleToPackages()).isEmpty();
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 281ce2b..df0c64c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -61,6 +61,7 @@
import android.view.Surface;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
+import android.view.translation.UiTranslationSpec;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -693,8 +694,8 @@
@Override
public void updateUiTranslationState(IBinder activityToken, int state,
- TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds) {
-
+ TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds,
+ UiTranslationSpec uiTranslationSpec) {
}
}
}
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 46dbe0f..e7ee9dc 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -166,7 +166,7 @@
Configuration newConfig = new Configuration();
newConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
- mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null);
+ mResourcesManager.applyConfigurationToResources(newConfig, null);
final Configuration expectedConfig = new Configuration();
expectedConfig.setToDefaults();
diff --git a/core/tests/coretests/src/android/graphics/ColorStateListTest.java b/core/tests/coretests/src/android/graphics/ColorStateListTest.java
index 1d34f938..a3d52ea 100644
--- a/core/tests/coretests/src/android/graphics/ColorStateListTest.java
+++ b/core/tests/coretests/src/android/graphics/ColorStateListTest.java
@@ -67,4 +67,10 @@
int defaultColor = mResources.getColor(R.color.color_no_default);
assertEquals(mResources.getColor(R.color.testcolor1), defaultColor);
}
+
+ @SmallTest
+ public void testLstar() throws Exception {
+ int defaultColor = mResources.getColor(R.color.color_with_lstar);
+ assertEquals(mResources.getColor(R.color.testcolor3), defaultColor);
+ }
}
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 61f58b0..fd39cde 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -762,6 +762,62 @@
// }
// }
+ @Test
+ public void syncDisabling() throws Exception {
+ Properties properties1 = new Properties.Builder(NAMESPACE)
+ .setString(KEY, VALUE)
+ .build();
+ Properties properties2 = new Properties.Builder(NAMESPACE)
+ .setString(KEY, VALUE2)
+ .build();
+
+ try {
+ // Ensure the device starts in a known state.
+ DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
+
+ // Assert starting state.
+ assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+ assertThat(DeviceConfig.setProperties(properties1)).isTrue();
+ assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
+ .isEqualTo(VALUE);
+
+ // Test disabled (persistent). Persistence is not actually tested, that would require
+ // a host test.
+ DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT);
+ assertThat(DeviceConfig.isSyncDisabled()).isTrue();
+ assertThat(DeviceConfig.setProperties(properties2)).isFalse();
+ assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
+ .isEqualTo(VALUE);
+
+ // Return to not disabled.
+ DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
+ assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+ assertThat(DeviceConfig.setProperties(properties2)).isTrue();
+ assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
+ .isEqualTo(VALUE2);
+
+ // Test disabled (persistent). Absence of persistence is not actually tested, that would
+ // require a host test.
+ DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT);
+ assertThat(DeviceConfig.isSyncDisabled()).isTrue();
+ assertThat(DeviceConfig.setProperties(properties1)).isFalse();
+ assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
+ .isEqualTo(VALUE2);
+
+ // Return to not disabled.
+ DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
+ assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+ assertThat(DeviceConfig.setProperties(properties1)).isTrue();
+ assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
+ .isEqualTo(VALUE);
+ } finally {
+ // Try to return to the default sync disabled state in case of failure.
+ DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
+
+ // NAMESPACE will be cleared by cleanUp()
+ }
+ }
+
private static boolean deleteViaContentProvider(String namespace, String key) {
ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
String compositeName = namespace + "/" + key;
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index 97e66c4..ee0b127 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -96,7 +96,8 @@
mCacheGenerationStore.set(0, ++mCurrentGeneration);
Bundle result = new Bundle();
- result.putBoolean(Settings.KEY_CONFIG_SET_RETURN, true);
+ result.putInt(Settings.KEY_CONFIG_SET_ALL_RETURN,
+ Settings.SET_ALL_RESULT_SUCCESS);
return result;
});
diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
index 4a5528d..79f7a5c 100644
--- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
@@ -40,6 +40,7 @@
@Test
public void testMeasuredEnergyBasedModel() {
+ mStatsRule.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
stats.updateDisplayMeasuredEnergyStatsLocked(300_000_000, Display.STATE_ON, 0);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
index 23fc35d..6457e3f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
@@ -35,11 +35,13 @@
private static final double PRECISION = 0.00001;
@Rule
- public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
- .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 1234.0); // Should be ignored
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
@Test
public void testDischargeTotals() {
+ // Nominal battery capacity should be ignored
+ mStatsRule.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 1234.0);
+
final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
@@ -56,7 +58,7 @@
BatteryUsageStats batteryUsageStats = mStatsRule.apply(calculator);
assertThat(batteryUsageStats.getConsumedPower())
- .isWithin(PRECISION).of(380.0);
+ .isWithin(PRECISION).of(1200.0); // 3,600 - 2,400
assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(10);
assertThat(batteryUsageStats.getDischargedPowerRange().getLower())
.isWithin(PRECISION).of(360.0);
@@ -74,4 +76,34 @@
assertThat(batteryUsageStats.getChargeTimeRemainingMs()).isEqualTo(100_000);
}
+
+ @Test
+ public void testDischargeTotals_chargeUahUnavailable() {
+ mStatsRule.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
+
+ final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 90, 72, 3700, 0, 0, 0,
+ 1_000_000, 1_000_000, 1_000_000);
+ batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 85, 72, 3700, 0, 0, 0,
+ 1_500_000, 1_500_000, 1_500_000);
+ batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 80, 72, 3700, 0, 0, 0,
+ 2_000_000, 2_000_000, 2_000_000);
+
+ BatteryChargeCalculator calculator = new BatteryChargeCalculator();
+ BatteryUsageStats batteryUsageStats = mStatsRule.apply(calculator);
+
+ assertThat(batteryUsageStats.getConsumedPower())
+ .isWithin(PRECISION).of(380.0); // 9.5% of 4,000.
+ assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(10);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getLower())
+ .isWithin(PRECISION).of(360.0); // 9% of 4,000
+ assertThat(batteryUsageStats.getDischargedPowerRange().getUpper())
+ .isWithin(PRECISION).of(400.0); // 10% of 4,000
+ assertThat(batteryUsageStats.getBatteryTimeRemainingMs()).isEqualTo(8_000_000);
+ assertThat(batteryUsageStats.getChargeTimeRemainingMs()).isEqualTo(-1);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index 623e77e..54d8701 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -363,7 +363,6 @@
assertEquals("Unexpected system cpu time for uid=" + testUids[i],
uidTimesUs[i][1], u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
}
- verify(mCpuUidUserSysTimeReader).removeUid(isolatedUid);
// Add an isolated uid mapping and repeat the test.
@@ -453,7 +452,6 @@
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
- verify(mCpuUidUserSysTimeReader).removeUid(invalidUid);
}
@Test
@@ -943,7 +941,6 @@
assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
}
- verify(mCpuUidFreqTimeReader).removeUid(isolatedUid);
// Add an isolated uid mapping and repeat the test.
@@ -1038,7 +1035,6 @@
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
- verify(mCpuUidFreqTimeReader).removeUid(invalidUid);
}
@Test
@@ -1142,7 +1138,6 @@
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
- verify(mCpuUidActiveTimeReader).removeUid(invalidUid);
}
@Test
@@ -1259,7 +1254,6 @@
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
- verify(mCpuUidClusterTimeReader).removeUid(invalidUid);
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index f168b3c..464412f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -506,6 +506,7 @@
public void testUpdateDisplayMeasuredEnergyStatsLocked() {
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.initMeasuredEnergyStats();
clocks.realtime = 0;
int screen = Display.STATE_OFF;
@@ -590,6 +591,7 @@
public void testUpdateCustomMeasuredEnergyStatsLocked_neverCalled() {
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.initMeasuredEnergyStats();
bi.setOnBatteryInternal(true);
final int uid1 = 11500;
@@ -603,6 +605,7 @@
public void testUpdateCustomMeasuredEnergyStatsLocked() {
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.initMeasuredEnergyStats();
final int bucketA = 0; // Custom bucket 0
final int bucketB = 1; // Custom bucket 1
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index d36f06a..7f0449b 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -45,7 +45,7 @@
private static final long MINUTE_IN_MS = 60 * 1000;
@Rule
- public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345);
@Test
public void test_getBatteryUsageStats() {
@@ -62,6 +62,8 @@
batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(54321);
+
Context context = InstrumentationRegistry.getContext();
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats);
@@ -75,6 +77,9 @@
.isEqualTo(20 * MINUTE_IN_MS);
assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND))
.isEqualTo(10 * MINUTE_IN_MS);
+
+ assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(12345);
+ assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(54321);
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 1a6408f..961e859 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -51,19 +51,20 @@
private final PowerProfile mPowerProfile;
private final MockClocks mMockClocks = new MockClocks();
- private final MockBatteryStatsImpl mBatteryStats = new MockBatteryStatsImpl(mMockClocks) {
- @Override
- public boolean hasBluetoothActivityReporting() {
- return true;
- }
- };
+ private final MockBatteryStatsImpl mBatteryStats;
private BatteryUsageStats mBatteryUsageStats;
private boolean mScreenOn;
public BatteryUsageStatsRule() {
+ this(0);
+ }
+
+ public BatteryUsageStatsRule(long currentTime) {
Context context = InstrumentationRegistry.getContext();
mPowerProfile = spy(new PowerProfile(context, true /* forTest */));
+ mMockClocks.currentTime = currentTime;
+ mBatteryStats = new MockBatteryStatsImpl(mMockClocks);
mBatteryStats.setPowerProfile(mPowerProfile);
}
@@ -110,10 +111,17 @@
/** Call only after setting the power profile information. */
public BatteryUsageStatsRule initMeasuredEnergyStatsLocked() {
+ return initMeasuredEnergyStatsLocked(new String[0]);
+ }
+
+ /** Call only after setting the power profile information. */
+ public BatteryUsageStatsRule initMeasuredEnergyStatsLocked(
+ String[] customPowerComponentNames) {
final boolean[] supportedStandardBuckets =
new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
Arrays.fill(supportedStandardBuckets, true);
- mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets, new String[0]);
+ mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets,
+ customPowerComponentNames);
mBatteryStats.informThatAllExternalStatsAreFlushed();
return this;
}
@@ -161,6 +169,10 @@
mMockClocks.uptime = uptimeMs;
}
+ public void setCurrentTime(long currentTimeMs) {
+ mMockClocks.currentTime = currentTimeMs;
+ }
+
BatteryUsageStats apply(PowerCalculator... calculators) {
return apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(),
calculators);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index c8564ac..380b4ae 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -44,13 +44,13 @@
@Test
public void testBuilder() {
- BatteryUsageStats batteryUsageStats = buildBatteryUsageStats();
+ BatteryUsageStats batteryUsageStats = buildBatteryUsageStats().build();
validateBatteryUsageStats(batteryUsageStats);
}
@Test
public void testParcelability() {
- final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats();
+ final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats().build();
final Parcel outParcel = Parcel.obtain();
outParcel.writeParcelable(outBatteryUsageStats, 0);
final byte[] bytes = outParcel.marshall();
@@ -65,9 +65,17 @@
validateBatteryUsageStats(inBatteryUsageStats);
}
+
+ @Test
+ public void testDefaultSessionDuration() {
+ final BatteryUsageStats stats =
+ buildBatteryUsageStats().setStatsDuration(10000).build();
+ assertThat(stats.getStatsDuration()).isEqualTo(10000);
+ }
+
@Test
public void testDump() {
- final BatteryUsageStats stats = buildBatteryUsageStats();
+ final BatteryUsageStats stats = buildBatteryUsageStats().build();
final StringWriter out = new StringWriter();
try (PrintWriter pw = new PrintWriter(out)) {
stats.dump(pw, " ");
@@ -93,7 +101,7 @@
assertThat(allNames).hasSize(BatteryConsumer.POWER_COMPONENT_COUNT);
}
- private BatteryUsageStats buildBatteryUsageStats() {
+ private BatteryUsageStats.Builder buildBatteryUsageStats() {
final MockClocks clocks = new MockClocks();
final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000);
@@ -103,7 +111,8 @@
.setBatteryCapacity(4000)
.setDischargePercentage(20)
.setDischargedPowerRange(1000, 2000)
- .setStatsStartTimestamp(1000);
+ .setStatsStartTimestamp(1000)
+ .setStatsEndTimestamp(3000);
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid)
.setPackageWithHighestDrain("foo")
.setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000)
@@ -152,7 +161,7 @@
.setUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40);
- return builder.build();
+ return builder;
}
public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
@@ -162,6 +171,8 @@
assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000);
assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000);
assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(1000);
+ assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(3000);
+ assertThat(batteryUsageStats.getStatsDuration()).isEqualTo(2000);
final List<UidBatteryConsumer> uidBatteryConsumers =
batteryUsageStats.getUidBatteryConsumers();
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index 2de621c8..5c84794 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -22,6 +22,7 @@
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.UidTraffic;
import android.os.BatteryConsumer;
+import android.os.BatteryStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
@@ -43,111 +44,89 @@
.setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE, 10.0)
.setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX, 50.0)
.setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0)
- .initMeasuredEnergyStatsLocked();
+ .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE, 3700);
@Test
public void testTimerBasedModel() {
- setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID)
- .getOrCreateBluetoothControllerActivityLocked(),
- 1000, 2000, 3000, 0);
-
- setDurationsAndPower(mStatsRule.getUidStats(APP_UID)
- .getOrCreateBluetoothControllerActivityLocked(),
- 4000, 5000, 6000, 0);
-
- setDurationsAndPower((BatteryStatsImpl.ControllerActivityCounterImpl)
- mStatsRule.getBatteryStats().getBluetoothControllerActivity(),
- 6000, 8000, 10000, 0);
+ setupBluetoothEnergyInfo(0, BatteryStats.POWER_DATA_UNAVAILABLE);
BluetoothPowerCalculator calculator =
new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.11388, 6000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(APP_UID),
- 0.24722, 15000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getDeviceBatteryConsumer(),
- 0.40555, 24000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getAppsBatteryConsumer(),
- 0.36111, 21000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
- public void testReportedPowerBasedModel() {
- setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID)
- .getOrCreateBluetoothControllerActivityLocked(),
- 1000, 2000, 3000, 360000);
-
- setDurationsAndPower(mStatsRule.getUidStats(APP_UID)
- .getOrCreateBluetoothControllerActivityLocked(),
- 4000, 5000, 6000, 720000);
-
- setDurationsAndPower((BatteryStatsImpl.ControllerActivityCounterImpl)
- mStatsRule.getBatteryStats().getBluetoothControllerActivity(),
- 6000, 8000, 10000, 1260000);
+ public void testReportedEnergyBasedModel() {
+ setupBluetoothEnergyInfo(4000000, BatteryStats.POWER_DATA_UNAVAILABLE);
BluetoothPowerCalculator calculator =
new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
- mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
-
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.1, 6000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(APP_UID),
- 0.2, 15000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getDeviceBatteryConsumer(),
- 0.35, 24000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getAppsBatteryConsumer(),
- 0.3, 21000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- }
-
- @Test
- public void testMeasuredEnergyBasedModel() {
- final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
- BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0, 100000);
- info.setUidTraffic(new UidTraffic[]{
- new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
- new UidTraffic(APP_UID, 3000, 4000)
- });
- mStatsRule.getBatteryStats().updateBluetoothStateLocked(info, 1200000, 1000, 1000);
-
- final BluetoothPowerCalculator calculator =
- new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
-
mStatsRule.apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(),
calculator);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.10378, 3583, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(APP_UID),
- 0.22950, 8416, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- assertBluetoothPowerAndDuration(
- mStatsRule.getDeviceBatteryConsumer(),
- 0.33333, 12000, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- assertBluetoothPowerAndDuration(
- mStatsRule.getAppsBatteryConsumer(),
- 0.33329, 11999, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ assertCalculatedPower(0.08216, 0.18169, 0.30030, 0.26386,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
- private void setDurationsAndPower(
- BatteryStatsImpl.ControllerActivityCounterImpl controllerActivity, int idleDurationMs,
- int rxDurationMs, int txDurationMs, long powerMaMs) {
- controllerActivity.getIdleTimeCounter().addCountLocked(idleDurationMs);
- controllerActivity.getRxTimeCounter().addCountLocked(rxDurationMs);
- controllerActivity.getTxTimeCounters()[0].addCountLocked(txDurationMs);
- controllerActivity.getPowerCounter().addCountLocked(powerMaMs);
+ @Test
+ public void testMeasuredEnergyBasedModel() {
+ mStatsRule.initMeasuredEnergyStatsLocked();
+ setupBluetoothEnergyInfo(0, 1200000);
+
+ final BluetoothPowerCalculator calculator =
+ new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(calculator);
+
+ assertCalculatedPower(0.10378, 0.22950, 0.33333, 0.33329,
+ BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ }
+
+ @Test
+ public void testIgnoreMeasuredEnergyBasedModel() {
+ mStatsRule.initMeasuredEnergyStatsLocked();
+ setupBluetoothEnergyInfo(4000000, 1200000);
+
+ BluetoothPowerCalculator calculator =
+ new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
+
+ assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ }
+
+ private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) {
+ final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0,
+ reportedEnergyUc);
+ info.setUidTraffic(new UidTraffic[]{
+ new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+ new UidTraffic(APP_UID, 3000, 4000)
+ });
+ mStatsRule.getBatteryStats().updateBluetoothStateLocked(info,
+ consumedEnergyUc, 1000, 1000);
+ }
+
+ private void assertCalculatedPower(double bluetoothUidPowerMah, double appPowerMah,
+ double devicePowerMah, double allAppsPowerMah, int powerModelPowerProfile) {
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+ bluetoothUidPowerMah, 3583, powerModelPowerProfile);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(APP_UID),
+ appPowerMah, 8416, powerModelPowerProfile);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getDeviceBatteryConsumer(),
+ devicePowerMah, 12000, powerModelPowerProfile);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getAppsBatteryConsumer(),
+ allAppsPowerMah, 11999, powerModelPowerProfile);
}
private void assertBluetoothPowerAndDuration(@Nullable BatteryConsumer batteryConsumer,
@@ -162,7 +141,6 @@
long usageDurationMillis = batteryConsumer.getUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
-
assertThat(usageDurationMillis).isEqualTo(durationMs);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
index f8c2bc6..5334f07 100644
--- a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
@@ -38,7 +38,8 @@
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@Rule
- public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .initMeasuredEnergyStatsLocked(new String[]{"CUSTOM_COMPONENT1", "CUSTOM_COMPONENT2"});
@Test
public void testMeasuredEnergyCopiedIntoBatteryConsumers() {
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 7a7d9f5..80def71 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -48,13 +48,6 @@
setExternalStatsSyncLocked(new DummyExternalStatsSync());
informThatAllExternalStatsAreFlushed();
- final boolean[] supportedStandardBuckets =
- new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
- Arrays.fill(supportedStandardBuckets, true);
- final String[] customBucketNames = {"FOO", "BAR"};
- mGlobalMeasuredEnergyStats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
-
// A no-op handler.
mHandler = new Handler(Looper.getMainLooper()) {
};
@@ -64,6 +57,15 @@
this(new MockClocks());
}
+ public void initMeasuredEnergyStats() {
+ final boolean[] supportedStandardBuckets =
+ new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+ Arrays.fill(supportedStandardBuckets, true);
+ final String[] customBucketNames = {"FOO", "BAR"};
+ mGlobalMeasuredEnergyStats =
+ new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
+ }
+
public TimeBase getOnBatteryTimeBase() {
return mOnBatteryTimeBase;
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockClocks.java b/core/tests/coretests/src/com/android/internal/os/MockClocks.java
index d0fa756..c26505e 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockClocks.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockClocks.java
@@ -21,6 +21,8 @@
public long realtime;
/** Uptime in ms */
public long uptime;
+ /** Current time in ms */
+ public long currentTime;
@Override
public long elapsedRealtime() {
@@ -31,4 +33,9 @@
public long uptimeMillis() {
return uptime;
}
+
+ @Override
+ public long currentTimeMillis() {
+ return currentTime;
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
index 4c29c20..c695fc9 100644
--- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
@@ -47,6 +47,7 @@
@Test
public void testMeasuredEnergyBasedModel() {
+ mStatsRule.initMeasuredEnergyStatsLocked();
BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0);
diff --git a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
index 82830f2..a7f4fb3 100644
--- a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
@@ -79,8 +79,8 @@
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
.isWithin(PRECISION).of(0.6);
- BatteryConsumer appConsumer = mStatsRule.getDeviceBatteryConsumer();
+ BatteryConsumer appConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
- .isWithin(PRECISION).of(0.6);
+ .isWithin(PRECISION).of(0.1);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index 2d894f5..a70033b 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -499,4 +499,84 @@
assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_DOZE));
assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_DOZE_SUSPEND));
}
+
+ /** Test MeasuredEnergyStats#isSupportEqualTo */
+ @Test
+ public void testIsSupportEqualTo() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+ Arrays.fill(supportedStandardBuckets, true);
+ final String[] customBucketNames = {"A", "B"};
+
+ final MeasuredEnergyStats stats =
+ new MeasuredEnergyStats(supportedStandardBuckets.clone(),
+ customBucketNames.clone());
+
+ assertTrue(
+ "All standard and custom bucket supports match",
+ stats.isSupportEqualTo(supportedStandardBuckets, customBucketNames));
+
+ boolean[] differentSupportedStandardBuckets = supportedStandardBuckets.clone();
+ differentSupportedStandardBuckets[0] = !differentSupportedStandardBuckets[0];
+ assertFalse(
+ "Standard bucket support mismatch",
+ stats.isSupportEqualTo(differentSupportedStandardBuckets, customBucketNames));
+
+ assertFalse(
+ "Custom bucket support mismatch",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"C", "B"}));
+
+ assertFalse(
+ "Fewer custom buckets supported",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A"}));
+
+ assertFalse(
+ "More custom bucket supported",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B", "C"}));
+
+ assertFalse(
+ "Custom bucket support order changed",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"B", "A"}));
+ }
+
+ /** Test MeasuredEnergyStats#isSupportEqualTo when holding a null array of custom buckets */
+ @Test
+ public void testIsSupportEqualTo_nullCustomBuckets() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+
+ final MeasuredEnergyStats stats =
+ new MeasuredEnergyStats(supportedStandardBuckets.clone(), null);
+
+ assertTrue(
+ "Null custom bucket name lists should match",
+ stats.isSupportEqualTo(supportedStandardBuckets, null));
+
+ assertTrue(
+ "Null and empty custom buckets should match",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[0]));
+
+ assertFalse(
+ "Null custom buckets should not match populated list",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B"}));
+ }
+
+ /** Test MeasuredEnergyStats#isSupportEqualTo when holding an empty array of custom buckets */
+ @Test
+ public void testIsSupportEqualTo_emptyCustomBuckets() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+
+ final MeasuredEnergyStats stats =
+ new MeasuredEnergyStats(supportedStandardBuckets.clone(), new String[0]);
+
+ assertTrue(
+ "Empty custom buckets should match",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[0]));
+
+ assertTrue(
+ "Empty and null custom buckets should match",
+ stats.isSupportEqualTo(supportedStandardBuckets, null));
+
+ assertFalse(
+ "Empty custom buckets should not match populated list",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B"}));
+ }
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 85b60f8..be1e2b2 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -127,6 +127,20 @@
}
prebuilt_etc {
+ name: "privapp_whitelist_com.android.networkstack",
+ sub_dir: "permissions",
+ src: "com.android.networkstack.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.networkstack.tethering",
+ sub_dir: "permissions",
+ src: "com.android.networkstack.tethering.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_com.android.provision",
system_ext_specific: true,
sub_dir: "permissions",
diff --git a/data/etc/car/com.android.car.bugreport.xml b/data/etc/car/com.android.car.bugreport.xml
index 2ff9835..ec9128e 100644
--- a/data/etc/car/com.android.car.bugreport.xml
+++ b/data/etc/car/com.android.car.bugreport.xml
@@ -22,5 +22,6 @@
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
<permission name="android.car.permission.CAR_DRIVING_STATE"/>
+ <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/com.android.networkstack.tethering.xml b/data/etc/com.android.networkstack.tethering.xml
new file mode 100644
index 0000000..f26a961
--- /dev/null
+++ b/data/etc/com.android.networkstack.tethering.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+
+<permissions>
+ <privapp-permissions package="com.android.networkstack.tethering">
+ <permission name="android.permission.BLUETOOTH_PRIVILEGED" />
+ <permission name="android.permission.MANAGE_USB"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+ <permission name="android.permission.TETHER_PRIVILEGED"/>
+ <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+ <permission name="android.permission.UPDATE_DEVICE_STATS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.networkstack.xml b/data/etc/com.android.networkstack.xml
new file mode 100644
index 0000000..06fec1c
--- /dev/null
+++ b/data/etc/com.android.networkstack.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+
+<permissions>
+ <privapp-permissions package="com.android.networkstack">
+ <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/>
+ <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+ <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+ <permission name="android.permission.CONTROL_VPN"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+ <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/>
+ <permission name="android.permission.MANAGE_USB"/>
+ <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/>
+ <permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
+ <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+ <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <permission name="android.permission.READ_WIFI_CREDENTIAL"/>
+ <permission name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"/>
+ <permission name="android.permission.TETHER_PRIVILEGED"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 545a564..0c2b68b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -154,9 +154,6 @@
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<permission name="android.permission.MODIFY_AUDIO_ROUTING" />
<permission name="android.permission.GET_RUNTIME_PERMISSION_GROUP_MAPPING" />
-
- <!-- For permission hub 2 debugging only -->
- <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
</privapp-permissions>
<privapp-permissions package="com.android.phone">
@@ -271,36 +268,6 @@
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
</privapp-permissions>
- <privapp-permissions package="com.android.networkstack">
- <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/>
- <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
- <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
- <permission name="android.permission.CONTROL_VPN"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
- <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/>
- <permission name="android.permission.MANAGE_USB"/>
- <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/>
- <permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
- <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
- <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
- <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
- <permission name="android.permission.READ_WIFI_CREDENTIAL"/>
- <permission name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"/>
- <permission name="android.permission.TETHER_PRIVILEGED"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- </privapp-permissions>
-
- <privapp-permissions package="com.android.networkstack.tethering">
- <permission name="android.permission.BLUETOOTH_PRIVILEGED" />
- <permission name="android.permission.MANAGE_USB"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
- <permission name="android.permission.TETHER_PRIVILEGED"/>
- <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
- <permission name="android.permission.UPDATE_DEVICE_STATS"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.server.telecom">
<permission name="android.permission.BIND_CONNECTION_SERVICE"/>
<permission name="android.permission.BIND_INCALL_SERVICE"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 9b9511b..3c9086d 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -211,12 +211,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
- "-1890326172": {
- "message": "no-history finish of %s on new resume",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1884933373": {
"message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
"level": "INFO",
@@ -1429,6 +1423,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-484194149": {
+ "message": "no-history finish of %s on new resume",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
+ },
"-481924678": {
"message": "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w.isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d",
"level": "DEBUG",
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 8f1223b..954d062 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -1195,6 +1195,7 @@
// so not checking for isolated process here.
initHintSession();
+ nSetIsHighEndGfx(ActivityManager.isHighEndGfx());
// Defensively clear out the context in case we were passed a context that can leak
// if we live longer than it, e.g. an activity context.
mContext = null;
@@ -1315,6 +1316,8 @@
private static native void nSetSdrWhitePoint(long nativeProxy, float whitePoint);
+ private static native void nSetIsHighEndGfx(boolean isHighEndGfx);
+
private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
private static native void nDestroy(long nativeProxy, long rootRenderNode);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index e222570..51bf6d53 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -255,6 +255,12 @@
| FILTER_BITMAP_FLAG;
/**
+ * These flags are always set on a reset paint or a new paint instantiated using
+ * {@link #Paint()}.
+ */
+ private static final int DEFAULT_PAINT_FLAGS = ANTI_ALIAS_FLAG | DITHER_FLAG;
+
+ /**
* Font hinter option that disables font hinting.
*
* @see #setHinting(int)
@@ -570,10 +576,13 @@
* accelerated drawing always acts as if {@link #FILTER_BITMAP_FLAG} is set.
* On devices running {@link Build.VERSION_CODES#Q} and above,
* {@code FILTER_BITMAP_FLAG} is set by this constructor, and it can be
- * cleared with {@link #setFlags} or {@link #setFilterBitmap}.</p>
+ * cleared with {@link #setFlags} or {@link #setFilterBitmap}.
+ * On devices running {@link Build.VERSION_CODES#S} and above, {@code ANTI_ALIAS_FLAG} and
+ * {@code DITHER_FLAG} are set by this constructor, and they can be cleared with
+ * {@link #setFlags} or {@link #setAntiAlias} and {@link #setDither}, respectively.</p>
*/
public Paint() {
- this(0);
+ this(DEFAULT_PAINT_FLAGS);
}
/**
@@ -618,7 +627,7 @@
/** Restores the paint to its default settings. */
public void reset() {
nReset(mNativePaint);
- setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
+ setFlags(HIDDEN_DEFAULT_PAINT_FLAGS | DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index 199365e..e582e66 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -122,14 +122,18 @@
* between the center and edge of the circle.
* @param tileMode The Shader tiling mode
*
- * @throws IllegalArgumentException If one of the following circumstances:
- * - There are less than two colors
- * - The colors do not share the same {@link ColorSpace}
- * - The colors do not use a valid {@link ColorSpace}
- * - The {@code stops} parameter is not {@code null} and has a different length
- * from {@code colors}.
- * - The {@param startRadius} is negative
- * - The {@param endRadius} is less than or equal to zero
+ * @throws IllegalArgumentException In one of the following circumstances:
+ * <ul>
+ * <li>There are less than two colors</li>
+ * <li>The colors do not share the same {@link ColorSpace}</li>
+ * <li>The colors do not use a valid {@link ColorSpace}</li>
+ * <li>
+ * The {@code stops} parameter is not {@code null} and has a different length from
+ * {@code colors}.
+ * </li>
+ * <li>The {@code startRadius} is negative</li>
+ * <li>The {@code endRadius} is less than or equal to zero</li>
+ * </ul>
*/
public RadialGradient(float startX, float startY, @FloatRange(from = 0.0f) float startRadius,
float endX, float endY, @FloatRange(from = 0.0f, fromInclusive = false) float endRadius,
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index d00492c..e5651e0 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -43,22 +43,24 @@
+ "uniform shader in_shader;\n";
private static final String SHADER_LIB =
"float triangleNoise(vec2 n) {\n"
- + " n = fract(n * vec2(5.3987, 5.4421));\n"
- + " n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));\n"
- + " float xy = n.x * n.y;\n"
- + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
+ + " n = fract(n * vec2(5.3987, 5.4421));\n"
+ + " n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));\n"
+ + " float xy = n.x * n.y;\n"
+ + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
+ "}"
+ "const float PI = 3.1415926535897932384626;\n"
+ "\n"
+ + "float threshold(float v, float l, float h) {\n"
+ + " return step(l, v) * (1.0 - step(h, v));\n"
+ + "}\n"
+ "float sparkles(vec2 uv, float t) {\n"
+ " float n = triangleNoise(uv);\n"
+ " float s = 0.0;\n"
+ " for (float i = 0; i < 4; i += 1) {\n"
- + " float l = i * 0.01;\n"
- + " float h = l + 0.2;\n"
- + " float o = smoothstep(n - l, h, n);\n"
- + " o *= abs(sin(PI * o * (t + 0.55 * i)));\n"
- + " s += o;\n"
+ + " float l = i * 0.1;\n"
+ + " float h = l + 0.025;\n"
+ + " float o = sin(PI * (t + 0.35 * i));\n"
+ + " s += threshold(n + o, l, h);\n"
+ " }\n"
+ " return saturate(s) * in_sparkleColor.a;\n"
+ "}\n"
@@ -110,22 +112,21 @@
+ " vec2 uv = p * in_resolutionScale;\n"
+ " vec2 densityUv = uv - mod(uv, in_noiseScale);\n"
+ " float turbulence = turbulence(uv, in_turbulencePhase);\n"
- + " float sparkle = sparkles(densityUv, in_noisePhase) * ring * alpha "
+ + " float sparkleAlpha = sparkles(densityUv, in_noisePhase) * ring * alpha "
+ "* turbulence;\n"
+ " float fade = min(fadeIn, 1. - fadeOutRipple);\n"
- + " float circleAlpha = softCircle(p, center, in_maxRadius * scaleIn, 0.2) * fade;\n"
- + " vec3 color = mix(in_color.rgb, in_sparkleColor.rgb, sparkle);\n"
+ + " float waveAlpha = softCircle(p, center, in_maxRadius * scaleIn, 0.2) * fade "
+ + "* in_color.a;\n"
+ + " vec4 waveColor = vec4(in_color.rgb * waveAlpha, waveAlpha);\n"
+ + " vec4 sparkleColor = vec4(in_sparkleColor.rgb * in_sparkleColor.a, "
+ + "in_sparkleColor.a);\n"
+ " float mask = in_hasMask == 1. ? sample(in_shader, p).a > 0. ? 1. : 0. : 1.;\n"
- + " float a = (in_color.a * circleAlpha + in_sparkleColor.a * sparkle) * mask;\n"
- + " return vec4(color * a, a);\n"
+ + " return mix(waveColor, sparkleColor, sparkleAlpha) * mask;\n"
+ "}";
private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN;
private static final double PI_ROTATE_RIGHT = Math.PI * 0.0078125;
private static final double PI_ROTATE_LEFT = Math.PI * -0.0078125;
- private float mNoisePhase;
- private float mProgress;
-
RippleShader() {
super(SHADER, false);
}
@@ -141,15 +142,6 @@
setUniform("in_maxRadius", radius);
}
- /**
- * Continuous offset used as noise phase.
- */
- public void setNoisePhase(float phase) {
- mNoisePhase = phase;
- setUniform("in_noisePhase", phase);
- updateTurbulence();
- }
-
public void setOrigin(float x, float y) {
setUniform("in_origin", new float[] {x, y});
}
@@ -159,18 +151,20 @@
}
public void setProgress(float progress) {
- mProgress = progress;
setUniform("in_progress", progress);
- updateTurbulence();
}
- private void updateTurbulence() {
- final float turbulencePhase = (float) ((mProgress + mNoisePhase * 0.333f) * 5f * Math.PI);
- setUniform("in_turbulencePhase", turbulencePhase);
+ /**
+ * Continuous offset used as noise phase.
+ */
+ public void setNoisePhase(float phase) {
+ setUniform("in_noisePhase", phase * 0.001f);
//
// Keep in sync with: frameworks/base/libs/hwui/pipeline/skia/AnimatedDrawables.h
//
+ final float turbulencePhase = phase;
+ setUniform("in_turbulencePhase", turbulencePhase);
final float scale = 1.5f;
setUniform("in_tCircle1", new float[]{
(float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.cos(scale * 0.55))),
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
index 9c01a4b..df47f73 100644
--- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
@@ -134,7 +134,11 @@
throw new IllegalArgumentException("customizationType must be specified");
}
if (customizationType.equals("new-named-family")) {
- out.add(FontListParser.readFamily(parser, fontDir, updatableFontMap, false));
+ FontFamily fontFamily = FontListParser.readFamily(
+ parser, fontDir, updatableFontMap, false);
+ if (fontFamily != null) {
+ out.add(fontFamily);
+ }
} else {
throw new IllegalArgumentException("Unknown customizationType=" + customizationType);
}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 255f9e6..0da2b51 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -85,6 +85,15 @@
}
}
+ /**
+ * @hide
+ */
+ public static void resetAvailableFonts() {
+ synchronized (LOCK) {
+ sAvailableFonts = null;
+ }
+ }
+
private static @Nullable ByteBuffer mmap(@NonNull String fullPath) {
try (FileInputStream file = new FileInputStream(fullPath)) {
final FileChannel fileChannel = file.getChannel();
@@ -262,8 +271,14 @@
*/
@VisibleForTesting
public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) {
+ return buildSystemFallback(fontConfig, new ArrayMap<>());
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig,
+ ArrayMap<String, ByteBuffer> outBufferCache) {
final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>();
- final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
@@ -273,7 +288,7 @@
if (familyName == null) {
continue;
}
- appendNamedFamily(xmlFamily, bufferCache, fallbackListMap);
+ appendNamedFamily(xmlFamily, outBufferCache, fallbackListMap);
}
// Then, add fallback fonts to the each fallback map.
@@ -282,7 +297,7 @@
// The first family (usually the sans-serif family) is always placed immediately
// after the primary family in the fallback.
if (i == 0 || xmlFamily.getName() == null) {
- pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache);
+ pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
}
}
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index df579bb..1034847 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
@@ -140,6 +141,7 @@
if (mBinder == null || retryLookup) {
mBinder = IKeystoreService.Stub.asInterface(ServiceManager
.getService(KEYSTORE2_SERVICE_NAME));
+ Binder.allowBlocking(mBinder.asBinder());
}
return mBinder;
}
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index a6552dd..e6c1ea8 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.hardware.security.keymint.KeyParameter;
+import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.security.keymaster.KeymasterDefs;
@@ -39,6 +40,7 @@
Long challenge,
KeyParameter[] parameters
) {
+ Binder.allowBlocking(operation.asBinder());
this.mOperation = operation;
this.mChallenge = challenge;
this.mParameters = parameters;
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index d188b65..b85dd74 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.app.compat.CompatChanges;
import android.hardware.security.keymint.KeyParameter;
+import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.security.keystore.BackendBusyException;
@@ -45,6 +46,7 @@
private final IKeystoreSecurityLevel mSecurityLevel;
public KeyStoreSecurityLevel(IKeystoreSecurityLevel securityLevel) {
+ Binder.allowBlocking(securityLevel.asBinder());
this.mSecurityLevel = securityLevel;
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index fe05989..97592b4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -252,7 +252,9 @@
blockModes,
userAuthenticationRequired,
(int) userAuthenticationValidityDurationSeconds,
- keymasterHwEnforcedUserAuthenticators,
+ userAuthenticationRequirementEnforcedBySecureHardware
+ ? keymasterHwEnforcedUserAuthenticators
+ : keymasterSwEnforcedUserAuthenticators,
userAuthenticationRequirementEnforcedBySecureHardware,
userAuthenticationValidWhileOnBody,
trustedUserPresenceRequired,
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 3e2fb94..f3cfcf1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -41,6 +41,8 @@
import android.system.keystore2.ResponseCode;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -974,7 +976,6 @@
}
private Set<String> getUniqueAliases() {
-
try {
final KeyDescriptor[] keys = mKeyStore.list(
getTargetDomain(),
@@ -987,7 +988,7 @@
return aliases;
} catch (android.security.KeyStoreException e) {
Log.e(TAG, "Failed to list keystore entries.", e);
- return null;
+ return new HashSet<>();
}
}
@@ -1099,6 +1100,17 @@
return caAlias;
}
+ /**
+ * Used by Tests to initialize with a fake KeyStore2.
+ * @hide
+ * @param keystore
+ */
+ @VisibleForTesting
+ public void initForTesting(KeyStore2 keystore) {
+ mKeyStore = keystore;
+ mNamespace = KeyProperties.NAMESPACE_APPLICATION;
+ }
+
@Override
public void engineStore(OutputStream stream, char[] password) throws IOException,
NoSuchAlgorithmException, CertificateException {
diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp
index 2315a85..7de4523 100644
--- a/keystore/tests/Android.bp
+++ b/keystore/tests/Android.bp
@@ -28,6 +28,7 @@
static_libs: [
"androidx.test.rules",
"hamcrest-library",
+ "mockito-target-minus-junit4",
],
platform_apis: true,
libs: ["android.test.runner"],
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index b7d72fc..2ae61ab 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -43,7 +43,6 @@
static final String ALIAS = "keystore-alias";
static final String ANOTHER_ALIAS = "another-keystore-alias";
static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY;
- static final int UID = 1230;
static final int KEYSIZE = 2048;
static final X500Principal SUBJECT = new X500Principal("CN=subject");
static final BigInteger SERIAL = new BigInteger("1234567890");
@@ -61,7 +60,7 @@
public static KeyGenParameterSpec configureDefaultSpec() {
return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
- .setUid(UID)
+ .setNamespace(KeyProperties.NAMESPACE_WIFI)
.setKeySize(KEYSIZE)
.setCertificateSubject(SUBJECT)
.setCertificateSerialNumber(SERIAL)
@@ -88,10 +87,11 @@
.build();
}
- public static void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
+ public static void validateSpecValues(KeyGenParameterSpec spec,
+ @KeyProperties.Namespace int namespace, String alias) {
assertThat(spec.getKeystoreAlias(), is(alias));
assertThat(spec.getPurposes(), is(KEY_PURPOSES));
- assertThat(spec.getUid(), is(uid));
+ assertThat(spec.getNamespace(), is(namespace));
assertThat(spec.getKeySize(), is(KEYSIZE));
assertThat(spec.getCertificateSubject(), is(SUBJECT));
assertThat(spec.getCertificateSerialNumber(), is(SERIAL));
@@ -134,7 +134,7 @@
Parcel parcel = parcelForReading(spec);
ParcelableKeyGenParameterSpec fromParcel =
ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel);
- validateSpecValues(fromParcel.getSpec(), UID, ALIAS);
+ validateSpecValues(fromParcel.getSpec(), KeyProperties.NAMESPACE_WIFI, ALIAS);
assertThat(parcel.dataAvail(), is(0));
}
diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
index b2edfd0..ddbb1d8 100644
--- a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
@@ -21,8 +21,6 @@
import static org.junit.Assert.assertThat;
import android.security.ParcelableKeyGenParameterSpecTest;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
import androidx.test.runner.AndroidJUnit4;
@@ -41,7 +39,7 @@
KeyGenParameterSpec copiedSpec =
new KeyGenParameterSpec.Builder(spec).build();
ParcelableKeyGenParameterSpecTest.validateSpecValues(
- copiedSpec, spec.getUid(), spec.getKeystoreAlias());
+ copiedSpec, spec.getNamespace(), spec.getKeystoreAlias());
}
@Test
diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java
new file mode 100644
index 0000000..1bd3069
--- /dev/null
+++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.security.KeyStore2;
+import android.security.KeyStoreException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class AndroidKeyStoreSpiTest {
+
+ @Mock
+ private KeyStore2 mKeystore2;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testEngineAliasesReturnsEmptySetOnKeyStoreError() throws Exception {
+ when(mKeystore2.list(anyInt(), anyLong()))
+ .thenThrow(new KeyStoreException(6, "Some Error"));
+ AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi();
+ spi.initForTesting(mKeystore2);
+
+ assertThat("Empty collection expected", !spi.engineAliases().hasMoreElements());
+
+ verify(mKeystore2).list(anyInt(), anyLong());
+ }
+
+}
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index 6bd0e0a..10df726 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -18,6 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell">
<!-- System permission required by WM Shell Task Organizer. -->
+ <uses-permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 26f98d8..4c2863e 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -24,6 +24,9 @@
<!-- Animation duration for resizing of PIP. -->
<integer name="config_pipResizeAnimationDuration">425</integer>
+ <!-- Animation duration for crossfading of PIP (specifically to fade out the layer on top). -->
+ <integer name="config_pipCrossfadeAnimationDuration">150</integer>
+
<!-- Allow dragging the PIP to a location to close it -->
<bool name="config_pipEnableDismissDragToEdge">true</bool>
diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml
index 434a000..8831b61 100644
--- a/libs/WindowManager/Shell/res/values/ids.xml
+++ b/libs/WindowManager/Shell/res/values/ids.xml
@@ -16,6 +16,8 @@
-->
<resources>
<item type="id" name="action_pip_resize" />
+ <item type="id" name="action_pip_stash" />
+ <item type="id" name="action_pip_unstash" />
<!-- Accessibility actions for the docked stack divider -->
<item type="id" name="action_move_tl_full" />
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index b1425e4..e512698 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -48,6 +48,12 @@
<!-- Accessibility action for resizing PIP [CHAR LIMIT=NONE] -->
<string name="accessibility_action_pip_resize">Resize</string>
+ <!-- Accessibility action for stashing PIP [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_pip_stash">Stash</string>
+
+ <!-- Accessibility action for unstashing PIP [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_pip_unstash">Unstash</string>
+
<!-- TODO Deprecated. Label for PIP action to Minimize the PIP. DO NOT TRANSLATE [CHAR LIMIT=25] -->
<string name="pip_phone_minimize">Minimize</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index 6984ea45..006730d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -42,7 +42,7 @@
private final SyncTransactionQueue mSyncQueue;
- private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>();
+ private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>();
public FullscreenTaskListener(SyncTransactionQueue syncQueue) {
mSyncQueue = syncQueue;
@@ -50,14 +50,14 @@
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- if (mLeashByTaskId.get(taskInfo.taskId) != null) {
+ if (mDataByTaskId.get(taskInfo.taskId) != null) {
throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
taskInfo.taskId);
- mLeashByTaskId.put(taskInfo.taskId, leash);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
final Point positionInParent = taskInfo.positionInParent;
+ mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent));
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
mSyncQueue.runInSync(t -> {
// Reset several properties back to fullscreen (PiP, for example, leaves all these
// properties in a bad state).
@@ -72,45 +72,57 @@
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
- final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId);
+ final TaskData data = mDataByTaskId.get(taskInfo.taskId);
final Point positionInParent = taskInfo.positionInParent;
- mSyncQueue.runInSync(t -> {
- // Reset several properties back. For instance, when an Activity enters PiP with
- // multiple activities in the same task, a new task will be created from that Activity
- // and we want reset the leash of the original task.
- t.setPosition(leash, positionInParent.x, positionInParent.y);
- t.setWindowCrop(leash, null);
- });
+ if (!positionInParent.equals(data.positionInParent)) {
+ data.positionInParent.set(positionInParent.x, positionInParent.y);
+ mSyncQueue.runInSync(t -> {
+ t.setPosition(data.surface, positionInParent.x, positionInParent.y);
+ });
+ }
}
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- if (mLeashByTaskId.get(taskInfo.taskId) == null) {
+ if (mDataByTaskId.get(taskInfo.taskId) == null) {
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
return;
}
- mLeashByTaskId.remove(taskInfo.taskId);
+ mDataByTaskId.remove(taskInfo.taskId);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
taskInfo.taskId);
}
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
- if (!mLeashByTaskId.contains(taskId)) {
+ if (!mDataByTaskId.contains(taskId)) {
throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
}
- b.setParent(mLeashByTaskId.get(taskId));
+ b.setParent(mDataByTaskId.get(taskId).surface);
}
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + this);
- pw.println(innerPrefix + mLeashByTaskId.size() + " Tasks");
+ pw.println(innerPrefix + mDataByTaskId.size() + " Tasks");
}
@Override
public String toString() {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
}
+
+ /**
+ * Per-task data for each managed task.
+ */
+ private static class TaskData {
+ public final SurfaceControl surface;
+ public final Point positionInParent;
+
+ public TaskData(SurfaceControl surface, Point positionInParent) {
+ this.surface = surface;
+ this.positionInParent = positionInParent;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index d451f4a..0b941b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -16,14 +16,14 @@
package com.android.wm.shell;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
@@ -145,7 +145,7 @@
}
final int taskId = new Integer(args[2]);
final int sideStagePosition = args.length > 3
- ? new Integer(args[3]) : STAGE_POSITION_BOTTOM_OR_RIGHT;
+ ? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT;
mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index eb82c6d..e6d088e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -20,11 +20,15 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
import android.graphics.Rect;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -35,6 +39,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitLayout;
@@ -45,7 +50,7 @@
* {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
* Also includes all UI for managing the pair like the divider.
*/
-class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChangeListener {
+class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayoutHandler {
private static final String TAG = AppPair.class.getSimpleName();
private ActivityManager.RunningTaskInfo mRootTaskInfo;
@@ -54,6 +59,9 @@
private SurfaceControl mTaskLeash1;
private ActivityManager.RunningTaskInfo mTaskInfo2;
private SurfaceControl mTaskLeash2;
+ private SurfaceControl mDimLayer1;
+ private SurfaceControl mDimLayer2;
+ private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final AppPairsController mController;
private final SyncTransactionQueue mSyncQueue;
@@ -101,7 +109,8 @@
mSplitLayout = new SplitLayout(TAG + "SplitDivider",
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
mRootTaskInfo.configuration, this /* layoutChangeListener */,
- b -> b.setParent(mRootTaskLeash), mDisplayImeController);
+ b -> b.setParent(mRootTaskLeash), mDisplayImeController,
+ mController.getTaskOrganizer());
final WindowContainerToken token1 = task1.token;
final WindowContainerToken token2 = task2.token;
@@ -153,9 +162,13 @@
} else if (taskInfo.taskId == getTaskId1()) {
mTaskInfo1 = taskInfo;
mTaskLeash1 = leash;
+ mSyncQueue.runInSync(t -> mDimLayer1 =
+ SurfaceUtils.makeDimLayer(t, mTaskLeash1, "Dim layer", mSurfaceSession));
} else if (taskInfo.taskId == getTaskId2()) {
mTaskInfo2 = taskInfo;
mTaskLeash2 = leash;
+ mSyncQueue.runInSync(t -> mDimLayer2 =
+ SurfaceUtils.makeDimLayer(t, mTaskLeash2, "Dim layer", mSurfaceSession));
} else {
throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
}
@@ -182,6 +195,11 @@
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (!taskInfo.supportsMultiWindow) {
+ // Dismiss AppPair if the task no longer supports multi window.
+ mController.unpair(mRootTaskInfo.taskId);
+ return;
+ }
if (taskInfo.taskId == getRootTaskId()) {
if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
mSyncQueue.runInSync(t -> {
@@ -208,12 +226,31 @@
}
@Override
+ public int getSplitItemPosition(WindowContainerToken token) {
+ if (token == null) {
+ return SPLIT_POSITION_UNDEFINED;
+ }
+
+ if (token.equals(mTaskInfo1.getToken())) {
+ return SPLIT_POSITION_TOP_OR_LEFT;
+ } else if (token.equals(mTaskInfo2.getToken())) {
+ return SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ }
+
+ return SPLIT_POSITION_UNDEFINED;
+ }
+
+ @Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
if (taskInfo.taskId == getRootTaskId()) {
// We don't want to release this object back to the pool since the root task went away.
mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */);
- } else if (taskInfo.taskId == getTaskId1() || taskInfo.taskId == getTaskId2()) {
+ } else if (taskInfo.taskId == getTaskId1()) {
mController.unpair(mRootTaskInfo.taskId);
+ mSyncQueue.runInSync(t -> t.remove(mDimLayer1));
+ } else if (taskInfo.taskId == getTaskId2()) {
+ mController.unpair(mRootTaskInfo.taskId);
+ mSyncQueue.runInSync(t -> t.remove(mDimLayer2));
}
}
@@ -259,40 +296,16 @@
@Override
public void onBoundsChanging(SplitLayout layout) {
- final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
- if (dividerLeash == null) return;
- final Rect dividerBounds = layout.getDividerBounds();
- final Rect bounds1 = layout.getBounds1();
- final Rect bounds2 = layout.getBounds2();
- mSyncQueue.runInSync(t -> t
- .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
- .setPosition(mTaskLeash1, bounds1.left, bounds1.top)
- .setPosition(mTaskLeash2, bounds2.left, bounds2.top)
- // Sets crop to prevent visible region of tasks overlap with each other when
- // re-positioning surfaces while resizing.
- .setWindowCrop(mTaskLeash1, bounds1.width(), bounds1.height())
- .setWindowCrop(mTaskLeash2, bounds2.width(), bounds2.height()));
+ mSyncQueue.runInSync(t ->
+ layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
}
@Override
public void onBoundsChanged(SplitLayout layout) {
- final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
- if (dividerLeash == null) return;
- final Rect dividerBounds = layout.getDividerBounds();
- final Rect bounds1 = layout.getBounds1();
- final Rect bounds2 = layout.getBounds2();
final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(mTaskInfo1.token, bounds1)
- .setBounds(mTaskInfo2.token, bounds2);
- mController.getTaskOrganizer().applyTransaction(wct);
- mSyncQueue.runInSync(t -> t
- // Resets layer of divider bar to make sure it is always on top.
- .setLayer(dividerLeash, Integer.MAX_VALUE)
- .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
- .setPosition(mTaskLeash1, bounds1.left, bounds1.top)
- .setPosition(mTaskLeash2, bounds2.left, bounds2.top)
- // Resets crop to apply new surface bounds directly.
- .setWindowCrop(mTaskLeash1, null)
- .setWindowCrop(mTaskLeash2, null));
+ layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
+ mSyncQueue.queue(wct);
+ mSyncQueue.runInSync(t ->
+ layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
new file mode 100644
index 0000000..e59845c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common;
+
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+/**
+ * Helpers for working with screenshots.
+ */
+public class ScreenshotUtils {
+
+ /**
+ * Take a screenshot of the specified SurfaceControl.
+ *
+ * @param t the transaction used to set changes on the resulting screenshot.
+ * @param sc the SurfaceControl to take a screenshot of
+ * @param crop the crop to use when capturing the screenshot
+ *
+ * @return A SurfaceControl where the screenshot will be attached, or null if failed.
+ */
+ public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
+ Rect crop) {
+ final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers(
+ new SurfaceControl.LayerCaptureArgs.Builder(sc)
+ .setSourceCrop(crop)
+ .setCaptureSecureLayers(true)
+ .setAllowProtected(true)
+ .build()
+ );
+ if (buffer == null || buffer.getHardwareBuffer() == null) {
+ return null;
+ }
+ final GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
+ buffer.getHardwareBuffer());
+ final SurfaceControl screenshot = new SurfaceControl.Builder()
+ .setName("ScreenshotUtils screenshot")
+ .setFormat(PixelFormat.TRANSLUCENT)
+ .setSecure(buffer.containsSecureLayers())
+ .setCallsite("ScreenshotUtils.takeScreenshot")
+ .setBLASTLayer()
+ .build();
+
+ t.setBuffer(screenshot, graphicBuffer);
+ t.setColorSpace(screenshot, buffer.getColorSpace());
+ t.reparent(screenshot, sc);
+ t.setLayer(screenshot, Integer.MAX_VALUE);
+ t.show(screenshot);
+ t.apply();
+ return screenshot;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
new file mode 100644
index 0000000..55c5125
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common;
+
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+/**
+ * Helpers for handling surface.
+ */
+public class SurfaceUtils {
+ /** Creates a dim layer above indicated host surface. */
+ public static SurfaceControl makeDimLayer(SurfaceControl.Transaction t, SurfaceControl host,
+ String name, SurfaceSession surfaceSession) {
+ SurfaceControl dimLayer = new SurfaceControl.Builder(surfaceSession)
+ .setParent(host)
+ .setColorLayer()
+ .setName(name)
+ .setCallsite("SurfaceUtils.makeDimLayer")
+ .build();
+ t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
+ return dimLayer;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 442e7a4..cba019a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -36,13 +36,11 @@
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
-import com.android.wm.shell.common.DisplayImeController;
/**
* Divider for multi window splits.
*/
-public class DividerView extends FrameLayout implements View.OnTouchListener,
- DisplayImeController.ImePositionProcessor {
+public class DividerView extends FrameLayout implements View.OnTouchListener {
public static final long TOUCH_ANIMATION_DURATION = 150;
public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
@@ -99,12 +97,6 @@
}
@Override
- public void onImeVisibilityChanged(int displayId, boolean isShowing) {
- if (displayId != getDisplay().getDisplayId()) return;
- setInteractive(!isShowing);
- }
-
- @Override
public boolean onTouch(View v, MotionEvent event) {
if (mSplitLayout == null || !mInteractive) {
return false;
@@ -139,11 +131,10 @@
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- mVelocityTracker.addMovement(event);
releaseTouching();
-
if (!mMoving) break;
+ mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(1000 /* units */);
final float velocity = isLandscape
? mVelocityTracker.getXVelocity()
@@ -217,7 +208,7 @@
mViewHost.relayout(lp);
}
- private void setInteractive(boolean interactive) {
+ void setInteractive(boolean interactive) {
if (interactive == mInteractive) return;
mInteractive = interactive;
releaseTouching();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index d318a5a..e42f511 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -25,16 +25,22 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayImeController;
@@ -42,7 +48,32 @@
* Records and handles layout of splits. Helps to calculate proper bounds when configuration or
* divide position changes.
*/
-public class SplitLayout {
+public final class SplitLayout {
+ /**
+ * Split position isn't specified normally meaning to use what ever it is currently set to.
+ */
+ public static final int SPLIT_POSITION_UNDEFINED = -1;
+
+ /**
+ * Specifies that a split is positioned at the top half of the screen if
+ * in portrait mode or at the left half of the screen if in landscape mode.
+ */
+ public static final int SPLIT_POSITION_TOP_OR_LEFT = 0;
+
+ /**
+ * Specifies that a split is positioned at the bottom half of the screen if
+ * in portrait mode or at the right half of the screen if in landscape mode.
+ */
+ public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1;
+
+ @IntDef(prefix = {"SPLIT_POSITION_"}, value = {
+ SPLIT_POSITION_UNDEFINED,
+ SPLIT_POSITION_TOP_OR_LEFT,
+ SPLIT_POSITION_BOTTOM_OR_RIGHT
+ })
+ public @interface SplitPosition {
+ }
+
private final int mDividerWindowWidth;
private final int mDividerInsets;
private final int mDividerSize;
@@ -51,8 +82,11 @@
private final Rect mDividerBounds = new Rect();
private final Rect mBounds1 = new Rect();
private final Rect mBounds2 = new Rect();
- private final LayoutChangeListener mLayoutChangeListener;
+ private final SplitLayoutHandler mSplitLayoutHandler;
private final SplitWindowManager mSplitWindowManager;
+ private final DisplayImeController mDisplayImeController;
+ private final ImePositionProcessor mImePositionProcessor;
+ private final ShellTaskOrganizer mTaskOrganizer;
private Context mContext;
private DividerSnapAlgorithm mDividerSnapAlgorithm;
@@ -60,18 +94,21 @@
private boolean mInitialized = false;
public SplitLayout(String windowName, Context context, Configuration configuration,
- LayoutChangeListener layoutChangeListener,
+ SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
- DisplayImeController displayImeController) {
+ DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
mContext = context.createConfigurationContext(configuration);
- mLayoutChangeListener = layoutChangeListener;
+ mSplitLayoutHandler = splitLayoutHandler;
+ mDisplayImeController = displayImeController;
mSplitWindowManager = new SplitWindowManager(
- windowName, mContext, configuration, parentContainerCallbacks,
- displayImeController);
+ windowName, mContext, configuration, parentContainerCallbacks);
+ mTaskOrganizer = taskOrganizer;
+ mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
- mDividerWindowWidth = context.getResources().getDimensionPixelSize(
+ final Resources resources = context.getResources();
+ mDividerWindowWidth = resources.getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
- mDividerInsets = context.getResources().getDimensionPixelSize(
+ mDividerInsets = resources.getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_insets);
mDividerSize = mDividerWindowWidth - mDividerInsets * 2;
@@ -82,17 +119,17 @@
/** Gets bounds of the primary split. */
public Rect getBounds1() {
- return mBounds1;
+ return new Rect(mBounds1);
}
/** Gets bounds of the secondary split. */
public Rect getBounds2() {
- return mBounds2;
+ return new Rect(mBounds2);
}
/** Gets bounds of divider window. */
public Rect getDividerBounds() {
- return mDividerBounds;
+ return new Rect(mDividerBounds);
}
/** Returns leash of the current divider bar. */
@@ -153,6 +190,7 @@
if (mInitialized) return;
mInitialized = true;
mSplitWindowManager.init(this);
+ mDisplayImeController.addPositionProcessor(mImePositionProcessor);
}
/** Releases the surface holding the current {@link DividerView}. */
@@ -160,6 +198,8 @@
if (!mInitialized) return;
mInitialized = false;
mSplitWindowManager.release();
+ mDisplayImeController.removePositionProcessor(mImePositionProcessor);
+ mImePositionProcessor.reset();
}
/**
@@ -168,14 +208,14 @@
*/
void updateDivideBounds(int position) {
updateBounds(position);
- mLayoutChangeListener.onBoundsChanging(this);
mSplitWindowManager.setResizingSplits(true);
+ mSplitLayoutHandler.onBoundsChanging(this);
}
void setDividePosition(int position) {
mDividePosition = position;
updateBounds(mDividePosition);
- mLayoutChangeListener.onBoundsChanged(this);
+ mSplitLayoutHandler.onBoundsChanged(this);
mSplitWindowManager.setResizingSplits(false);
}
@@ -192,11 +232,11 @@
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
switch (snapTarget.flag) {
case FLAG_DISMISS_START:
- mLayoutChangeListener.onSnappedToDismiss(false /* bottomOrRight */);
+ mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */);
mSplitWindowManager.setResizingSplits(false);
break;
case FLAG_DISMISS_END:
- mLayoutChangeListener.onSnappedToDismiss(true /* bottomOrRight */);
+ mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */);
mSplitWindowManager.setResizingSplits(false);
break;
default:
@@ -206,7 +246,7 @@
}
void onDoubleTappedDivider() {
- mLayoutChangeListener.onDoubleTappedDivider();
+ mSplitLayoutHandler.onDoubleTappedDivider();
}
/**
@@ -265,8 +305,38 @@
return bounds.width() > bounds.height();
}
- /** Listens layout change event. */
- public interface LayoutChangeListener {
+ /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
+ public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1,
+ SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
+ final Rect dividerBounds = mImePositionProcessor.adjustForIme(mDividerBounds);
+ final Rect bounds1 = mImePositionProcessor.adjustForIme(mBounds1);
+ final Rect bounds2 = mImePositionProcessor.adjustForIme(mBounds2);
+ final SurfaceControl dividerLeash = getDividerLeash();
+ if (dividerLeash != null) {
+ t.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
+ // Resets layer of divider bar to make sure it is always on top.
+ .setLayer(dividerLeash, Integer.MAX_VALUE);
+ }
+
+ t.setPosition(leash1, bounds1.left, bounds1.top)
+ .setWindowCrop(leash1, bounds1.width(), bounds1.height());
+
+ t.setPosition(leash2, bounds2.left, bounds2.top)
+ .setWindowCrop(leash2, bounds2.width(), bounds2.height());
+
+ mImePositionProcessor.applySurfaceDimValues(t, dimLayer1, dimLayer2);
+ }
+
+ /** Apply recorded task layout to the {@link WindowContainerTransaction}. */
+ public void applyTaskChanges(WindowContainerTransaction wct,
+ ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
+ wct.setBounds(task1.token, mImePositionProcessor.adjustForIme(mBounds1))
+ .setBounds(task2.token, mImePositionProcessor.adjustForIme(mBounds2));
+ }
+
+ /** Handles layout change event. */
+ public interface SplitLayoutHandler {
+
/** Calls when dismissing split. */
void onSnappedToDismiss(boolean snappedToEnd);
@@ -279,5 +349,133 @@
/** Calls when user double tapped on the divider bar. */
default void onDoubleTappedDivider() {
}
+
+ /** Returns split position of the token. */
+ @SplitPosition
+ int getSplitItemPosition(WindowContainerToken token);
+ }
+
+ /** Records IME top offset changes and updates SplitLayout correspondingly. */
+ private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor {
+ /**
+ * Maximum size of an adjusted split bounds relative to original stack bounds. Used to
+ * restrict IME adjustment so that a min portion of top split remains visible.
+ */
+ private static final float ADJUSTED_SPLIT_FRACTION_MAX = 0.7f;
+ private static final float ADJUSTED_NONFOCUS_DIM = 0.3f;
+
+ private final int mDisplayId;
+
+ private int mYOffsetForIme;
+ private float mDimValue1;
+ private float mDimValue2;
+
+ private int mStartImeTop;
+ private int mEndImeTop;
+
+ private int mTargetYOffset;
+ private int mLastYOffset;
+ private float mTargetDim1;
+ private float mTargetDim2;
+ private float mLastDim1;
+ private float mLastDim2;
+
+ private ImePositionProcessor(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ @Override
+ public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
+ boolean showing, boolean isFloating, SurfaceControl.Transaction t) {
+ if (displayId != mDisplayId) return 0;
+ final int imeTargetPosition = getImeTargetPosition();
+ if (!mInitialized || imeTargetPosition == SPLIT_POSITION_UNDEFINED) return 0;
+ mStartImeTop = showing ? hiddenTop : shownTop;
+ mEndImeTop = showing ? shownTop : hiddenTop;
+
+ // Update target dim values
+ mLastDim1 = mDimValue1;
+ mTargetDim1 = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT && showing
+ ? ADJUSTED_NONFOCUS_DIM : 0.0f;
+ mLastDim2 = mDimValue2;
+ mTargetDim2 = imeTargetPosition == SPLIT_POSITION_TOP_OR_LEFT && showing
+ ? ADJUSTED_NONFOCUS_DIM : 0.0f;
+
+ // Calculate target bounds offset for IME
+ mLastYOffset = mYOffsetForIme;
+ final boolean needOffset = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
+ && !isFloating && !isLandscape(mRootBounds) && showing;
+ mTargetYOffset = needOffset ? getTargetYOffset() : 0;
+
+ // Make {@link DividerView} non-interactive while IME showing in split mode. Listen to
+ // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
+ // because DividerView won't receive onImeVisibilityChanged callback after it being
+ // re-inflated.
+ mSplitWindowManager.setInteractive(
+ !showing || imeTargetPosition == SPLIT_POSITION_UNDEFINED);
+
+ return 0;
+ }
+
+ @Override
+ public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
+ if (displayId != mDisplayId) return;
+ onProgress(getProgress(imeTop));
+ mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
+ }
+
+ @Override
+ public void onImeEndPositioning(int displayId, boolean cancel,
+ SurfaceControl.Transaction t) {
+ if (displayId != mDisplayId || cancel) return;
+ onProgress(1.0f);
+ mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
+ }
+
+ private int getTargetYOffset() {
+ final int desireOffset = Math.abs(mEndImeTop - mStartImeTop);
+ // Make sure to keep at least 30% visible for the top split.
+ final int maxOffset = (int) (mBounds1.height() * ADJUSTED_SPLIT_FRACTION_MAX);
+ return -Math.min(desireOffset, maxOffset);
+ }
+
+ @SplitPosition
+ private int getImeTargetPosition() {
+ final WindowContainerToken token = mTaskOrganizer.getImeTarget(mDisplayId);
+ return mSplitLayoutHandler.getSplitItemPosition(token);
+ }
+
+ private float getProgress(int currImeTop) {
+ return ((float) currImeTop - mStartImeTop) / (mEndImeTop - mStartImeTop);
+ }
+
+ private void onProgress(float progress) {
+ mDimValue1 = getProgressValue(mLastDim1, mTargetDim1, progress);
+ mDimValue2 = getProgressValue(mLastDim2, mTargetDim2, progress);
+ mYOffsetForIme =
+ (int) getProgressValue((float) mLastYOffset, (float) mTargetYOffset, progress);
+ }
+
+ private float getProgressValue(float start, float end, float progress) {
+ return start + (end - start) * progress;
+ }
+
+ private void reset() {
+ mYOffsetForIme = 0;
+ mDimValue1 = mDimValue2 = 0.0f;
+ }
+
+ /* Adjust bounds with IME offset. */
+ private Rect adjustForIme(Rect bounds) {
+ final Rect temp = new Rect(bounds);
+ if (mYOffsetForIme != 0) temp.offset(0, mYOffsetForIme);
+ return temp;
+ }
+
+ private void applySurfaceDimValues(SurfaceControl.Transaction t, SurfaceControl dimLayer1,
+ SurfaceControl dimLayer2) {
+ t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);
+ t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index f6efb01..0cea0ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -46,7 +46,6 @@
import androidx.annotation.Nullable;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayImeController;
/**
* Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split.
@@ -55,7 +54,6 @@
private static final String TAG = SplitWindowManager.class.getSimpleName();
private final String mWindowName;
- private final DisplayImeController mDisplayImeController;
private final ParentContainerCallbacks mParentContainerCallbacks;
private Context mContext;
private SurfaceControlViewHost mViewHost;
@@ -68,13 +66,11 @@
}
public SplitWindowManager(String windowName, Context context, Configuration config,
- ParentContainerCallbacks parentContainerCallbacks,
- DisplayImeController displayImeController) {
+ ParentContainerCallbacks parentContainerCallbacks) {
super(config, null /* rootSurface */, null /* hostInputToken */);
mContext = context.createConfigurationContext(config);
mParentContainerCallbacks = parentContainerCallbacks;
mWindowName = windowName;
- mDisplayImeController = displayImeController;
}
@Override
@@ -128,7 +124,6 @@
lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
mViewHost.setView(mDividerView, lp);
mDividerView.setup(splitLayout, mViewHost);
- mDisplayImeController.addPositionProcessor(mDividerView);
}
/**
@@ -137,7 +132,6 @@
*/
void release() {
if (mDividerView != null) {
- mDisplayImeController.removePositionProcessor(mDividerView);
mDividerView = null;
}
@@ -152,6 +146,11 @@
}
}
+ void setInteractive(boolean interactive) {
+ if (mDividerView == null) return;
+ mDividerView.setInteractive(interactive);
+ }
+
void setResizingSplits(boolean resizing) {
if (resizing == mResizingSplits) return;
try {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 9a09ca4..9bcc3ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -29,14 +29,14 @@
import static android.content.Intent.EXTRA_TASK_ID;
import static android.content.Intent.EXTRA_USER;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -64,7 +64,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.splitscreen.SplitScreen.StagePosition;
+import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -203,10 +203,10 @@
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
@StageType int stage = STAGE_TYPE_UNDEFINED;
- @StagePosition int position = STAGE_POSITION_UNDEFINED;
+ @SplitPosition int position = SPLIT_POSITION_UNDEFINED;
if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
// Update launch options for the split side we are targeting.
- position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
+ position = leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
if (!inSplitScreen) {
// Launch in the side stage if we are not in split-screen already.
stage = STAGE_TYPE_SIDE;
@@ -219,7 +219,7 @@
}
private void startClipDescription(ClipDescription description, Intent intent,
- @StageType int stage, @StagePosition int position) {
+ @StageType int stage, @SplitPosition int position) {
final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
@@ -291,12 +291,12 @@
* Interface for actually committing the task launches.
*/
public interface Starter {
- void startTask(int taskId, @StageType int stage, @StagePosition int position,
+ void startTask(int taskId, @StageType int stage, @SplitPosition int position,
@Nullable Bundle options);
void startShortcut(String packageName, String shortcutId, @StageType int stage,
- @StagePosition int position, @Nullable Bundle options, UserHandle user);
+ @SplitPosition int position, @Nullable Bundle options, UserHandle user);
void startIntent(PendingIntent intent, Intent fillInIntent,
- @StageType int stage, @StagePosition int position,
+ @StageType int stage, @SplitPosition int position,
@Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
void exitSplitScreen();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index 60f7ee2..7e4010d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -521,12 +521,14 @@
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- mVelocityTracker.addMovement(event);
-
- if (!mMoving) break;
+ if (!mMoving) {
+ stopDragging();
+ break;
+ }
x = (int) event.getRawX();
y = (int) event.getRawY();
+ mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(1000);
int position = calculatePosition(x, y);
stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
@@ -685,9 +687,9 @@
mTmpRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), mHandle.getBottom());
if (isHorizontalDivision()) {
- mTmpRect.offsetTo(0, mDividerPositionY);
+ mTmpRect.offsetTo(mHandle.getLeft(), mDividerPositionY);
} else {
- mTmpRect.offsetTo(mDividerPositionX, 0);
+ mTmpRect.offsetTo(mDividerPositionX, mHandle.getTop());
}
mWindowManagerProxy.setTouchRegion(mTmpRect);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
index 9eacaec..ee2202a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
@@ -577,7 +577,7 @@
mSplits.getSplitTransitions().dismissSplit(
mSplits, mSplitLayout, !toPrimaryTask, snapped);
} else {
- mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, !toPrimaryTask);
+ mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, !toPrimaryTask);
onDismissSplit();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
index cf35656..86bf3ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
@@ -38,6 +38,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.transition.Transitions;
@@ -70,9 +71,9 @@
private final LegacySplitScreenTransitions mSplitTransitions;
LegacySplitScreenTaskListener(LegacySplitScreenController splitScreenController,
- ShellTaskOrganizer shellTaskOrganizer,
- Transitions transitions,
- SyncTransactionQueue syncQueue) {
+ ShellTaskOrganizer shellTaskOrganizer,
+ Transitions transitions,
+ SyncTransactionQueue syncQueue) {
mSplitScreenController = splitScreenController;
mTaskOrganizer = shellTaskOrganizer;
mSplitTransitions = new LegacySplitScreenTransitions(splitScreenController.mTransactionPool,
@@ -146,21 +147,11 @@
ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared Supported", TAG);
// Initialize dim surfaces:
- mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession)
- .setParent(mPrimarySurface).setColorLayer()
- .setName("Primary Divider Dim")
- .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
- .build();
- mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession)
- .setParent(mSecondarySurface).setColorLayer()
- .setName("Secondary Divider Dim")
- .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
- .build();
SurfaceControl.Transaction t = getTransaction();
- t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
- t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
- t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
- t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
+ mPrimaryDim = SurfaceUtils.makeDimLayer(
+ t, mPrimarySurface, "Primary Divider Dim", mSurfaceSession);
+ mSecondaryDim = SurfaceUtils.makeDimLayer(
+ t, mSecondarySurface, "Secondary Divider Dim", mSurfaceSession);
t.apply();
releaseTransaction(t);
}
@@ -203,6 +194,22 @@
return;
}
synchronized (this) {
+ if (!taskInfo.supportsMultiWindow) {
+ if (mSplitScreenController.isDividerVisible()) {
+ // Dismiss the split screen if the task no longer supports multi window.
+ if (taskInfo.taskId == mPrimary.taskId
+ || taskInfo.parentTaskId == mPrimary.taskId) {
+ // If the primary is focused, dismiss to primary.
+ mSplitScreenController
+ .startDismissSplit(taskInfo.isFocused /* toPrimaryTask */);
+ } else {
+ // If the secondary is not focused, dismiss to primary.
+ mSplitScreenController
+ .startDismissSplit(!taskInfo.isFocused /* toPrimaryTask */);
+ }
+ }
+ return;
+ }
if (taskInfo.hasParentTask()) {
// changed messages are noisy since it reports on every ensureVisibility. This
// conflicts with legacy app-transitions which "swaps" the position to a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index 1072845..e42e43b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -41,6 +41,7 @@
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.transition.Transitions;
@@ -214,8 +215,12 @@
if (!rootTask.supportsMultiWindow && rootTask.topActivityType != ACTIVITY_TYPE_HOME) {
continue;
}
- // Only move fullscreen tasks to split secondary.
- if (rootTask.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+ // Only move split controlling tasks to split secondary.
+ final int windowingMode = rootTask.getWindowingMode();
+ if (!ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, windowingMode)
+ || !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, rootTask.getActivityType())
+ // Excludes split screen secondary due to it's the root we're reparenting to.
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
continue;
}
// Since this iterates from bottom to top, update topHomeTask for every fullscreen task
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index 242f8f1..8dc05de9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -19,7 +19,6 @@
import android.content.res.Configuration;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
/**
* Interface to engage one handed feature.
@@ -60,11 +59,6 @@
void stopOneHanded(int uiEvent);
/**
- * Sets navigation 3 button mode enabled or disabled by users.
- */
- void setThreeButtonModeEnabled(boolean enabled);
-
- /**
* Sets one handed feature temporary locked in enabled or disabled state, this won't change
* settings configuration.
*
@@ -80,12 +74,6 @@
void registerTransitionCallback(OneHandedTransitionCallback callback);
/**
- * Registers callback for one handed gesture, this gesture callback will be activated on
- * 3 button navigation mode only
- */
- void registerGestureCallback(OneHandedGestureEventCallback callback);
-
- /**
* Receive onConfigurationChanged() events
*/
void onConfigChanged(Configuration newConfig);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index 180cceb..1ae2636 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.view.SurfaceControl;
@@ -113,20 +114,18 @@
/**
* Animator for OneHanded transition animation which supports both alpha and bounds animation.
- *
- * @param <T> Type of property to animate, either offset (float)
*/
// TODO: Refactoring to use SpringAnimation and DynamicAnimation instead of using ValueAnimator
// to implement One-Handed transition animation. (b/185129031)
- public abstract static class OneHandedTransitionAnimator<T> extends ValueAnimator implements
+ public abstract static class OneHandedTransitionAnimator extends ValueAnimator implements
ValueAnimator.AnimatorUpdateListener,
ValueAnimator.AnimatorListener {
private final SurfaceControl mLeash;
private final WindowContainerToken mToken;
- private T mStartValue;
- private T mEndValue;
- private T mCurrentValue;
+ private float mStartValue;
+ private float mEndValue;
+ private float mCurrentValue;
private final List<OneHandedAnimationCallback> mOneHandedAnimationCallbacks =
new ArrayList<>();
@@ -137,7 +136,7 @@
private @TransitionDirection int mTransitionDirection;
private OneHandedTransitionAnimator(WindowContainerToken token, SurfaceControl leash,
- T startValue, T endValue) {
+ float startValue, float endValue) {
mLeash = leash;
mToken = token;
mStartValue = startValue;
@@ -204,9 +203,11 @@
mSurfaceTransactionHelper = helper;
}
- OneHandedTransitionAnimator<T> addOneHandedAnimationCallback(
- OneHandedAnimationCallback callback) {
- mOneHandedAnimationCallbacks.add(callback);
+ OneHandedTransitionAnimator addOneHandedAnimationCallback(
+ @Nullable OneHandedAnimationCallback callback) {
+ if (callback != null) {
+ mOneHandedAnimationCallbacks.add(callback);
+ }
return this;
}
@@ -223,27 +224,27 @@
return mTransitionDirection;
}
- OneHandedTransitionAnimator<T> setTransitionDirection(int direction) {
+ OneHandedTransitionAnimator setTransitionDirection(int direction) {
mTransitionDirection = direction;
return this;
}
- T getStartValue() {
+ float getStartValue() {
return mStartValue;
}
- T getEndValue() {
+ float getEndValue() {
return mEndValue;
}
- void setCurrentValue(T value) {
+ void setCurrentValue(float value) {
mCurrentValue = value;
}
/**
* Updates the {@link #mEndValue}.
*/
- void updateEndValue(T endValue) {
+ void updateEndValue(float endValue) {
mEndValue = endValue;
}
@@ -252,10 +253,10 @@
}
@VisibleForTesting
- static OneHandedTransitionAnimator<Float> ofYOffset(WindowContainerToken token,
+ static OneHandedTransitionAnimator ofYOffset(WindowContainerToken token,
SurfaceControl leash, float startValue, float endValue, Rect displayBounds) {
- return new OneHandedTransitionAnimator<Float>(token, leash, startValue, endValue) {
+ return new OneHandedTransitionAnimator(token, leash, startValue, endValue) {
private final Rect mTmpRect = new Rect(displayBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index ce57638..49266ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -41,7 +41,6 @@
import android.provider.Settings;
import android.util.Slog;
import android.view.Surface;
-import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -59,7 +58,6 @@
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
import java.io.PrintWriter;
@@ -101,7 +99,6 @@
private final OneHandedImpl mImpl = new OneHandedImpl();
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
- private OneHandedGestureHandler mGestureHandler;
private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
/**
@@ -113,7 +110,6 @@
return;
}
mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct);
- mGestureHandler.onRotateDisplay(mDisplayAreaOrganizer.getDisplayLayout());
};
private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
@@ -196,7 +192,7 @@
private boolean isInitialized() {
if (mDisplayAreaOrganizer == null || mDisplayController == null
- || mGestureHandler == null || mOneHandedSettingsUtil == null) {
+ || mOneHandedSettingsUtil == null) {
Slog.w(TAG, "Components may not initialized yet!");
return false;
}
@@ -221,13 +217,11 @@
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
OneHandedState transitionState = new OneHandedState();
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
- displayLayout, windowManager, mainExecutor);
+ displayLayout, windowManager, settingsUtil, mainExecutor);
OneHandedAnimationController animationController =
new OneHandedAnimationController(context);
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
mainExecutor);
- OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler(
- context, displayLayout, ViewConfiguration.get(context), mainExecutor);
OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
new OneHandedBackgroundPanelOrganizer(context, displayLayout, mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
@@ -238,7 +232,7 @@
ServiceManager.getService(Context.OVERLAY_SERVICE));
return new OneHandedController(context, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
- gestureHandler, settingsUtil, accessibilityUtil, timeoutHandler, transitionState,
+ settingsUtil, accessibilityUtil, timeoutHandler, transitionState,
oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
mainHandler);
}
@@ -250,7 +244,6 @@
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
OneHandedTouchHandler touchHandler,
OneHandedTutorialHandler tutorialHandler,
- OneHandedGestureHandler gestureHandler,
OneHandedSettingsUtil settingsUtil,
OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
OneHandedTimeoutHandler timeoutHandler,
@@ -269,7 +262,6 @@
mTouchHandler = touchHandler;
mState = state;
mTutorialHandler = tutorialHandler;
- mGestureHandler = gestureHandler;
mOverlayManager = overlayManager;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
@@ -307,6 +299,8 @@
mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
+
+ mState.addSListeners(mTutorialHandler);
}
public OneHanded asOneHanded() {
@@ -399,24 +393,15 @@
mOneHandedUiEventLogger.writeEvent(uiEvent);
}
- private void setThreeButtonModeEnabled(boolean enabled) {
- mGestureHandler.onThreeButtonModeEnabled(enabled);
- }
-
@VisibleForTesting
void registerTransitionCallback(OneHandedTransitionCallback callback) {
mDisplayAreaOrganizer.registerTransitionCallback(callback);
}
- private void registerGestureCallback(OneHandedGestureEventCallback callback) {
- mGestureHandler.setGestureEventListener(callback);
- }
-
private void setupCallback() {
mTouchHandler.registerTouchEventListener(() ->
stopOneHanded(OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT));
mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler);
- mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer);
mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallBack);
@@ -465,7 +450,6 @@
private void updateDisplayLayout(int displayId) {
final DisplayLayout newDisplayLayout = mDisplayController.getDisplayLayout(displayId);
mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
- mGestureHandler.onDisplayChanged(newDisplayLayout);
mTutorialHandler.onDisplayChanged(newDisplayLayout);
}
@@ -588,7 +572,6 @@
}
mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
- mGestureHandler.onGestureEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled);
if (!mIsOneHandedEnabled) {
mDisplayAreaOrganizer.unregisterOrganizer();
@@ -643,19 +626,16 @@
return;
}
mLockedDisabled = locked && !enabled;
-
- // Disabled gesture when keyguard ON
- mGestureHandler.onGestureEnabled(!mLockedDisabled && isFeatureEnabled);
}
private void onConfigChanged(Configuration newConfig) {
- if (mTutorialHandler != null) {
- if (!mIsOneHandedEnabled
- || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
- return;
- }
- mTutorialHandler.onConfigurationChanged(newConfig);
+ if (mTutorialHandler == null) {
+ return;
}
+ if (!mIsOneHandedEnabled || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ return;
+ }
+ mTutorialHandler.onConfigurationChanged();
}
private void onUserSwitch(int newUserId) {
@@ -685,10 +665,6 @@
mDisplayAreaOrganizer.dump(pw);
}
- if (mGestureHandler != null) {
- mGestureHandler.dump(pw);
- }
-
if (mTouchHandler != null) {
mTouchHandler.dump(pw);
}
@@ -775,13 +751,6 @@
}
@Override
- public void setThreeButtonModeEnabled(boolean enabled) {
- mMainExecutor.execute(() -> {
- OneHandedController.this.setThreeButtonModeEnabled(enabled);
- });
- }
-
- @Override
public void setLockedDisabled(boolean locked, boolean enabled) {
mMainExecutor.execute(() -> {
OneHandedController.this.setLockedDisabled(locked, enabled);
@@ -796,13 +765,6 @@
}
@Override
- public void registerGestureCallback(OneHandedGestureEventCallback callback) {
- mMainExecutor.execute(() -> {
- OneHandedController.this.registerGestureCallback(callback);
- });
- }
-
- @Override
public void onConfigChanged(Configuration newConfig) {
mMainExecutor.execute(() -> {
OneHandedController.this.onConfigChanged(newConfig);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
deleted file mode 100644
index 0383229..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
+++ /dev/null
@@ -1,308 +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.wm.shell.onehanded;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.hardware.input.InputManager;
-import android.os.Looper;
-import android.view.Display;
-import android.view.InputChannel;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.InputMonitor;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.ViewConfiguration;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.ShellExecutor;
-
-import java.io.PrintWriter;
-
-/**
- * The class manage swipe up and down gesture for 3-Button mode navigation, others(e.g, 2-button,
- * full gesture mode) are handled by Launcher quick steps. TODO(b/160934654) Migrate to Launcher
- * quick steps
- */
-public class OneHandedGestureHandler implements OneHandedTransitionCallback {
- private static final String TAG = "OneHandedGestureHandler";
-
- private static final int ANGLE_MAX = 150;
- private static final int ANGLE_MIN = 30;
- private final float mDragDistThreshold;
- private final float mSquaredSlop;
- private final PointF mDownPos = new PointF();
- private final PointF mLastPos = new PointF();
- private final PointF mStartDragPos = new PointF();
-
- private boolean mPassedSlop;
- private boolean mAllowGesture;
- private boolean mIsEnabled;
- private int mNavGestureHeight;
- private boolean mIsThreeButtonModeEnabled;
- private int mRotation = Surface.ROTATION_0;
-
- @VisibleForTesting
- InputMonitor mInputMonitor;
- @VisibleForTesting
- InputEventReceiver mInputEventReceiver;
- private final ShellExecutor mMainExecutor;
- @VisibleForTesting
- @Nullable
- OneHandedGestureEventCallback mGestureEventCallback;
- private Rect mGestureRegion = new Rect();
- private boolean mIsStopGesture;
-
- /**
- * Constructor of OneHandedGestureHandler, we only handle the gesture of {@link
- * Display#DEFAULT_DISPLAY}
- *
- * @param context Any context
- * @param displayLayout Current {@link DisplayLayout} from controller
- * @param viewConfig {@link ViewConfiguration} to obtain touch slop
- * @param mainExecutor The wm-shell main executor
- */
- public OneHandedGestureHandler(Context context,
- DisplayLayout displayLayout,
- ViewConfiguration viewConfig,
- ShellExecutor mainExecutor) {
- mMainExecutor = mainExecutor;
- mDragDistThreshold = context.getResources().getDimensionPixelSize(
- R.dimen.gestures_onehanded_drag_threshold);
-
- final float slop = viewConfig.getScaledTouchSlop();
- mSquaredSlop = slop * slop;
- onDisplayChanged(displayLayout);
- updateIsEnabled();
- }
-
- /**
- * Notifies by {@link OneHandedController}, when swipe down gesture is enabled on 3 button
- * navigation bar mode.
- *
- * @param isEnabled Either one handed mode or swipe for notification function enabled or not
- */
- public void onGestureEnabled(boolean isEnabled) {
- mIsEnabled = isEnabled;
- updateIsEnabled();
- }
-
- void onThreeButtonModeEnabled(boolean isEnabled) {
- mIsThreeButtonModeEnabled = isEnabled;
- updateIsEnabled();
- }
-
- /**
- * Registers {@link OneHandedGestureEventCallback} to receive onStart(), onStop() callback
- */
- public void setGestureEventListener(OneHandedGestureEventCallback callback) {
- mGestureEventCallback = callback;
- }
-
- /**
- * Called when onDisplayAdded() or onDisplayRemoved() callback
- * @param displayLayout The latest {@link DisplayLayout} representing current displayId
- */
- public void onDisplayChanged(DisplayLayout displayLayout) {
- mNavGestureHeight = getNavBarSize(displayLayout);
- mGestureRegion.set(0, displayLayout.height() - mNavGestureHeight, displayLayout.width(),
- displayLayout.height());
- mRotation = displayLayout.rotation();
- }
-
- private void onMotionEvent(MotionEvent ev) {
- int action = ev.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN) {
- mAllowGesture = isWithinTouchRegion(ev.getX(), ev.getY()) && isGestureAvailable();
- if (mAllowGesture) {
- mDownPos.set(ev.getX(), ev.getY());
- mLastPos.set(mDownPos);
- }
- } else if (mAllowGesture) {
- switch (action) {
- case MotionEvent.ACTION_MOVE:
- mLastPos.set(ev.getX(), ev.getY());
- if (!mPassedSlop) {
- if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
- > mSquaredSlop) {
- mStartDragPos.set(mLastPos.x, mLastPos.y);
- if (isValidStartAngle(
- mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)
- || isValidExitAngle(
- mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)) {
- mPassedSlop = true;
- mInputMonitor.pilferPointers();
- }
- }
- } else {
- float distance = (float) Math.hypot(mLastPos.x - mDownPos.x,
- mLastPos.y - mDownPos.y);
- if (distance > mDragDistThreshold) {
- mIsStopGesture = true;
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mLastPos.y >= mDownPos.y && mPassedSlop) {
- mGestureEventCallback.onStart();
- } else if (mIsStopGesture) {
- mGestureEventCallback.onStop();
- }
- clearState();
- break;
- case MotionEvent.ACTION_CANCEL:
- clearState();
- break;
- default:
- break;
- }
- }
- }
-
- private void clearState() {
- mPassedSlop = false;
- mIsStopGesture = false;
- }
-
- private void disposeInputChannel() {
- if (mInputEventReceiver != null) {
- mInputEventReceiver.dispose();
- mInputEventReceiver = null;
- }
-
- if (mInputMonitor != null) {
- mInputMonitor.dispose();
- mInputMonitor = null;
- }
- }
-
- private boolean isWithinTouchRegion(float x, float y) {
- return mGestureRegion.contains(Math.round(x), Math.round(y));
- }
-
- private int getNavBarSize(@NonNull DisplayLayout displayLayout) {
- return isGestureAvailable() ? displayLayout.navBarFrameHeight() : 0 /* In landscape */;
- }
-
- private void updateIsEnabled() {
- disposeInputChannel();
-
- if (mIsEnabled && mIsThreeButtonModeEnabled && isGestureAvailable()) {
- mInputMonitor = InputManager.getInstance().monitorGestureInput(
- "onehanded-gesture-offset", DEFAULT_DISPLAY);
- try {
- mMainExecutor.executeBlocking(() -> {
- mInputEventReceiver = new EventReceiver(
- mInputMonitor.getInputChannel(), Looper.myLooper());
- });
- } catch (InterruptedException e) {
- throw new RuntimeException("Failed to create input event receiver", e);
- }
- }
- }
-
- private void onInputEvent(InputEvent ev) {
- if (ev instanceof MotionEvent) {
- onMotionEvent((MotionEvent) ev);
- }
- }
-
- /**
- * Handler for display rotation changes by {@link DisplayLayout}
- *
- * @param displayLayout The rotated displayLayout
- */
- public void onRotateDisplay(DisplayLayout displayLayout) {
- mRotation = displayLayout.rotation();
- mNavGestureHeight = getNavBarSize(displayLayout);
- mGestureRegion.set(0, displayLayout.height() - mNavGestureHeight, displayLayout.width(),
- displayLayout.height());
- updateIsEnabled();
- }
-
- // TODO: Use BatchedInputEventReceiver
- private class EventReceiver extends InputEventReceiver {
- EventReceiver(InputChannel channel, Looper looper) {
- super(channel, looper);
- }
-
- public void onInputEvent(InputEvent event) {
- OneHandedGestureHandler.this.onInputEvent(event);
- finishInputEvent(event, true);
- }
- }
-
- private boolean isGestureAvailable() {
- // Either OHM or swipe notification shade can activate in portrait mode only
- return mRotation == Surface.ROTATION_0 || mRotation == Surface.ROTATION_180;
- }
-
- private boolean isValidStartAngle(float deltaX, float deltaY) {
- final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
- return angle > -(ANGLE_MAX) && angle < -(ANGLE_MIN);
- }
-
- private boolean isValidExitAngle(float deltaX, float deltaY) {
- final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
- return angle > ANGLE_MIN && angle < ANGLE_MAX;
- }
-
- private float squaredHypot(float x, float y) {
- return x * x + y * y;
- }
-
- void dump(@NonNull PrintWriter pw) {
- final String innerPrefix = " ";
- pw.println(TAG);
- pw.print(innerPrefix + "mAllowGesture=");
- pw.println(mAllowGesture);
- pw.print(innerPrefix + "mIsEnabled=");
- pw.println(mIsEnabled);
- pw.print(innerPrefix + "mGestureRegion=");
- pw.println(mGestureRegion);
- pw.print(innerPrefix + "mNavGestureHeight=");
- pw.println(mNavGestureHeight);
- pw.print(innerPrefix + "mIsThreeButtonModeEnabled=");
- pw.println(mIsThreeButtonModeEnabled);
- pw.print(innerPrefix + "mRotation=");
- pw.println(mRotation);
- }
-
- /**
- * The touch(gesture) events to notify {@link OneHandedController} start or stop one handed
- */
- public interface OneHandedGestureEventCallback {
- /**
- * Handles the start gesture.
- */
- void onStart();
-
- /**
- * Handles the exit gesture.
- */
- void onStop();
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index bb68224..90fc823 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -29,7 +29,7 @@
import java.lang.annotation.RetentionPolicy;
/**
- * APIs for querying or updating one handed settings .
+ * APIs for querying or updating one handed settings.
*/
public final class OneHandedSettingsUtil {
private static final String TAG = "OneHandedSettingsUtil";
@@ -62,7 +62,7 @@
public static final int ONE_HANDED_TIMEOUT_LONG_IN_SECONDS = 12;
/**
- * Register one handed preference settings observer
+ * Registers one handed preference settings observer
*
* @param key Setting key to monitor in observer
* @param resolver ContentResolver of context
@@ -82,7 +82,7 @@
}
/**
- * Unregister one handed preference settings observer
+ * Unregisters one handed preference settings observer.
*
* @param resolver ContentResolver of context
* @param observer preference key change observer
@@ -95,7 +95,7 @@
}
/**
- * Query one handed enable or disable flag from Settings provider.
+ * Queries one handed enable or disable flag from Settings provider.
*
* @return enable or disable one handed mode flag.
*/
@@ -105,7 +105,7 @@
}
/**
- * Query taps app to exit config from Settings provider.
+ * Queries taps app to exit config from Settings provider.
*
* @return enable or disable taps app exit.
*/
@@ -115,7 +115,7 @@
}
/**
- * Query timeout value from Settings provider. Default is
+ * Queries timeout value from Settings provider. Default is.
* {@link OneHandedSettingsUtil#ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS}
*
* @return timeout value in seconds.
@@ -135,10 +135,31 @@
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0, userId) == 1;
}
+
/**
- * Sets one handed activated or not to notify state for shortcut
+ * Queries tutorial shown counts from Settings provider. Default is 0.
*
- * @return activated or not
+ * @return counts tutorial shown counts.
+ */
+ public int getTutorialShownCounts(ContentResolver resolver, int userId) {
+ return Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0, userId);
+ }
+
+ /**
+ * Sets tutorial shown counts.
+ *
+ * @return true if the value was set, false on database errors.
+ */
+ public boolean setTutorialShownCounts(ContentResolver resolver, int shownCounts, int userId) {
+ return Settings.Secure.putIntForUser(resolver,
+ Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, shownCounts, userId);
+ }
+
+ /**
+ * Sets one handed activated or not to notify state for shortcut.
+ *
+ * @return true if one handed mode is activated.
*/
public boolean getOneHandedModeActivated(ContentResolver resolver, int userId) {
return Settings.Secure.getIntForUser(resolver,
@@ -146,9 +167,9 @@
}
/**
- * Sets one handed activated or not to notify state for shortcut
+ * Sets one handed activated or not to notify state for shortcut.
*
- * @return activated or not
+ * @return true if the value was set, false on database errors.
*/
public boolean setOneHandedModeActivated(ContentResolver resolver, int state, int userId) {
return Settings.Secure.putIntForUser(resolver,
@@ -167,6 +188,8 @@
pw.println(getSettingsTapsAppToExit(resolver, userId));
pw.print(innerPrefix + "shortcutActivated=");
pw.println(getOneHandedModeActivated(resolver, userId));
+ pw.print(innerPrefix + "tutorialShownCounts=");
+ pw.println(getTutorialShownCounts(resolver, userId));
}
public OneHandedSettingsUtil() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedState.java
index facc4bd..47bb99d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedState.java
@@ -21,6 +21,8 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
Represents current OHM state by following steps, a generic CUJ is
@@ -28,13 +30,13 @@
*/
public class OneHandedState {
/** DEFAULT STATE after OHM feature initialized. */
- public static final int STATE_NONE = 0x00000000;
+ public static final int STATE_NONE = 0;
/** The state flag set when user trigger OHM. */
- public static final int STATE_ENTERING = 0x00000001;
+ public static final int STATE_ENTERING = 1;
/** The state flag set when transitioning */
- public static final int STATE_ACTIVE = 0x00000002;
+ public static final int STATE_ACTIVE = 2;
/** The state flag set when user stop OHM feature. */
- public static final int STATE_EXITING = 0x00000004;
+ public static final int STATE_EXITING = 3;
@IntDef(prefix = { "STATE_" }, value = {
STATE_NONE,
@@ -54,9 +56,18 @@
private static final String TAG = OneHandedState.class.getSimpleName();
+ private List<OnStateChangedListener> mStateChangeListeners = new ArrayList<>();
+
+ /**
+ * Adds listener to be called back when one handed state changed.
+ * @param listener the listener to be called back
+ */
+ public void addSListeners(OnStateChangedListener listener) {
+ mStateChangeListeners.add(listener);
+ }
+
/**
* Gets current transition state of One handed mode.
- *
* @return The bitwise flags representing current states.
*/
public @State int getState() {
@@ -85,6 +96,9 @@
*/
public void setState(@State int newState) {
sCurrentState = newState;
+ if (!mStateChangeListeners.isEmpty()) {
+ mStateChangeListeners.forEach((listener) -> listener.onStateChanged(newState));
+ }
}
/** Dumps internal state. */
@@ -93,4 +107,14 @@
pw.println(TAG);
pw.println(innerPrefix + "sCurrentState=" + sCurrentState);
}
+
+ /**
+ * Gets notified when one handed state changed
+ *
+ * @see OneHandedState
+ */
+ public interface OnStateChangedListener {
+ /** Called when one handed state changed */
+ default void onStateChanged(@State int newState) {}
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
index 0f9b320..5b9f0c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
@@ -37,7 +37,6 @@
/**
* Manages all the touch handling for One Handed on the Phone, including user tap outside region
* to exit, reset timer when user is in one-handed mode.
- * Refer {@link OneHandedGestureHandler} to see start and stop one handed gesture
*/
public class OneHandedTouchHandler implements OneHandedTransitionCallback {
private static final String TAG = "OneHandedTouchHandler";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index e8cee8a..6cee404 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -16,13 +16,19 @@
package com.android.wm.shell.onehanded;
+import static android.os.UserHandle.myUserId;
+
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
+
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemProperties;
-import android.provider.Settings;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -32,6 +38,7 @@
import androidx.annotation.NonNull;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -39,42 +46,33 @@
import java.io.PrintWriter;
/**
- * Manages the user tutorial handling for One Handed operations, including animations synchronized
- * with one-handed translation.
- * Refer {@link OneHandedGestureHandler} and {@link OneHandedTouchHandler} to see start and stop
- * one handed gesture
+ * Handles tutorial visibility and synchronized transition for One Handed operations,
+ * TargetViewContainer only be created and attach to window when
+ * shown counts < {@link MAX_TUTORIAL_SHOW_COUNT}, and detach TargetViewContainer from window
+ * after exiting one handed mode.
*/
-public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
+public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
+ OneHandedState.OnStateChangedListener {
private static final String TAG = "OneHandedTutorialHandler";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
"persist.debug.one_handed_offset_percentage";
private static final int MAX_TUTORIAL_SHOW_COUNT = 2;
- private final WindowManager mWindowManager;
- private final String mPackageName;
+
private final float mTutorialHeightRatio;
+ private final WindowManager mWindowManager;
+ private final OneHandedSettingsUtil mSettingsUtil;
+ private final ShellExecutor mShellExecutor;
+
+ private boolean mCanShow;
+ private @OneHandedState.State int mCurrentState;
+ private int mShownCounts;
+ private int mTutorialAreaHeight;
private Context mContext;
- private Rect mDisplayBounds;
- private View mTutorialView;
private ContentResolver mContentResolver;
- private boolean mCanShowTutorial;
- private boolean mIsOneHandedMode;
-
- private enum ONE_HANDED_TRIGGER_STATE {
- UNSET, ENTERING, EXITING
- }
- /**
- * Current One-Handed trigger state.
- * Note: This is a dynamic state, whenever last state has been confirmed
- * (i.e. onStartFinished() or onStopFinished()), the state should be set "UNSET" at final.
- */
- private ONE_HANDED_TRIGGER_STATE mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
-
- /**
- * Container of the tutorial panel showing at outside region when one handed starting
- */
- private ViewGroup mTargetViewContainer;
- private int mTutorialAreaHeight;
+ private Rect mDisplayBounds;
+ private @Nullable View mTutorialView;
+ private @Nullable ViewGroup mTargetViewContainer;
private final OneHandedAnimationCallback mAnimationCallback = new OneHandedAnimationCallback() {
@Override
@@ -82,63 +80,51 @@
if (!canShowTutorial()) {
return;
}
- mTargetViewContainer.setVisibility(View.VISIBLE);
mTargetViewContainer.setTransitionGroup(true);
mTargetViewContainer.setTranslationY(yPos - mTargetViewContainer.getHeight());
}
-
- @Override
- public void onOneHandedAnimationStart(
- OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- final float startValue = (float) animator.getStartValue();
- if (mTriggerState == ONE_HANDED_TRIGGER_STATE.UNSET) {
- mTriggerState = (startValue == 0f)
- ? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING;
- if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) {
- attachTurtorialTarget();
- }
- }
- }
};
public OneHandedTutorialHandler(Context context, DisplayLayout displayLayout,
- WindowManager windowManager, ShellExecutor mainExecutor) {
+ WindowManager windowManager, OneHandedSettingsUtil settingsUtil,
+ ShellExecutor mainExecutor) {
mContext = context;
- mWindowManager = windowManager;
- mPackageName = context.getPackageName();
mContentResolver = context.getContentResolver();
+ mWindowManager = windowManager;
+ mSettingsUtil = settingsUtil;
+ mShellExecutor = mainExecutor;
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
final int sysPropPercentageConfig = SystemProperties.getInt(
ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
mTutorialHeightRatio = sysPropPercentageConfig / 100.0f;
- onDisplayChanged(displayLayout);
- mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
- ? false : true;
- mIsOneHandedMode = false;
-
- mainExecutor.execute(() -> {
- recreateTutorialView(mContext);
- });
+ mShownCounts = mSettingsUtil.getTutorialShownCounts(mContentResolver, myUserId());
}
@Override
- public void onStartFinished(Rect bounds) {
- updateFinished(View.VISIBLE, 0f);
- updateTutorialCount();
- mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
- }
-
- @Override
- public void onStopFinished(Rect bounds) {
- updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight());
- removeTutorialFromWindowManager();
- mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
+ public void onStateChanged(int newState) {
+ mCurrentState = newState;
+ if (!canShowTutorial()) {
+ return;
+ }
+ switch (newState) {
+ case STATE_ENTERING:
+ createViewAndAttachToWindow(mContext);
+ break;
+ case STATE_ACTIVE:
+ case STATE_EXITING:
+ // no - op
+ break;
+ case STATE_NONE:
+ removeTutorialFromWindowManager(true /* increment */);
+ break;
+ default:
+ break;
+ }
}
/**
- * Called when onDisplayAdded() or onDisplayRemoved() callback
+ * Called when onDisplayAdded() or onDisplayRemoved() callback.
* @param displayLayout The latest {@link DisplayLayout} representing current displayId
*/
public void onDisplayChanged(DisplayLayout displayLayout) {
@@ -151,38 +137,32 @@
mTutorialAreaHeight = Math.round(mDisplayBounds.height() * mTutorialHeightRatio);
}
- private void recreateTutorialView(Context context) {
- mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial,
- null);
- mTargetViewContainer = new FrameLayout(context);
- mTargetViewContainer.setClipChildren(false);
- mTargetViewContainer.addView(mTutorialView);
- mTargetViewContainer.setVisibility(mIsOneHandedMode ? View.VISIBLE : View.GONE);
- }
-
- private void updateFinished(int visible, float finalPosition) {
+ @VisibleForTesting
+ void createViewAndAttachToWindow(Context context) {
if (!canShowTutorial()) {
return;
}
- mIsOneHandedMode = (finalPosition == 0f) ? true : false;
- mTargetViewContainer.setVisibility(visible);
- mTargetViewContainer.setTranslationY(finalPosition);
+ mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
+ mTargetViewContainer = new FrameLayout(context);
+ mTargetViewContainer.setClipChildren(false);
+ mTargetViewContainer.addView(mTutorialView);
+
+ attachTargetToWindow();
}
- private void updateTutorialCount() {
- int showCount = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0);
- showCount = Math.min(MAX_TUTORIAL_SHOW_COUNT, showCount + 1);
- mCanShowTutorial = showCount < MAX_TUTORIAL_SHOW_COUNT;
- Settings.Secure.putInt(mContentResolver,
- Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, showCount);
+ @VisibleForTesting
+ boolean setTutorialShownCountIncrement() {
+ if (!canShowTutorial()) {
+ return false;
+ }
+ mShownCounts += 1;
+ return mSettingsUtil.setTutorialShownCounts(mContentResolver, mShownCounts, myUserId());
}
/**
- * Adds the tutorial target view to the WindowManager and update its layout, so it's ready
- * to be animated in.
+ * Adds the tutorial target view to the WindowManager and update its layout.
*/
- private void attachTurtorialTarget() {
+ private void attachTargetToWindow() {
if (!mTargetViewContainer.isAttachedToWindow()) {
try {
mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
@@ -195,14 +175,18 @@
}
}
- private void removeTutorialFromWindowManager() {
- if (mTargetViewContainer.isAttachedToWindow()) {
+ @VisibleForTesting
+ void removeTutorialFromWindowManager(boolean increment) {
+ if (mTargetViewContainer != null && mTargetViewContainer.isAttachedToWindow()) {
mWindowManager.removeViewImmediate(mTargetViewContainer);
+ if (increment) {
+ setTutorialShownCountIncrement();
+ }
}
}
- OneHandedAnimationCallback getAnimationCallback() {
- return mAnimationCallback;
+ @Nullable OneHandedAnimationCallback getAnimationCallback() {
+ return canShowTutorial() ? mAnimationCallback : null /* Disabled */;
}
/**
@@ -222,38 +206,36 @@
return lp;
}
- void dump(@NonNull PrintWriter pw) {
- final String innerPrefix = " ";
- pw.println(TAG);
- pw.print(innerPrefix + "mTriggerState=");
- pw.println(mTriggerState);
- pw.print(innerPrefix + "mDisplayBounds=");
- pw.println(mDisplayBounds);
- pw.print(innerPrefix + "mTutorialAreaHeight=");
- pw.println(mTutorialAreaHeight);
- }
-
- private boolean canShowTutorial() {
- if (!mCanShowTutorial) {
- // Since canSHowTutorial() will be called in onAnimationUpdate() and we still need to
- // hide Tutorial text in the period of continuously onAnimationUpdate() API call,
- // so we have to hide mTargetViewContainer here.
- mTargetViewContainer.setVisibility(View.GONE);
- return false;
- }
- return true;
+ @VisibleForTesting
+ boolean canShowTutorial() {
+ return mCanShow = mShownCounts < MAX_TUTORIAL_SHOW_COUNT;
}
/**
* onConfigurationChanged events for updating tutorial text.
- * @param newConfig
*/
- public void onConfigurationChanged(Configuration newConfig) {
- if (!mCanShowTutorial) {
+ public void onConfigurationChanged() {
+ if (!canShowTutorial()) {
return;
}
- removeTutorialFromWindowManager();
- recreateTutorialView(mContext.createConfigurationContext(newConfig));
- attachTurtorialTarget();
+ removeTutorialFromWindowManager(false /* increment */);
+ if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
+ createViewAndAttachToWindow(mContext);
+ }
+ }
+
+ void dump(@NonNull PrintWriter pw) {
+ final String innerPrefix = " ";
+ pw.println(TAG);
+ pw.print(innerPrefix + "mCanShow=");
+ pw.println(mCanShow);
+ pw.print(innerPrefix + "mCurrentState=");
+ pw.println(mCurrentState);
+ pw.print(innerPrefix + "mDisplayBounds=");
+ pw.println(mDisplayBounds);
+ pw.print(innerPrefix + "mShownCounts=");
+ pw.println(mShownCounts);
+ pw.print(innerPrefix + "mTutorialAreaHeight=");
+ pw.println(mTutorialAreaHeight);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
index a6ffa6e..ddc85f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
@@ -17,6 +17,7 @@
package com.android.wm.shell.pip;
import android.app.PictureInPictureParams;
+import android.view.SurfaceControl;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
@@ -48,8 +49,10 @@
*
* @param componentName ComponentName represents the Activity
* @param destinationBounds the destination bounds the PiP window lands into
+ * @param overlay an optional overlay to fade out after entering PiP
*/
- oneway void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 2;
+ oneway void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds,
+ in SurfaceControl overlay) = 2;
/**
* Sets listener to get pinned stack animation callbacks.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index ca05ff4..17e0d1b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -208,6 +208,24 @@
}
/**
+ * A handler class that could register itself to apply the transaction instead of the
+ * animation controller doing it. For example, the menu controller can be one such handler.
+ */
+ public static class PipTransactionHandler {
+
+ /**
+ * Called when the animation controller is about to apply a transaction. Allow a registered
+ * handler to apply the transaction instead.
+ *
+ * @return true if handled by the handler, false otherwise.
+ */
+ public boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
+ Rect destinationBounds) {
+ return false;
+ }
+ }
+
+ /**
* Animator for PiP transition animation which supports both alpha and bounds animation.
* @param <T> Type of property to animate, either alpha (float) or bounds (Rect)
*/
@@ -225,6 +243,7 @@
private T mEndValue;
private float mStartingAngle;
private PipAnimationCallback mPipAnimationCallback;
+ private PipTransactionHandler mPipTransactionHandler;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
@@ -293,6 +312,20 @@
mPipAnimationCallback = callback;
return this;
}
+
+ PipTransitionAnimator<T> setPipTransactionHandler(PipTransactionHandler handler) {
+ mPipTransactionHandler = handler;
+ return this;
+ }
+
+ boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
+ Rect destinationBounds) {
+ if (mPipTransactionHandler != null) {
+ return mPipTransactionHandler.handlePipTransaction(leash, tx, destinationBounds);
+ }
+ return false;
+ }
+
@VisibleForTesting
@TransitionDirection public int getTransitionDirection() {
return mTransitionDirection;
@@ -499,7 +532,9 @@
getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
initialSourceValue, bounds, insets);
}
- tx.apply();
+ if (!handlePipTransaction(leash, tx, bounds)) {
+ tx.apply();
+ }
}
private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index 7b834b2..046c320 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -367,7 +367,15 @@
* the default stack bounds when first entering PiP.
*/
public float getSnapFraction(Rect stackBounds) {
- return mSnapAlgorithm.getSnapFraction(stackBounds, getMovementBounds(stackBounds));
+ return getSnapFraction(stackBounds, getMovementBounds(stackBounds));
+ }
+
+ /**
+ * @return the default snap fraction to apply instead of the default gravity when calculating
+ * the default stack bounds when first entering PiP.
+ */
+ public float getSnapFraction(Rect stackBounds, Rect movementBounds) {
+ return mSnapAlgorithm.getSnapFraction(stackBounds, movementBounds);
}
/**
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
index 561dff0..e3674dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -55,7 +55,7 @@
STASH_TYPE_RIGHT
})
@Retention(RetentionPolicy.SOURCE)
- @interface StashType {}
+ public @interface StashType {}
private static final String TAG = PipBoundsState.class.getSimpleName();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 319d57a..48a15d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -20,7 +20,6 @@
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.os.SystemProperties;
import android.view.SurfaceControl;
import com.android.wm.shell.R;
@@ -44,10 +43,7 @@
* @param context the current context
*/
public void onDensityOrFontScaleChanged(Context context) {
- final boolean enableCornerRadius =
- SystemProperties.getBoolean("debug.sf.enable_hole_punch_pip", false);
- mCornerRadius = enableCornerRadius
- ? context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius) : 0;
+ mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 4ce6c9e..c0708c2 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
@@ -70,6 +70,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread;
@@ -147,6 +148,7 @@
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final int mEnterAnimationDuration;
private final int mExitAnimationDuration;
+ private final int mCrossFadeAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<LegacySplitScreenController> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
@@ -200,6 +202,19 @@
}
};
+ private final PipAnimationController.PipTransactionHandler mPipTransactionHandler =
+ new PipAnimationController.PipTransactionHandler() {
+ @Override
+ public boolean handlePipTransaction(SurfaceControl leash,
+ SurfaceControl.Transaction tx, Rect destinationBounds) {
+ if (mPipMenuController.isMenuVisible()) {
+ mPipMenuController.movePipMenu(leash, tx, destinationBounds);
+ return true;
+ }
+ return false;
+ }
+ };
+
private ActivityManager.RunningTaskInfo mTaskInfo;
// To handle the edge case that onTaskInfoChanged callback is received during the entering
// PiP transition, where we do not want to intercept the transition but still want to apply the
@@ -244,6 +259,12 @@
*/
private boolean mInSwipePipToHomeTransition;
+ /**
+ * An optional overlay used to mask content changing between an app in/out of PiP, only set if
+ * {@link #mInSwipePipToHomeTransition} is true.
+ */
+ private SurfaceControl mSwipePipToHomeOverlay;
+
public PipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue,
@NonNull PipBoundsState pipBoundsState,
@@ -267,6 +288,8 @@
.getInteger(R.integer.config_pipEnterAnimationDuration);
mExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipExitAnimationDuration);
+ mCrossFadeAnimationDuration = context.getResources()
+ .getInteger(R.integer.config_pipCrossfadeAnimationDuration);
mSurfaceTransactionHelper = surfaceTransactionHelper;
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
@@ -337,10 +360,12 @@
* Callback when launcher finishes swipe-pip-to-home operation.
* Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
*/
- public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+ public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds,
+ SurfaceControl overlay) {
// do nothing if there is no startSwipePipToHome being called before
if (mInSwipePipToHomeTransition) {
mPipBoundsState.setBounds(destinationBounds);
+ mSwipePipToHomeOverlay = overlay;
}
}
@@ -433,8 +458,10 @@
// removePipImmediately is expected when the following animation finishes.
ValueAnimator animator = mPipAnimationController
- .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), 1f, 0f)
+ .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(),
+ 1f /* alphaStart */, 0f /* alphaEnd */)
.setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
+ .setPipTransactionHandler(mPipTransactionHandler)
.setPipAnimationCallback(mPipAnimationCallback);
animator.setDuration(mExitAnimationDuration);
animator.setInterpolator(Interpolators.ALPHA_OUT);
@@ -573,6 +600,7 @@
.getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f)
.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
+ .setPipTransactionHandler(mPipTransactionHandler)
.setDuration(durationMs)
.start();
// mState is set right after the animation is kicked off to block any resize
@@ -583,6 +611,7 @@
private void onEndOfSwipePipToHomeTransition() {
final Rect destinationBounds = mPipBoundsState.getBounds();
+ final SurfaceControl swipeToHomeOverlay = mSwipePipToHomeOverlay;
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper.resetScale(tx, mLeash, destinationBounds);
mSurfaceTransactionHelper.crop(tx, mLeash, destinationBounds);
@@ -591,8 +620,14 @@
// Ensure menu's settled in its final bounds first.
finishResizeForMenu(destinationBounds);
sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+
+ // Remove the swipe to home overlay
+ if (swipeToHomeOverlay != null) {
+ fadeOutAndRemoveOverlay(swipeToHomeOverlay);
+ }
}, tx);
mInSwipePipToHomeTransition = false;
+ mSwipePipToHomeOverlay = null;
}
private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable,
@@ -749,6 +784,7 @@
mPipAnimationController
.getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), alphaStart, alphaEnd)
.setTransitionDirection(TRANSITION_DIRECTION_SAME)
+ .setPipTransactionHandler(mPipTransactionHandler)
.setDuration(show ? mEnterAnimationDuration : mExitAnimationDuration)
.start();
mHasFadeOut = !show;
@@ -1084,6 +1120,7 @@
private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
@PipAnimationController.AnimationType int type) {
+ final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
@@ -1107,41 +1144,26 @@
&& mPictureInPictureParams != null
&& !mPictureInPictureParams.isSeamlessResizeEnabled();
if (animateCrossFadeResize) {
- // Take a snapshot of the PIP task and hide it. We'll show it and fade it out after
- // the wct transaction is applied and the activity is laid out again.
- final SurfaceControl snapshotSurface = mTaskOrganizer.takeScreenshot(mToken);
- mSurfaceTransactionHelper.reparentAndShowSurfaceSnapshot(
- mSurfaceControlTransactionFactory.getTransaction(), mLeash, snapshotSurface);
- mSyncTransactionQueue.queue(wct);
- mSyncTransactionQueue.runInSync(t -> {
- // Scale the snapshot from its pre-resize bounds to the post-resize bounds.
- final Rect snapshotSrc = new Rect(0, 0, snapshotSurface.getWidth(),
- snapshotSurface.getHeight());
- final Rect snapshotDest = new Rect(0, 0, destinationBounds.width(),
- destinationBounds.height());
- mSurfaceTransactionHelper.scale(t, snapshotSurface, snapshotSrc, snapshotDest);
+ // Take a snapshot of the PIP task and show it. We'll fade it out after the wct
+ // transaction is applied and the activity is laid out again.
+ preResizeBounds.offsetTo(0, 0);
+ final Rect snapshotDest = new Rect(0, 0, destinationBounds.width(),
+ destinationBounds.height());
+ final SurfaceControl snapshotSurface = ScreenshotUtils.takeScreenshot(
+ mSurfaceControlTransactionFactory.getTransaction(), mLeash, preResizeBounds);
+ if (snapshotSurface != null) {
+ mSyncTransactionQueue.queue(wct);
+ mSyncTransactionQueue.runInSync(t -> {
+ // Scale the snapshot from its pre-resize bounds to the post-resize bounds.
+ mSurfaceTransactionHelper.scale(t, snapshotSurface, preResizeBounds,
+ snapshotDest);
- // Start animation to fade out the snapshot.
- final ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 0.0f);
- animator.setDuration(mEnterAnimationDuration);
- animator.addUpdateListener(animation -> {
- final float alpha = (float) animation.getAnimatedValue();
- final SurfaceControl.Transaction transaction =
- mSurfaceControlTransactionFactory.getTransaction();
- transaction.setAlpha(snapshotSurface, alpha);
- transaction.apply();
+ // Start animation to fade out the snapshot.
+ fadeOutAndRemoveOverlay(snapshotSurface);
});
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
- tx.remove(snapshotSurface);
- tx.apply();
- }
- });
- animator.start();
- });
+ } else {
+ applyFinishBoundsResize(wct, direction);
+ }
} else {
applyFinishBoundsResize(wct, direction);
}
@@ -1226,6 +1248,7 @@
sourceHintRect, direction, startingAngle, rotationDelta);
animator.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
+ .setPipTransactionHandler(mPipTransactionHandler)
.setDuration(durationMs)
.start();
if (rotationDelta != Surface.ROTATION_0 && direction == TRANSITION_DIRECTION_TO_PIP) {
@@ -1283,6 +1306,35 @@
}
/**
+ * Fades out and removes an overlay surface.
+ */
+ private void fadeOutAndRemoveOverlay(SurfaceControl surface) {
+ if (surface == null) {
+ return;
+ }
+
+ final ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 0.0f);
+ animator.setDuration(mCrossFadeAnimationDuration);
+ animator.addUpdateListener(animation -> {
+ final float alpha = (float) animation.getAnimatedValue();
+ final SurfaceControl.Transaction transaction =
+ mSurfaceControlTransactionFactory.getTransaction();
+ transaction.setAlpha(surface, alpha);
+ transaction.apply();
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ tx.remove(surface);
+ tx.apply();
+ }
+ });
+ animator.start();
+ }
+
+ /**
* Dumps internal states.
*/
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 9b6909b..4759550 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -111,14 +111,14 @@
final Rect sourceHintRect =
PipBoundsAlgorithm.getValidSourceHintRect(
taskInfo.pictureInPictureParams, currentBounds);
- animator = mPipAnimationController.getAnimator(taskInfo, leash,
- currentBounds, currentBounds, destinationBounds, sourceHintRect,
- TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
+ animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
+ currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
+ 0 /* startingAngle */, Surface.ROTATION_0);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
t.setAlpha(leash, 0f);
t.apply();
- animator = mPipAnimationController.getAnimator(taskInfo, leash,
- destinationBounds, 0f, 1f);
+ animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
+ 0f, 1f);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
} else {
throw new RuntimeException("Unrecognized animation type: "
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 9cf0b72..140f32f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -70,12 +70,19 @@
*/
public interface Listener {
/**
- * Called when the PIP menu visibility changes.
+ * Called when the PIP menu visibility change has started.
*
- * @param menuState the current state of the menu
+ * @param menuState the new, about-to-change state of the menu
* @param resize whether or not to resize the PiP with the state change
*/
- void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback);
+ void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback);
+
+ /**
+ * Called when the PIP menu state has finished changing/animating.
+ *
+ * @param menuState the new state of the menu.
+ */
+ void onPipMenuStateChangeFinish(int menuState);
/**
* Called when the PIP requested to be expanded.
@@ -166,13 +173,6 @@
detachPipMenuView();
}
-
- void onPinnedStackAnimationEnded() {
- if (isMenuVisible()) {
- mPipMenuView.onPipAnimationEnded();
- }
- }
-
private void attachPipMenuView() {
// In case detach was not called (e.g. PIP unexpectedly closed)
if (mPipMenuView != null) {
@@ -485,15 +485,15 @@
/**
* Handles changes in menu visibility.
*/
- void onMenuStateChanged(int menuState, boolean resize, Runnable callback) {
+ void onMenuStateChangeStart(int menuState, boolean resize, Runnable callback) {
if (DEBUG) {
- Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState
+ Log.d(TAG, "onMenuStateChangeStart() mMenuState=" + mMenuState
+ " menuState=" + menuState + " resize=" + resize
+ " callers=\n" + Debug.getCallers(5, " "));
}
if (menuState != mMenuState) {
- mListeners.forEach(l -> l.onPipMenuStateChanged(menuState, resize, callback));
+ mListeners.forEach(l -> l.onPipMenuStateChangeStart(menuState, resize, callback));
if (menuState == MENU_STATE_FULL) {
// Once visible, start listening for media action changes. This call will trigger
// the menu actions to be updated again.
@@ -511,6 +511,12 @@
Log.e(TAG, "Unable to update focus as menu appears/disappears", e);
}
}
+ }
+
+ void onMenuStateChangeFinish(int menuState) {
+ if (menuState != mMenuState) {
+ mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState));
+ }
mMenuState = menuState;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 3b65899..47a8c67 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,8 @@
*/
package com.android.wm.shell.pip.phone;
+import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
+
import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Rect;
@@ -59,6 +61,7 @@
private final PipTaskOrganizer mTaskOrganizer;
private final PipSnapAlgorithm mSnapAlgorithm;
private final Runnable mUpdateMovementBoundCallback;
+ private final Runnable mUnstashCallback;
private final AccessibilityCallbacks mCallbacks;
private final IAccessibilityInteractionConnection mConnectionImpl;
@@ -72,7 +75,7 @@
@NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm,
AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback,
- ShellExecutor mainExcutor) {
+ Runnable unstashCallback, ShellExecutor mainExcutor) {
mContext = context;
mMainExcutor = mainExcutor;
mPipBoundsState = pipBoundsState;
@@ -80,6 +83,7 @@
mTaskOrganizer = taskOrganizer;
mSnapAlgorithm = snapAlgorithm;
mUpdateMovementBoundCallback = updateMovementBoundCallback;
+ mUnstashCallback = unstashCallback;
mCallbacks = callbacks;
mConnectionImpl = new PipAccessibilityInteractionConnectionImpl();
}
@@ -118,6 +122,13 @@
setToNormalBounds();
}
result = true;
+ } else if (action == R.id.action_pip_stash) {
+ mMotionHelper.animateToStashedClosestEdge();
+ result = true;
+ } else if (action == R.id.action_pip_unstash) {
+ mUnstashCallback.run();
+ mPipBoundsState.setStashed(STASH_TYPE_NONE);
+ result = true;
} else {
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK:
@@ -246,6 +257,10 @@
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(R.id.action_pip_resize,
context.getString(R.string.accessibility_action_pip_resize)));
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(R.id.action_pip_stash,
+ context.getString(R.string.accessibility_action_pip_stash)));
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(R.id.action_pip_unstash,
+ context.getString(R.string.accessibility_action_pip_unstash)));
info.setImportantForAccessibility(true);
info.setClickable(true);
info.setVisibleToUser(true);
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 29a483b..36589e9 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
@@ -36,7 +36,6 @@
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -44,6 +43,7 @@
import android.util.Size;
import android.util.Slog;
import android.view.DisplayInfo;
+import android.view.SurfaceControl;
import android.view.WindowManagerGlobal;
import android.window.WindowContainerTransaction;
@@ -450,7 +450,7 @@
null /* windowContainerTransaction */);
};
- if (saveRestoreSnapFraction) {
+ if (mPipTaskOrganizer.isInPip() && saveRestoreSnapFraction) {
// Calculate the snap fraction of the current stack along the old movement bounds
final PipSnapAlgorithm pipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
final Rect postChangeStackBounds = new Rect(mPipBoundsState.getBounds());
@@ -535,10 +535,8 @@
private void onPipCornerRadiusChanged() {
if (mPinnedStackAnimationRecentsCallback != null) {
- final boolean enableCornerRadius =
- SystemProperties.getBoolean("debug.sf.enable_hole_punch_pip", false);
- final int cornerRadius = enableCornerRadius
- ? mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius) : 0;
+ final int cornerRadius =
+ mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
try {
mPinnedStackAnimationRecentsCallback.onPipCornerRadiusChanged(cornerRadius);
} catch (RemoteException e) {
@@ -559,8 +557,9 @@
return entryBounds;
}
- private void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
- mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds);
+ private void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds,
+ SurfaceControl overlay) {
+ mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds, overlay);
}
@Override
@@ -606,7 +605,6 @@
// Re-enable touches after the animation completes
mTouchHandler.setTouchEnabled(true);
mTouchHandler.onPinnedStackAnimationEnded(direction);
- mMenuController.onPinnedStackAnimationEnded();
}
private void updateMovementBounds(@Nullable Rect toBounds, boolean fromRotation,
@@ -852,10 +850,11 @@
}
@Override
- public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+ public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds,
+ SurfaceControl overlay) {
executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
(controller) -> {
- controller.stopSwipePipToHome(componentName, destinationBounds);
+ controller.stopSwipePipToHome(componentName, destinationBounds, overlay);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index ecbf0f1..e210835 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -40,12 +40,10 @@
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
@@ -152,11 +150,7 @@
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
inflate(context, R.layout.pip_menu, this);
- final boolean enableCornerRadius =
- SystemProperties.getBoolean("debug.sf.enable_hole_punch_pip", false);
- mBackgroundDrawable = enableCornerRadius
- ? mContext.getDrawable(R.drawable.pip_menu_background)
- : new ColorDrawable(Color.BLACK);
+ mBackgroundDrawable = mContext.getDrawable(R.drawable.pip_menu_background);
mBackgroundDrawable.setAlpha(0);
mViewRoot = findViewById(R.id.background);
mViewRoot.setBackground(mBackgroundDrawable);
@@ -281,17 +275,24 @@
}
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
- if (allowMenuTimeout) {
- mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
+ mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAllowTouches = true;
+ notifyMenuStateChangeFinish(menuState);
+ if (allowMenuTimeout) {
repostDelayedHide(INITIAL_DISMISS_DELAY);
}
- });
- }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mAllowTouches = true;
+ }
+ });
if (withDelay) {
// starts the menu container animation after window expansion is completed
- notifyMenuStateChange(menuState, resizeMenuOnShow, () -> {
+ notifyMenuStateChangeStart(menuState, resizeMenuOnShow, () -> {
if (mMenuContainerAnimator == null) {
return;
}
@@ -300,11 +301,11 @@
mMenuContainerAnimator.start();
});
} else {
- notifyMenuStateChange(menuState, resizeMenuOnShow, null);
+ notifyMenuStateChangeStart(menuState, resizeMenuOnShow, null);
setVisibility(VISIBLE);
mMenuContainerAnimator.start();
}
- updateActionViews(stackBounds);
+ updateActionViews(menuState, stackBounds);
} else {
// If we are already visible, then just start the delayed dismiss and unregister any
// existing input consumers from the previous drag
@@ -330,10 +331,6 @@
cancelDelayedHide();
}
- void onPipAnimationEnded() {
- mAllowTouches = true;
- }
-
void updateMenuLayout(Rect bounds) {
mPipMenuIconsAlgorithm.onBoundsChanged(bounds);
}
@@ -357,7 +354,7 @@
if (mMenuState != MENU_STATE_NONE) {
cancelDelayedHide();
if (notifyMenuVisibility) {
- notifyMenuStateChange(MENU_STATE_NONE, resize, null);
+ notifyMenuStateChangeStart(MENU_STATE_NONE, resize, null);
}
mMenuContainerAnimator = new AnimatorSet();
ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
@@ -376,6 +373,9 @@
@Override
public void onAnimationEnd(Animator animation) {
setVisibility(GONE);
+ if (notifyMenuVisibility) {
+ notifyMenuStateChangeFinish(MENU_STATE_NONE);
+ }
if (animationFinishedRunnable != null) {
animationFinishedRunnable.run();
}
@@ -404,11 +404,11 @@
mActions.clear();
mActions.addAll(actions);
if (mMenuState == MENU_STATE_FULL) {
- updateActionViews(stackBounds);
+ updateActionViews(mMenuState, stackBounds);
}
}
- private void updateActionViews(Rect stackBounds) {
+ private void updateActionViews(int menuState, Rect stackBounds) {
ViewGroup expandContainer = findViewById(R.id.expand_container);
ViewGroup actionsContainer = findViewById(R.id.actions_container);
actionsContainer.setOnTouchListener((v, ev) -> {
@@ -417,13 +417,13 @@
});
// Update the expand button only if it should show with the menu
- expandContainer.setVisibility(mMenuState == MENU_STATE_FULL
+ expandContainer.setVisibility(menuState == MENU_STATE_FULL
? View.VISIBLE
: View.INVISIBLE);
FrameLayout.LayoutParams expandedLp =
(FrameLayout.LayoutParams) expandContainer.getLayoutParams();
- if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE || mMenuState == MENU_STATE_NONE) {
+ if (mActions.isEmpty() || menuState == MENU_STATE_CLOSE || menuState == MENU_STATE_NONE) {
actionsContainer.setVisibility(View.INVISIBLE);
// Update the expand container margin to adjust the center of the expand button to
@@ -493,9 +493,13 @@
expandContainer.requestLayout();
}
- private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
+ private void notifyMenuStateChangeStart(int menuState, boolean resize, Runnable callback) {
+ mController.onMenuStateChangeStart(menuState, resize, callback);
+ }
+
+ private void notifyMenuStateChangeFinish(int menuState) {
mMenuState = menuState;
- mController.onMenuStateChanged(menuState, resize, callback);
+ mController.onMenuStateChangeFinish(menuState);
}
private void expandPip() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 9401cd6..15e7f07 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
@@ -21,7 +21,9 @@
import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND;
+import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_DISMISS;
import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
@@ -499,6 +501,28 @@
}
/**
+ * Animates the PiP to the stashed state, choosing the closest edge.
+ */
+ void animateToStashedClosestEdge() {
+ Rect tmpBounds = new Rect();
+ final Rect insetBounds = mPipBoundsState.getDisplayLayout().stableInsets();
+ final int stashType =
+ mPipBoundsState.getBounds().left == mPipBoundsState.getMovementBounds().left
+ ? STASH_TYPE_LEFT : STASH_TYPE_RIGHT;
+ final float leftEdge = stashType == STASH_TYPE_LEFT
+ ? mPipBoundsState.getStashOffset()
+ - mPipBoundsState.getBounds().width() + insetBounds.left
+ : mPipBoundsState.getDisplayBounds().right
+ - mPipBoundsState.getStashOffset() - insetBounds.right;
+ tmpBounds.set((int) leftEdge,
+ mPipBoundsState.getBounds().top,
+ (int) (leftEdge + mPipBoundsState.getBounds().width()),
+ mPipBoundsState.getBounds().bottom);
+ resizeAndAnimatePipUnchecked(tmpBounds, UNSTASH_DURATION);
+ mPipBoundsState.setStashed(stashType);
+ }
+
+ /**
* Animates the PiP from stashed state into un-stashed, popping it out from the edge.
*/
void animateToUnStashedBounds(Rect unstashedBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
index f8125fd..23153be72 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
@@ -27,6 +27,7 @@
private static final int PINCH_RESIZE_MAX_ANGLE_ROTATION = 45;
private static final float OVERROTATE_DAMP_FACTOR = 0.4f;
private static final float ANGLE_THRESHOLD = 5f;
+ private static final float OVERRESIZE_DAMP_FACTOR = 0.25f;
private final PointF mTmpDownVector = new PointF();
private final PointF mTmpLastVector = new PointF();
@@ -46,7 +47,10 @@
lastSecondPoint.y - lastPoint.y);
float minScale = getMinScale(initialBounds, minSize);
float maxScale = getMaxScale(initialBounds, maxSize);
- float scale = Math.max(minScale, Math.min(maxScale, dist / downDist));
+ float overStretchMin = minScale - dist / downDist > 0 ? minScale - dist / downDist : 0;
+ float overStretchMax = dist / downDist - maxScale > 0 ? dist / downDist - maxScale : 0;
+ float scale = Math.max(minScale - overStretchMin * OVERRESIZE_DAMP_FACTOR,
+ Math.min(maxScale + overStretchMax * OVERRESIZE_DAMP_FACTOR, dist / downDist));
// Scale the bounds by the change in distance between the points
resizeBoundsOut.set(initialBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 8726ee7..32553f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -383,14 +383,17 @@
return;
}
+ final Rect pipBounds = mPipBoundsState.getBounds();
if (action == MotionEvent.ACTION_POINTER_DOWN) {
- if (mFirstIndex == -1 && mSecondIndex == -1) {
+ if (mFirstIndex == -1 && mSecondIndex == -1
+ && pipBounds.contains((int) ev.getRawX(0), (int) ev.getRawY(0))
+ && pipBounds.contains((int) ev.getRawX(1), (int) ev.getRawY(1))) {
mAllowGesture = true;
mFirstIndex = 0;
mSecondIndex = 1;
mDownPoint.set(ev.getRawX(mFirstIndex), ev.getRawY(mFirstIndex));
mDownSecondPoint.set(ev.getRawX(mSecondIndex), ev.getRawY(mSecondIndex));
- mDownBounds.set(mPipBoundsState.getBounds());
+ mDownBounds.set(pipBounds);
mLastPoint.set(mDownPoint);
mLastSecondPoint.set(mLastSecondPoint);
@@ -419,13 +422,13 @@
// Reset the down to begin resizing from this point
mDownPoint.set(mLastPoint);
mDownSecondPoint.set(mLastSecondPoint);
- }
- if (mThresholdCrossed) {
if (mPhonePipMenuController.isMenuVisible()) {
mPhonePipMenuController.hideMenu();
}
+ }
+ if (mThresholdCrossed) {
mAngle = mPinchResizingAlgorithm.calculateBoundsAndAngle(mDownPoint,
mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize,
mDownBounds, mLastResizeBounds);
@@ -514,7 +517,18 @@
|| mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y);
}
- final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mLastResizeBounds);
+ final int leftEdge = mLastResizeBounds.left;
+ final Rect movementBounds =
+ mPipBoundsAlgorithm.getMovementBounds(mLastResizeBounds);
+ final int fromLeft = Math.abs(leftEdge - movementBounds.left);
+ final int fromRight = Math.abs(movementBounds.right - leftEdge);
+ // The PIP will be snapped to either the right or left edge, so calculate which one
+ // is closest to the current position.
+ final int newLeft = fromLeft < fromRight
+ ? movementBounds.left : movementBounds.right;
+ mLastResizeBounds.offsetTo(newLeft, mLastResizeBounds.top);
+ final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
+ mLastResizeBounds, movementBounds);
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
PINCH_RESIZE_SNAP_DURATION, mAngle, callback);
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 0a0798e..304390d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -129,8 +129,13 @@
*/
private class PipMenuListener implements PhonePipMenuController.Listener {
@Override
- public void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback) {
- setMenuState(menuState, resize, callback);
+ public void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback) {
+ PipTouchHandler.this.onPipMenuStateChangeStart(menuState, resize, callback);
+ }
+
+ @Override
+ public void onPipMenuStateChangeFinish(int menuState) {
+ setMenuState(menuState);
}
@Override
@@ -199,7 +204,8 @@
mainExecutor);
mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
- this::onAccessibilityShowMenu, this::updateMovementBounds, mainExecutor);
+ this::onAccessibilityShowMenu, this::updateMovementBounds,
+ this::animateToUnStashedState, mainExecutor);
}
public void init() {
@@ -467,17 +473,20 @@
float aspectRatio) {
final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
mPipBoundsState.getDisplayBounds().height());
- final int totalPadding = insetBounds.left * 2;
+ final int totalHorizontalPadding = insetBounds.left
+ + (mPipBoundsState.getDisplayBounds().width() - insetBounds.right);
+ final int totalVerticalPadding = insetBounds.top
+ + (mPipBoundsState.getDisplayBounds().height() - insetBounds.bottom);
final int minWidth, minHeight, maxWidth, maxHeight;
if (aspectRatio > 1f) {
minWidth = (int) Math.min(normalBounds.width(), shorterLength * MINIMUM_SIZE_PERCENT);
minHeight = (int) (minWidth / aspectRatio);
- maxWidth = (int) Math.max(normalBounds.width(), shorterLength - totalPadding);
+ maxWidth = (int) Math.max(normalBounds.width(), shorterLength - totalHorizontalPadding);
maxHeight = (int) (maxWidth / aspectRatio);
} else {
minHeight = (int) Math.min(normalBounds.height(), shorterLength * MINIMUM_SIZE_PERCENT);
minWidth = (int) (minHeight * aspectRatio);
- maxHeight = (int) Math.max(normalBounds.height(), shorterLength - totalPadding);
+ maxHeight = (int) Math.max(normalBounds.height(), shorterLength - totalVerticalPadding);
maxWidth = (int) (maxHeight * aspectRatio);
}
@@ -614,7 +623,7 @@
}
}
- shouldDeliverToMenu |= !mPipBoundsState.isStashed();
+ shouldDeliverToMenu &= !mPipBoundsState.isStashed();
// Deliver the event to PipMenuActivity to handle button click if the menu has shown.
if (shouldDeliverToMenu) {
@@ -646,9 +655,9 @@
}
/**
- * Sets the menu visibility.
+ * Called when the PiP menu state is in the process of animating/changing from one to another.
*/
- private void setMenuState(int menuState, boolean resize, Runnable callback) {
+ private void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback) {
if (mMenuState == menuState && !resize) {
return;
}
@@ -686,6 +695,9 @@
mSavedSnapFraction = -1f;
}
}
+ }
+
+ private void setMenuState(int menuState) {
mMenuState = menuState;
updateMovementBounds();
// If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index 66a4a60..d0998eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.graphics.Rect;
+import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -36,8 +37,9 @@
private boolean mIsActive = false;
MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
- super(taskOrganizer, displayId, callbacks, syncQueue);
+ StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
+ SurfaceSession surfaceSession) {
+ super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
}
boolean isActive() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 01a81d2..82f95a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.graphics.Rect;
+import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -33,8 +34,9 @@
private static final String TAG = SideStage.class.getSimpleName();
SideStage(ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
- super(taskOrganizer, displayId, callbacks, syncQueue);
+ StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
+ SurfaceSession surfaceSession) {
+ super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
}
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index d4506fd..002bfb6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
/**
* Interface to engage split-screen feature.
@@ -27,29 +28,6 @@
@ExternalThread
public interface SplitScreen {
/**
- * Stage position isn't specified normally meaning to use what ever it is currently set to.
- */
- int STAGE_POSITION_UNDEFINED = -1;
- /**
- * Specifies that a stage is positioned at the top half of the screen if
- * in portrait mode or at the left half of the screen if in landscape mode.
- */
- int STAGE_POSITION_TOP_OR_LEFT = 0;
-
- /**
- * Specifies that a stage is positioned at the bottom half of the screen if
- * in portrait mode or at the right half of the screen if in landscape mode.
- */
- int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
-
- @IntDef(prefix = { "STAGE_POSITION_" }, value = {
- STAGE_POSITION_UNDEFINED,
- STAGE_POSITION_TOP_OR_LEFT,
- STAGE_POSITION_BOTTOM_OR_RIGHT
- })
- @interface StagePosition {}
-
- /**
* Stage type isn't specified normally meaning to use what ever the default is.
* E.g. exit split-screen and launch the app in fullscreen.
*/
@@ -75,7 +53,7 @@
/** Callback interface for listening to changes in a split-screen stage. */
interface SplitScreenListener {
- void onStagePositionChanged(@StageType int stage, @StagePosition int position);
+ void onStagePositionChanged(@StageType int stage, @SplitPosition int position);
void onTaskStageChanged(int taskId, @StageType int stage, boolean visible);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 5aa59f2..9a457b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -19,9 +19,9 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -53,6 +53,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
import com.android.wm.shell.transition.Transitions;
@@ -122,7 +123,7 @@
return mStageCoordinator.isSplitScreenVisible();
}
- public boolean moveToSideStage(int taskId, @SplitScreen.StagePosition int sideStagePosition) {
+ public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId);
@@ -131,7 +132,7 @@
}
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitScreen.StagePosition int sideStagePosition) {
+ @SplitPosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
}
@@ -139,7 +140,7 @@
return mStageCoordinator.removeFromSideStage(taskId);
}
- public void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
+ public void setSideStagePosition(@SplitPosition int sideStagePosition) {
mStageCoordinator.setSideStagePosition(sideStagePosition);
}
@@ -149,7 +150,7 @@
public void enterSplitScreen(int taskId, boolean leftOrTop) {
moveToSideStage(taskId,
- leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
+ leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
}
public void exitSplitScreen() {
@@ -173,7 +174,7 @@
}
public void startTask(int taskId, @SplitScreen.StageType int stage,
- @SplitScreen.StagePosition int position, @Nullable Bundle options) {
+ @SplitPosition int position, @Nullable Bundle options) {
options = resolveStartStage(stage, position, options);
try {
@@ -184,7 +185,7 @@
}
public void startShortcut(String packageName, String shortcutId,
- @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+ @SplitScreen.StageType int stage, @SplitPosition int position,
@Nullable Bundle options, UserHandle user) {
options = resolveStartStage(stage, position, options);
@@ -199,7 +200,7 @@
}
public void startIntent(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+ @SplitScreen.StageType int stage, @SplitPosition int position,
@Nullable Bundle options) {
options = resolveStartStage(stage, position, options);
@@ -211,11 +212,11 @@
}
private Bundle resolveStartStage(@SplitScreen.StageType int stage,
- @SplitScreen.StagePosition int position, @Nullable Bundle options) {
+ @SplitPosition int position, @Nullable Bundle options) {
switch (stage) {
case STAGE_TYPE_UNDEFINED: {
// Use the stage of the specified position is valid.
- if (position != STAGE_POSITION_UNDEFINED) {
+ if (position != SPLIT_POSITION_UNDEFINED) {
if (position == mStageCoordinator.getSideStagePosition()) {
options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
} else {
@@ -228,7 +229,7 @@
break;
}
case STAGE_TYPE_SIDE: {
- if (position != STAGE_POSITION_UNDEFINED) {
+ if (position != SPLIT_POSITION_UNDEFINED) {
mStageCoordinator.setSideStagePosition(position);
} else {
position = mStageCoordinator.getSideStagePosition();
@@ -240,10 +241,10 @@
break;
}
case STAGE_TYPE_MAIN: {
- if (position != STAGE_POSITION_UNDEFINED) {
+ if (position != SPLIT_POSITION_UNDEFINED) {
// Set the side stage opposite of what we want to the main stage.
- final int sideStagePosition = position == STAGE_POSITION_TOP_OR_LEFT
- ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
+ final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
+ ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
mStageCoordinator.setSideStagePosition(sideStagePosition);
} else {
position = mStageCoordinator.getMainStagePosition();
@@ -418,7 +419,7 @@
@Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
- @SplitScreen.StagePosition int sidePosition,
+ @SplitPosition int sidePosition,
@Nullable IRemoteTransition remoteTransition) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 4be9e75..0264c5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -25,8 +25,9 @@
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -47,14 +48,15 @@
import android.os.IBinder;
import android.util.Log;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.WindowManager;
import android.window.DisplayAreaInfo;
import android.window.IRemoteTransition;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
-
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -64,6 +66,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
@@ -84,19 +87,22 @@
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
*/
-class StageCoordinator implements SplitLayout.LayoutChangeListener,
- RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
+class StageCoordinator implements SplitLayout.SplitLayoutHandler,
+ RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
private static final String TAG = StageCoordinator.class.getSimpleName();
/** internal value for mDismissTop that represents no dismiss */
private static final int NO_DISMISS = -2;
+ private final SurfaceSession mSurfaceSession = new SurfaceSession();
+
private final MainStage mMainStage;
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
private final SideStage mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
- private @SplitScreen.StagePosition int mSideStagePosition = STAGE_POSITION_BOTTOM_OR_RIGHT;
+ @SplitPosition
+ private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
private final int mDisplayId;
private SplitLayout mSplitLayout;
@@ -115,8 +121,8 @@
/** Whether the device is supporting legacy split or not. */
private boolean mUseLegacySplit;
-
@SplitScreen.StageType int mDismissTop = NO_DISMISS;
+
private final Runnable mOnTransitionAnimationComplete = () -> {
// If still playing, let it finish.
if (!isSplitScreenVisible()) {
@@ -137,8 +143,18 @@
mSyncQueue = syncQueue;
mRootTDAOrganizer = rootTDAOrganizer;
mTaskOrganizer = taskOrganizer;
- mMainStage = new MainStage(mTaskOrganizer, mDisplayId, mMainStageListener, mSyncQueue);
- mSideStage = new SideStage(mTaskOrganizer, mDisplayId, mSideStageListener, mSyncQueue);
+ mMainStage = new MainStage(
+ mTaskOrganizer,
+ mDisplayId,
+ mMainStageListener,
+ mSyncQueue,
+ mSurfaceSession);
+ mSideStage = new SideStage(
+ mTaskOrganizer,
+ mDisplayId,
+ mSideStageListener,
+ mSyncQueue,
+ mSurfaceSession);
mDisplayImeController = displayImeController;
mRootTDAOrganizer.registerListener(displayId, this);
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
@@ -176,7 +192,7 @@
}
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitScreen.StagePosition int sideStagePosition) {
+ @SplitPosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
setSideStagePosition(sideStagePosition);
mMainStage.activate(getMainStageBounds(), wct);
@@ -201,7 +217,7 @@
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
- @Nullable Bundle sideOptions, @SplitScreen.StagePosition int sidePosition,
+ @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
@Nullable IRemoteTransition remoteTransition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mainOptions = mainOptions != null ? mainOptions : new Bundle();
@@ -225,20 +241,22 @@
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
}
- @SplitScreen.StagePosition int getSideStagePosition() {
+ @SplitLayout.SplitPosition
+ int getSideStagePosition() {
return mSideStagePosition;
}
- @SplitScreen.StagePosition int getMainStagePosition() {
- return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
+ @SplitLayout.SplitPosition
+ int getMainStagePosition() {
+ return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
+ ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
}
- void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
+ void setSideStagePosition(@SplitPosition int sideStagePosition) {
setSideStagePosition(sideStagePosition, true /* updateVisibility */);
}
- private void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition,
+ private void setSideStagePosition(@SplitPosition int sideStagePosition,
boolean updateVisibility) {
if (mSideStagePosition == sideStagePosition) return;
mSideStagePosition = sideStagePosition;
@@ -289,7 +307,7 @@
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
}
- void updateActivityOptions(Bundle opts, @SplitScreen.StagePosition int position) {
+ void updateActivityOptions(Bundle opts, @SplitPosition int position) {
addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage);
if (!mMainStage.isActive()) {
@@ -487,8 +505,8 @@
@Override
public void onSnappedToDismiss(boolean bottomOrRight) {
final boolean mainStageToTop =
- bottomOrRight ? mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT
- : mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT;
+ bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
+ : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
if (ENABLE_SHELL_TRANSITIONS) {
onSnappedToDismissTransition(mainStageToTop);
return;
@@ -497,55 +515,49 @@
}
@Override
- public void onBoundsChanging(SplitLayout layout) {
- final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
- if (dividerLeash == null) return;
- final Rect mainStageBounds = getMainStageBounds();
- final Rect sideStageBounds = getSideStageBounds();
-
- mSyncQueue.runInSync(t -> t
- .setPosition(dividerLeash,
- mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top)
- .setPosition(mMainStage.mRootLeash, mainStageBounds.left, mainStageBounds.top)
- .setPosition(mSideStage.mRootLeash, sideStageBounds.left, sideStageBounds.top)
- // Sets crop to prevent visible region of tasks overlap with each other when
- // re-positioning surfaces while resizing.
- .setWindowCrop(mMainStage.mRootLeash,
- mainStageBounds.width(), mainStageBounds.height())
- .setWindowCrop(mSideStage.mRootLeash,
- sideStageBounds.width(), sideStageBounds.height()));
-
+ public void onDoubleTappedDivider() {
+ setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
+ ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT);
}
@Override
- public void onDoubleTappedDivider() {
- setSideStagePosition(mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT);
+ public void onBoundsChanging(SplitLayout layout) {
+ final StageTaskListener topLeftStage =
+ mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
+ final StageTaskListener bottomRightStage =
+ mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
+
+ mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
+ bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
}
@Override
public void onBoundsChanged(SplitLayout layout) {
- final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
- if (dividerLeash == null) return;
- final Rect mainStageBounds = getMainStageBounds();
- final Rect sideStageBounds = getSideStageBounds();
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- mMainStage.setBounds(mainStageBounds, wct);
- mSideStage.setBounds(sideStageBounds, wct);
- mTaskOrganizer.applyTransaction(wct);
+ final StageTaskListener topLeftStage =
+ mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
+ final StageTaskListener bottomRightStage =
+ mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
- mSyncQueue.runInSync(t -> t
- // Resets layer of divider bar to make sure it is always on top.
- .setLayer(dividerLeash, Integer.MAX_VALUE)
- .setPosition(dividerLeash,
- mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top)
- .setPosition(mMainStage.mRootLeash,
- mainStageBounds.left, mainStageBounds.top)
- .setPosition(mSideStage.mRootLeash,
- sideStageBounds.left, sideStageBounds.top)
- // Resets crop to apply new surface bounds directly.
- .setWindowCrop(mMainStage.mRootLeash, null)
- .setWindowCrop(mSideStage.mRootLeash, null));
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
+ mSyncQueue.queue(wct);
+ mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
+ bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
+ }
+
+ @Override
+ public int getSplitItemPosition(WindowContainerToken token) {
+ if (token == null) {
+ return SPLIT_POSITION_UNDEFINED;
+ }
+
+ if (token.equals(mMainStage.mRootTaskInfo.getToken())) {
+ return getMainStagePosition();
+ } else if (token.equals(mSideStage.mRootTaskInfo.getToken())) {
+ return getSideStagePosition();
+ }
+
+ return SPLIT_POSITION_UNDEFINED;
}
@Override
@@ -555,7 +567,7 @@
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mDisplayAreaInfo.configuration, this,
b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b),
- mDisplayImeController);
+ mDisplayImeController, mTaskOrganizer);
}
}
@@ -574,12 +586,12 @@
}
private Rect getSideStageBounds() {
- return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
}
private Rect getMainStageBounds() {
- return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
}
@@ -742,7 +754,7 @@
// Update local states (before animating).
setDividerVisibility(true);
- setSideStagePosition(STAGE_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */);
+ setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */);
setSplitsVisible(true);
addDividerBarToTransition(info, t, true /* show */);
@@ -908,6 +920,13 @@
StageCoordinator.this.onStageRootTaskVanished(this);
}
+ @Override
+ public void onNoLongerSupportMultiWindow() {
+ if (mMainStage.isActive()) {
+ StageCoordinator.this.exitSplitScreen();
+ }
+ }
+
private void reset() {
mHasRootTask = false;
mVisible = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 1da0a2d..0fd8eca62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -29,11 +29,13 @@
import android.graphics.Rect;
import android.util.SparseArray;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import java.io.PrintWriter;
@@ -44,6 +46,7 @@
* They only serve to hold a collection of tasks and provide APIs like
* {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator}
* to perform operations in-sync with other containers.
+ *
* @see StageCoordinator
*/
class StageTaskListener implements ShellTaskOrganizer.TaskListener {
@@ -58,22 +61,31 @@
/** Callback interface for listening to changes in a split-screen stage. */
public interface StageListenerCallbacks {
void onRootTaskAppeared();
+
void onStatusChanged(boolean visible, boolean hasChildren);
+
void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
+
void onRootTaskVanished();
+ void onNoLongerSupportMultiWindow();
}
+
private final StageListenerCallbacks mCallbacks;
private final SyncTransactionQueue mSyncQueue;
+ private final SurfaceSession mSurfaceSession;
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
protected SurfaceControl mRootLeash;
+ protected SurfaceControl mDimLayer;
protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>();
StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
+ StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
+ SurfaceSession surfaceSession) {
mCallbacks = callbacks;
mSyncQueue = syncQueue;
+ mSurfaceSession = surfaceSession;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
}
@@ -93,6 +105,8 @@
mRootTaskInfo = taskInfo;
mCallbacks.onRootTaskAppeared();
sendStatusChanged();
+ mSyncQueue.runInSync(t -> mDimLayer =
+ SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession));
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
final int taskId = taskInfo.taskId;
mChildrenLeashes.put(taskId, leash);
@@ -113,6 +127,11 @@
@Override
@CallSuper
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (!taskInfo.supportsMultiWindow) {
+ // Leave split screen if the task no longer supports multi window.
+ mCallbacks.onNoLongerSupportMultiWindow();
+ return;
+ }
if (mRootTaskInfo.taskId == taskInfo.taskId) {
mRootTaskInfo = taskInfo;
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
@@ -140,6 +159,7 @@
final int taskId = taskInfo.taskId;
if (mRootTaskInfo.taskId == taskId) {
mCallbacks.onRootTaskVanished();
+ mSyncQueue.runInSync(t -> t.remove(mDimLayer));
mRootTaskInfo = null;
} else if (mChildrenTaskInfo.contains(taskId)) {
mChildrenTaskInfo.remove(taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index fd6f0ad9..a646231 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -39,6 +39,7 @@
import android.os.Trace;
import android.util.Slog;
import android.view.SurfaceControl;
+import android.view.View;
import android.window.SplashScreenView;
import com.android.internal.R;
@@ -66,7 +67,7 @@
// For example, an icon with the foreground 108*108 opaque pixels and it's background
// also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon.
private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f);
- private static final float NO_BACKGROUND_SCALE = 1.3f;
+ private static final float NO_BACKGROUND_SCALE = 192f / 160;
private final Context mContext;
private final IconProvider mIconProvider;
@@ -283,7 +284,8 @@
} else {
final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
- final int scaledIconDpi = (int) (0.5f + iconScale * densityDpi);
+ final int scaledIconDpi =
+ (int) (0.5f + iconScale * densityDpi * NO_BACKGROUND_SCALE);
iconDrawable = mIconProvider.getIcon(mActivityInfo, scaledIconDpi);
if (iconDrawable == null) {
iconDrawable = mContext.getPackageManager().getDefaultActivityIcon();
@@ -307,7 +309,7 @@
mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
mTmpAttrs.mIconBgColor != Color.TRANSPARENT
? mTmpAttrs.mIconBgColor : mThemeColor,
- iconDrawable, mFinalIconSize, mSplashscreenWorkerHandler);
+ iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler);
}
private boolean processAdaptiveIcon(Drawable iconDrawable) {
@@ -356,7 +358,7 @@
Slog.d(TAG, "makeSplashScreenContentView: choose fg icon");
}
// Reference AdaptiveIcon description, outer is 108 and inner is 72, so we
- // should enlarge the size 108/72 if we only draw adaptiveIcon's foreground.
+ // scale by 192/160 if we only draw adaptiveIcon's foreground.
final float noBgScale =
foreIconTester.nonTransparentRatio() < ENLARGE_FOREGROUND_ICON_THRESHOLD
? NO_BACKGROUND_SCALE : 1f;
@@ -397,7 +399,17 @@
splashScreenView.setNotCopyable();
splashScreenView.setRevealAnimationSupported(false);
}
- splashScreenView.makeSystemUIColorsTransparent();
+ splashScreenView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ SplashScreenView.applySystemBarsContrastColor(v.getWindowInsetsController(),
+ splashScreenView.getInitBackgroundColor());
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ }
+ });
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return splashScreenView;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index 6cbba9f..e8d95ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
@@ -32,7 +31,6 @@
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.graphics.Shader;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.ColorDrawable;
@@ -51,59 +49,53 @@
public class SplashscreenIconDrawableFactory {
static Drawable makeIconDrawable(@ColorInt int backgroundColor,
- @NonNull Drawable foregroundDrawable, int iconSize,
+ @NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize,
Handler splashscreenWorkerHandler) {
if (foregroundDrawable instanceof Animatable) {
return new AnimatableIconDrawable(backgroundColor, foregroundDrawable);
} else if (foregroundDrawable instanceof AdaptiveIconDrawable) {
- return new ImmobileIconDrawable((AdaptiveIconDrawable) foregroundDrawable, iconSize,
- splashscreenWorkerHandler);
+ return new ImmobileIconDrawable((AdaptiveIconDrawable) foregroundDrawable,
+ srcIconSize, iconSize, splashscreenWorkerHandler);
} else {
// TODO for legacy icon don't use adaptive icon drawable to wrapper it
return new ImmobileIconDrawable(new AdaptiveIconDrawable(
- new ColorDrawable(backgroundColor), foregroundDrawable), iconSize,
- splashscreenWorkerHandler);
+ new ColorDrawable(backgroundColor), foregroundDrawable),
+ srcIconSize, iconSize, splashscreenWorkerHandler);
}
}
private static class ImmobileIconDrawable extends Drawable {
- private boolean mCacheComplete;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
| Paint.FILTER_BITMAP_FLAG);
+ private final Matrix mMatrix = new Matrix();
+ private Bitmap mIconBitmap;
- ImmobileIconDrawable(AdaptiveIconDrawable drawable, int iconSize,
+ ImmobileIconDrawable(AdaptiveIconDrawable drawable, int srcIconSize, int iconSize,
Handler splashscreenWorkerHandler) {
- splashscreenWorkerHandler.post(() -> cachePaint(drawable, iconSize, iconSize));
+ final float scale = (float) iconSize / srcIconSize;
+ mMatrix.setScale(scale, scale);
+ splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, srcIconSize));
}
- private void cachePaint(AdaptiveIconDrawable drawable, int width, int height) {
+ private void preDrawIcon(AdaptiveIconDrawable drawable, int size) {
synchronized (mPaint) {
- if (mCacheComplete) {
- return;
- }
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "cachePaint");
- final Bitmap layersBitmap = Bitmap.createBitmap(width, height,
- Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(layersBitmap);
- drawable.setBounds(0, 0, width, height);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "preDrawIcon");
+ mIconBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(mIconBitmap);
+ drawable.setBounds(0, 0, size, size);
drawable.draw(canvas);
- final Shader layersShader = new BitmapShader(layersBitmap, Shader.TileMode.CLAMP,
- Shader.TileMode.CLAMP);
- mPaint.setShader(layersShader);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- mCacheComplete = true;
}
}
@Override
public void draw(Canvas canvas) {
synchronized (mPaint) {
- if (mCacheComplete) {
- final Rect bounds = getBounds();
- canvas.drawRect(bounds, mPaint);
+ if (mIconBitmap != null) {
+ canvas.drawBitmap(mIconBitmap, mMatrix, mPaint);
} else {
// this shouldn't happen, but if it really happen, invalidate self to wait
- // for cachePaint finish.
+ // for bitmap to be ready.
invalidateSelf();
}
}
@@ -111,12 +103,10 @@
@Override
public void setAlpha(int alpha) {
-
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
-
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index a1e6832..187e104 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -25,12 +25,10 @@
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
@@ -43,13 +41,13 @@
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
+import android.widget.FrameLayout;
import android.window.SplashScreenView;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.StartingWindowInfo;
import android.window.TaskSnapshot;
import com.android.internal.R;
-import com.android.internal.policy.PhoneWindow;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
@@ -103,7 +101,7 @@
private Choreographer mChoreographer;
private static final boolean DEBUG_ENABLE_REVEAL_ANIMATION =
- SystemProperties.getBoolean("persist.debug.enable_reveal_animation", false);
+ SystemProperties.getBoolean("persist.debug.enable_reveal_animation", true);
/**
* @param splashScreenExecutor The thread used to control add and remove starting window.
*/
@@ -152,14 +150,6 @@
return;
}
- CharSequence nonLocalizedLabel = activityInfo.nonLocalizedLabel;
- int labelRes = activityInfo.labelRes;
- if (activityInfo.nonLocalizedLabel == null && activityInfo.labelRes == 0) {
- ApplicationInfo app = activityInfo.applicationInfo;
- nonLocalizedLabel = app.nonLocalizedLabel;
- labelRes = app.labelRes;
- }
-
final int taskId = taskInfo.taskId;
Context context = mContext;
// replace with the default theme if the application didn't set
@@ -169,8 +159,7 @@
: com.android.internal.R.style.Theme_DeviceDefault_DayNight;
if (DEBUG_SPLASH_SCREEN) {
Slog.d(TAG, "addSplashScreen " + activityInfo.packageName
- + ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="
- + Integer.toHexString(theme) + " task= " + taskInfo.taskId);
+ + " theme=" + Integer.toHexString(theme) + " task= " + taskInfo.taskId);
}
// Obtain proper context to launch on the right display.
@@ -180,7 +169,7 @@
return;
}
context = displayContext;
- if (theme != context.getThemeResId() || labelRes != 0) {
+ if (theme != context.getThemeResId()) {
try {
context = context.createPackageContextAsUser(activityInfo.packageName,
CONTEXT_RESTRICTED, UserHandle.of(taskInfo.userId));
@@ -222,23 +211,22 @@
typedArray.recycle();
}
- final PhoneWindow win = new PhoneWindow(context);
- win.setIsStartingWindow(true);
-
- final Resources res = context.getResources();
- final CharSequence label = res.getText(labelRes, null);
- // Only change the accessibility title if the label is localized
- if (label != null) {
- win.setTitle(label, true);
- } else {
- win.setTitle(nonLocalizedLabel, false);
- }
-
- int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
+ params.setFitInsetsSides(0);
+ params.setFitInsetsTypes(0);
+ int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+ | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
final TypedArray a = context.obtainStyledAttributes(R.styleable.Window);
if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
}
+ params.layoutInDisplayCutoutMode = a.getInt(
+ R.styleable.Window_windowLayoutInDisplayCutoutMode,
+ params.layoutInDisplayCutoutMode);
+ params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);
a.recycle();
// Assumes it's safe to show starting windows of launched apps while
@@ -256,28 +244,15 @@
windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- win.setFlags(windowFlags, windowFlags);
-
- final int iconRes = activityInfo.getIconResource();
- final int logoRes = activityInfo.getLogoResource();
- win.setDefaultIcon(iconRes);
- win.setDefaultLogo(logoRes);
-
- final WindowManager.LayoutParams params = win.getAttributes();
- params.type = WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
- params.width = WindowManager.LayoutParams.MATCH_PARENT;
- params.height = WindowManager.LayoutParams.MATCH_PARENT;
+ params.flags = windowFlags;
params.token = appToken;
params.packageName = activityInfo.packageName;
- params.windowAnimations = win.getWindowStyle().getResourceId(
- com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
// Setting as trusted overlay to let touches pass through. This is safe because this
// window is controlled by the system.
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
- params.format = PixelFormat.RGBA_8888;
- if (!res.getCompatibilityInfo().supportsScreen()) {
+ if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
}
@@ -297,6 +272,7 @@
// Record whether create splash screen view success, notify to current thread after
// create splash screen view finished.
final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier();
+ final FrameLayout rootLayout = new FrameLayout(context);
final Runnable setViewSynchronized = () -> {
// waiting for setContentView before relayoutWindow
SplashScreenView contentView = viewSupplier.get();
@@ -306,8 +282,7 @@
// if view == null then creation of content view was failed.
if (contentView != null) {
try {
- win.setContentView(contentView);
- contentView.cacheRootWindow(win);
+ rootLayout.addView(contentView);
} catch (RuntimeException e) {
Slog.w(TAG, "failed set content view to starting window "
+ "at taskId: " + taskId, e);
@@ -321,9 +296,8 @@
viewSupplier::setView);
try {
- final View view = win.getDecorView();
final WindowManager wm = context.getSystemService(WindowManager.class);
- postAddWindow(taskId, appToken, view, wm, params);
+ postAddWindow(taskId, appToken, rootLayout, wm, params);
// We use the splash screen worker thread to create SplashScreenView while adding the
// window, as otherwise Choreographer#doFrame might be delayed on this thread.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 3251abc..60707cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -22,6 +22,7 @@
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
@@ -226,18 +227,11 @@
}
/**
- * Reparents all participants into a shared parent and orders them based on: the global transit
- * type, their transit mode, and their destination z-order.
+ * Sets up visibility/alpha/transforms to resemble the starting state of an animation.
*/
private static void setupStartState(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
boolean isOpening = isOpeningType(info.getType());
- if (info.getRootLeash().isValid()) {
- t.show(info.getRootLeash());
- }
- // Put animating stuff above this line and put static stuff below it.
- int zSplitLine = info.getChanges().size();
- // changes should be ordered top-to-bottom in z
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final SurfaceControl leash = change.getLeash();
@@ -254,6 +248,52 @@
continue;
}
+ if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
+ t.show(leash);
+ t.setMatrix(leash, 1, 0, 0, 1);
+ if (isOpening
+ // If this is a transferred starting window, we want it immediately visible.
+ && (change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
+ t.setAlpha(leash, 0.f);
+ // fix alpha in finish transaction in case the animator itself no-ops.
+ finishT.setAlpha(leash, 1.f);
+ }
+ } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
+ // Wallpaper is a bit of an anomaly: it's visibility is tied to other WindowStates.
+ // As a result, we actually can't hide it's WindowToken because there may not be a
+ // transition associated with it becoming visible again. Fortunately, since it is
+ // always z-ordered to the back, we don't have to worry about it flickering to the
+ // front during reparenting, so the hide here isn't necessary for it.
+ if ((change.getFlags() & FLAG_IS_WALLPAPER) == 0) {
+ finishT.hide(leash);
+ }
+ }
+ }
+ }
+
+ /**
+ * Reparents all participants into a shared parent and orders them based on: the global transit
+ * type, their transit mode, and their destination z-order.
+ */
+ private static void setupAnimHierarchy(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
+ boolean isOpening = isOpeningType(info.getType());
+ if (info.getRootLeash().isValid()) {
+ t.show(info.getRootLeash());
+ }
+ // Put animating stuff above this line and put static stuff below it.
+ int zSplitLine = info.getChanges().size();
+ // changes should be ordered top-to-bottom in z
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final SurfaceControl leash = change.getLeash();
+ final int mode = info.getChanges().get(i).getMode();
+
+ // Don't reparent anything that isn't independent within its parents
+ if (!TransitionInfo.isIndependent(change, info)) {
+ continue;
+ }
+
boolean hasParent = change.getParent() != null;
if (!hasParent) {
@@ -263,24 +303,12 @@
}
// Put all the OPEN/SHOW on top
if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
- t.show(leash);
- t.setMatrix(leash, 1, 0, 0, 1);
if (isOpening) {
- // put on top with 0 alpha
+ // put on top
t.setLayer(leash, zSplitLine + info.getChanges().size() - i);
- if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- // This received a transferred starting window, so make it immediately
- // visible.
- t.setAlpha(leash, 1.f);
- } else {
- t.setAlpha(leash, 0.f);
- // fix alpha in finish transaction in case the animator itself no-ops.
- finishT.setAlpha(leash, 1.f);
- }
} else {
- // put on bottom and leave it visible
+ // put on bottom
t.setLayer(leash, zSplitLine - i);
- t.setAlpha(leash, 1.f);
}
} else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
if (isOpening) {
@@ -290,7 +318,7 @@
// put on top
t.setLayer(leash, zSplitLine + info.getChanges().size() - i);
}
- } else { // CHANGE
+ } else { // CHANGE or other
t.setLayer(leash, zSplitLine + info.getChanges().size() - i);
}
}
@@ -329,6 +357,8 @@
active.mInfo = info;
active.mStartT = t;
active.mFinishT = finishT;
+ setupStartState(active.mInfo, active.mStartT, active.mFinishT);
+
if (activeIdx > 0) {
// This is now playing at the same time as an existing animation, so try merging it.
attemptMergeTransition(mActiveTransitions.get(0), active);
@@ -357,7 +387,7 @@
}
void playTransition(@NonNull ActiveTransition active) {
- setupStartState(active.mInfo, active.mStartT, active.mFinishT);
+ setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
// If a handler already chose to run this animation, try delegating to it first.
if (active.mHandler != null) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index fcd333f..c5b5b91 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -115,23 +115,20 @@
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
Region(0, 0, displayBounds.bounds.right,
- dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset)
+ dividerRegion.bounds.top + WindowUtils.dockedStackDividerInset)
} else {
- Region(0, 0, dividerRegion.bounds.left,
- dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset)
+ Region(0, 0, dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
+ displayBounds.bounds.bottom)
}
}
fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- Region(0,
- dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset)
+ Region(0, dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.bounds.right, displayBounds.bounds.bottom)
} else {
- Region(dividerRegion.bounds.right, 0,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset)
+ Region(dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, 0,
+ displayBounds.bounds.right, displayBounds.bounds.bottom)
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 1f9ff4ab..b5198bb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -18,7 +18,6 @@
import android.os.SystemClock
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +26,8 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import org.junit.After
import org.junit.Before
import org.junit.FixMethodOrder
@@ -49,7 +50,6 @@
class AppPairsTestCannotPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -64,21 +64,15 @@
}
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 1) {
- // Not support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, -1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@FlakyTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index 1e3595c..f2a375b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -18,7 +18,6 @@
import android.os.SystemClock
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +26,8 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import org.junit.After
import org.junit.Before
import org.junit.FixMethodOrder
@@ -49,7 +50,6 @@
class AppPairsTestSupportPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -64,21 +64,15 @@
}
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 0) {
- // Support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, 1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@FlakyTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 741773e..1935bb9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -20,11 +20,9 @@
import android.content.Context
import android.platform.test.annotations.Presubmit
import android.system.helpers.ActivityHelper
-import android.util.Log
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -41,10 +39,14 @@
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.BaseAppHelper
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.testapp.Components
+import org.junit.After
+import org.junit.Before
import org.junit.Test
-import java.io.IOException
abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -62,6 +64,21 @@
protected var primaryTaskId = ""
protected var secondaryTaskId = ""
protected var nonResizeableTaskId = ""
+ private var prevDevEnableNonResizableMultiWindow = 0
+
+ @Before
+ open fun setup() {
+ prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
+ if (prevDevEnableNonResizableMultiWindow != 0) {
+ // Turn off the development option
+ setDevEnableNonResizableMultiWindow(context, 0)
+ }
+ }
+
+ @After
+ open fun teardown() {
+ setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow)
+ }
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -117,11 +134,7 @@
}
internal fun executeShellCommand(cmd: String) {
- try {
- SystemUtil.runShellCommand(instrumentation, cmd)
- } catch (e: IOException) {
- Log.d("AppPairsTest", "executeShellCommand error! $e")
- }
+ BaseAppHelper.executeShellCommand(instrumentation, cmd)
}
internal fun composePairsCommand(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index 006b569..4fe69ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -21,11 +21,14 @@
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.util.Log
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
+import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.traces.parser.toWindowName
+import java.io.IOException
abstract class BaseAppHelper(
instrumentation: Instrumentation,
@@ -56,5 +59,13 @@
companion object {
private const val APP_CLOSE_WAIT_TIME_MS = 3_000L
+
+ fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
+ try {
+ SystemUtil.runShellCommand(instrumentation, cmd)
+ } catch (e: IOException) {
+ Log.e("BaseAppHelper", "executeShellCommand error! $e")
+ }
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
new file mode 100644
index 0000000..7f99e62
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.content.Context
+import android.provider.Settings
+
+class MultiWindowHelper(
+ instrumentation: Instrumentation,
+ activityLabel: String,
+ componentsInfo: ComponentName
+) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
+
+ companion object {
+ fun getDevEnableNonResizableMultiWindow(context: Context): Int =
+ Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+
+ fun setDevEnableNonResizableMultiWindow(context: Context, configValue: Int) =
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, configValue)
+
+ fun setSupportsNonResizableMultiWindow(instrumentation: Instrumentation, configValue: Int) =
+ executeShellCommand(
+ instrumentation,
+ createConfigSupportsNonResizableMultiWindowCommand(configValue))
+
+ fun resetMultiWindowConfig(instrumentation: Instrumentation) =
+ executeShellCommand(instrumentation, resetMultiWindowConfigCommand)
+
+ private fun createConfigSupportsNonResizableMultiWindowCommand(configValue: Int): String =
+ "wm set-multi-window-config --supportsNonResizable $configValue"
+
+ private const val resetMultiWindowConfigCommand: String = "wm reset-multi-window-config"
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 91d51de..dbbbcd2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -18,7 +18,6 @@
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.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -64,7 +63,7 @@
splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, *HOME_WINDOW_TITLE)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisible() =
testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index f975ed9..3ae6cfa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -18,7 +18,6 @@
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.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -66,13 +65,13 @@
secondaryApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisible() =
testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
splitScreenApp.defaultWindowName)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun dockedStackSecondaryBoundsIsVisible() =
testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
@@ -80,7 +79,6 @@
@Presubmit
@Test
- // b/169271943
fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible()
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index 04f97c8..c18c122 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +26,8 @@
import com.android.server.wm.flicker.helpers.canSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Assert
@@ -51,7 +52,6 @@
class EnterSplitScreenNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -76,21 +76,15 @@
splitScreenApp.defaultWindowName)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 1) {
- // Not support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, -1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index 2832bb4..d5b9a13 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +26,8 @@
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Before
@@ -50,7 +51,6 @@
class EnterSplitScreenSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -73,21 +73,15 @@
splitScreenApp.defaultWindowName)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow != 1) {
- // Support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, 1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index 32afd19..612018e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -32,6 +31,8 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Before
@@ -53,7 +54,6 @@
class LegacySplitScreenFromIntentNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -77,21 +77,15 @@
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 1) {
- // Not support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, -1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index af30758..65062f9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -30,6 +29,8 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Before
@@ -51,7 +52,6 @@
class LegacySplitScreenFromIntentSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -75,21 +75,15 @@
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 0) {
- // Support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, 1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 8c62758..3720787 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -33,6 +32,8 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Before
@@ -54,7 +55,6 @@
class LegacySplitScreenFromRecentNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -78,21 +78,15 @@
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 1) {
- // Not support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, -1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index 5b48f8a..61ebcd2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -31,6 +30,8 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Before
@@ -52,7 +53,6 @@
class LegacySplitScreenFromRecentSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -76,21 +76,15 @@
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 0) {
- // Support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, 1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 8684ba5..e8d4d1e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -32,7 +32,11 @@
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.After
+import org.junit.Before
import org.junit.Test
abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) {
@@ -44,6 +48,21 @@
protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
.launcherStrategy.supportedLauncherPackage
+ private var prevDevEnableNonResizableMultiWindow = 0
+
+ @Before
+ open fun setup() {
+ prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
+ if (prevDevEnableNonResizableMultiWindow != 0) {
+ // Turn off the development option
+ setDevEnableNonResizableMultiWindow(context, 0)
+ }
+ }
+
+ @After
+ open fun teardown() {
+ setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow)
+ }
/**
* List of windows that are ignored when verifying that visible elements appear on 2
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 4a59c62..0b4cb0f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -62,11 +62,11 @@
}
}
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisible() =
testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 834821b..8909e97 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -62,11 +62,11 @@
}
}
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisible() = testSpec.dockedStackPrimaryBoundsIsVisible(
testSpec.config.startRotation, splitScreenApp.defaultWindowName)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 8cf1990..e850b60 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -65,17 +65,17 @@
}
}
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisible() =
testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
splitScreenApp.defaultWindowName)
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
fun dockedStackSecondaryBoundsIsVisible() =
testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index db709a0..cae2338 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -70,17 +70,17 @@
}
}
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisible() =
testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
splitScreenApp.defaultWindowName)
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
fun dockedStackSecondaryBoundsIsVisible() =
testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
index d21183e..e73d9aa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
@@ -18,9 +18,12 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -43,7 +46,11 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-/** Tests for {@link AppPair} */
+/**
+ * Tests for {@link AppPair}
+ * Build/Install/Run:
+ * atest WMShellUnitTests:AppPairTests
+ */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AppPairTests extends ShellTestCase {
@@ -63,6 +70,7 @@
mTaskOrganizer,
mSyncQueue,
mDisplayController);
+ spyOn(mController);
}
@After
@@ -97,4 +105,19 @@
assertThat(pair.contains(task1.taskId)).isFalse();
assertThat(pair.contains(task2.taskId)).isFalse();
}
+
+ @Test
+ @UiThreadTest
+ public void testOnTaskInfoChanged_notSupportsMultiWindow() {
+ final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
+ final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
+
+ final AppPair pair = mController.pairInner(task1, task2);
+ assertThat(pair.contains(task1.taskId)).isTrue();
+ assertThat(pair.contains(task2.taskId)).isTrue();
+
+ task1.supportsMultiWindow = false;
+ pair.onTaskInfoChanged(task1);
+ verify(mController).unpair(pair.getRootTaskId());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 7b0e6b9..952dc31 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -35,6 +35,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayImeController;
@@ -48,9 +49,10 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SplitLayoutTests extends ShellTestCase {
- @Mock SplitLayout.LayoutChangeListener mLayoutChangeListener;
+ @Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler;
@Mock SurfaceControl mRootLeash;
@Mock DisplayImeController mDisplayImeController;
+ @Mock ShellTaskOrganizer mTaskOrganizer;
private SplitLayout mSplitLayout;
@Before
@@ -60,9 +62,10 @@
"TestSplitLayout",
mContext,
getConfiguration(false),
- mLayoutChangeListener,
+ mSplitLayoutHandler,
b -> b.setParent(mRootLeash),
- mDisplayImeController);
+ mDisplayImeController,
+ mTaskOrganizer);
}
@Test
@@ -76,19 +79,19 @@
@Test
public void testUpdateDivideBounds() {
mSplitLayout.updateDivideBounds(anyInt());
- verify(mLayoutChangeListener).onBoundsChanging(any(SplitLayout.class));
+ verify(mSplitLayoutHandler).onBoundsChanging(any(SplitLayout.class));
}
@Test
public void testSetDividePosition() {
mSplitLayout.setDividePosition(anyInt());
- verify(mLayoutChangeListener).onBoundsChanged(any(SplitLayout.class));
+ verify(mSplitLayoutHandler).onBoundsChanged(any(SplitLayout.class));
}
@Test
public void testOnDoubleTappedDivider() {
mSplitLayout.onDoubleTappedDivider();
- verify(mLayoutChangeListener).onDoubleTappedDivider();
+ verify(mSplitLayoutHandler).onDoubleTappedDivider();
}
@Test
@@ -98,11 +101,11 @@
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
- verify(mLayoutChangeListener).onSnappedToDismiss(eq(false));
+ verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false));
snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
- verify(mLayoutChangeListener).onSnappedToDismiss(eq(true));
+ verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true));
}
private static Configuration getConfiguration(boolean isLandscape) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
index 86d0d82..698315a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
@@ -29,7 +29,6 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayImeController;
import org.junit.Before;
import org.junit.Test;
@@ -43,7 +42,6 @@
public class SplitWindowManagerTests extends ShellTestCase {
@Mock SurfaceControl mSurfaceControl;
@Mock SplitLayout mSplitLayout;
- @Mock DisplayImeController mDisplayImeController;
private SplitWindowManager mSplitWindowManager;
@Before
@@ -52,7 +50,7 @@
final Configuration configuration = new Configuration();
configuration.setToDefaults();
mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration,
- b -> b.setParent(mSurfaceControl), mDisplayImeController);
+ b -> b.setParent(mSurfaceControl));
when(mSplitLayout.getDividerBounds()).thenReturn(
new Rect(0, 0, configuration.windowConfiguration.getBounds().width(),
configuration.windowConfiguration.getBounds().height()));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 2f2bbba..ba73d55 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -24,13 +24,13 @@
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -206,7 +206,7 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
}
@Test
@@ -218,12 +218,12 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -235,12 +235,12 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -252,7 +252,7 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
}
@Test
@@ -264,7 +264,7 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
}
@Test
@@ -277,13 +277,13 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -296,13 +296,13 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 99342f0..1852279 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -28,7 +28,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -79,8 +78,6 @@
@Mock
OneHandedTutorialHandler mMockTutorialHandler;
@Mock
- OneHandedGestureHandler mMockGestureHandler;
- @Mock
OneHandedSettingsUtil mMockSettingsUitl;
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@@ -131,7 +128,6 @@
mMockDisplayAreaOrganizer,
mMockTouchHandler,
mMockTutorialHandler,
- mMockGestureHandler,
mMockSettingsUitl,
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
@@ -179,7 +175,6 @@
@Test
public void testRegisterTransitionCallbackAfterInit() {
verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTouchHandler);
- verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockGestureHandler);
verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTutorialHandler);
}
@@ -205,7 +200,6 @@
mSpiedOneHandedController.setOneHandedEnabled(true);
verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
- verify(mMockGestureHandler, atLeastOnce()).onGestureEnabled(anyBoolean());
}
@Test
@@ -213,7 +207,6 @@
mSpiedOneHandedController.setSwipeToNotificationEnabled(mDefaultSwipeToNotificationEnabled);
verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
- verify(mMockGestureHandler, atLeastOnce()).onGestureEnabled(anyBoolean());
}
@Test
@@ -340,30 +333,6 @@
}
@Test
- public void testDisabled3ButtonGestureWhenKeyguardOn() {
- final boolean isOneHandedEnabled = true;
- final boolean isLockWhenKeyguardOn = true;
- final boolean isEnabledWhenKeyguardOn = false;
- mSpiedOneHandedController.setOneHandedEnabled(isOneHandedEnabled);
- mSpiedOneHandedController.setLockedDisabled(isLockWhenKeyguardOn, isEnabledWhenKeyguardOn);
-
- verify(mMockGestureHandler).onGestureEnabled(isEnabledWhenKeyguardOn);
- }
-
- @Test
- public void testEnabled3ButtonGestureWhenKeyguardGoingAway() {
- final boolean isOneHandedEnabled = true;
- final boolean isLockWhenKeyguardOn = false;
- final boolean isEnabledWhenKeyguardOn = false;
- mSpiedOneHandedController.setOneHandedEnabled(isOneHandedEnabled);
- reset(mMockGestureHandler);
-
- mSpiedOneHandedController.setLockedDisabled(isLockWhenKeyguardOn, isEnabledWhenKeyguardOn);
-
- verify(mMockGestureHandler).onGestureEnabled(isOneHandedEnabled);
- }
-
- @Test
public void testStateActive_shortcutRequestActivate_skipActions() {
when(mSpiedTransitionState.getState()).thenReturn(STATE_ACTIVE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
@@ -429,4 +398,14 @@
verify(mSpiedOneHandedController, never()).startOneHanded();
verify(mSpiedOneHandedController, never()).stopOneHanded();
}
+
+ @Test
+ public void testControllerInit_tutorialAddStateChangeListener() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(true);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false);
+
+ verify(mSpiedTransitionState).addSListeners(mMockTutorialHandler);
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
deleted file mode 100644
index 5d82a70..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
+++ /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.wm.shell.onehanded;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.view.Surface;
-import android.view.ViewConfiguration;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.ShellExecutor;
-
-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 OneHandedGestureHandlerTest extends OneHandedTestCase {
- OneHandedGestureHandler mGestureHandler;
- DisplayLayout mDisplayLayout;
- @Mock
- DisplayLayout mMockDisplayLayout;
- @Mock
- ShellExecutor mMockShellMainExecutor;
-
- @Before
- public void setUp() {
- final int mockNavBarHeight = 100;
- MockitoAnnotations.initMocks(this);
- mDisplayLayout = new DisplayLayout(mContext, mContext.getDisplay());
- mGestureHandler = new OneHandedGestureHandler(mContext, mDisplayLayout,
- ViewConfiguration.get(mTestContext), mMockShellMainExecutor);
- when(mMockDisplayLayout.navBarFrameHeight()).thenReturn(mockNavBarHeight);
- }
-
- @Test
- public void testSetGestureEventListener() {
- OneHandedGestureHandler.OneHandedGestureEventCallback callback =
- new OneHandedGestureHandler.OneHandedGestureEventCallback() {
- @Override
- public void onStart() {}
-
- @Override
- public void onStop() {}
- };
-
- mGestureHandler.setGestureEventListener(callback);
- assertThat(mGestureHandler.mGestureEventCallback).isEqualTo(callback);
- }
-
- @Test
- public void testOneHandedDisabled_shouldDisposeInputChannel() {
- mGestureHandler.onGestureEnabled(false);
-
- assertThat(mGestureHandler.mInputMonitor).isNull();
- assertThat(mGestureHandler.mInputEventReceiver).isNull();
- }
-
- @Test
- public void testChangeNavBarToNon3Button_shouldDisposeInputChannel() {
- mGestureHandler.onGestureEnabled(true);
- mGestureHandler.onThreeButtonModeEnabled(false);
-
- assertThat(mGestureHandler.mInputMonitor).isNull();
- assertThat(mGestureHandler.mInputEventReceiver).isNull();
- }
-
- @Test
- public void testOnlyHandleGestureInPortraitMode() {
- mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
- mGestureHandler.onGestureEnabled(true);
- mGestureHandler.onRotateDisplay(mDisplayLayout);
-
- assertThat(mGestureHandler.mInputMonitor).isNull();
- assertThat(mGestureHandler.mInputEventReceiver).isNull();
- }
-
- @Test
- public void testRotation90ShouldNotRegisterEventReceiver() throws InterruptedException {
- mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
- mGestureHandler.onGestureEnabled(true);
- mGestureHandler.onRotateDisplay(mDisplayLayout);
-
- verify(mMockShellMainExecutor, never()).executeBlocking(any());
- }
-
- @Test
- public void testRotation180ShouldNotRegisterEventReceiver() throws InterruptedException {
- mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
- mGestureHandler.onGestureEnabled(true);
- mGestureHandler.onRotateDisplay(mDisplayLayout);
-
- verify(mMockShellMainExecutor, never()).executeBlocking(any());
- }
-
- @Test
- public void testRotation270ShouldNotRegisterEventReceiver() throws InterruptedException {
- mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
- mGestureHandler.onGestureEnabled(true);
- mGestureHandler.onRotateDisplay(mDisplayLayout);
-
- verify(mMockShellMainExecutor, never()).executeBlocking(any());
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index 89aae65..e61f061 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -74,8 +74,6 @@
@Mock
OneHandedTutorialHandler mMockTutorialHandler;
@Mock
- OneHandedGestureHandler mMockGestureHandler;
- @Mock
OneHandedSettingsUtil mMockSettingsUitl;
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@@ -126,7 +124,6 @@
mMockDisplayAreaOrganizer,
mMockTouchHandler,
mMockTutorialHandler,
- mMockGestureHandler,
mMockSettingsUitl,
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index b82a8ca..1bc2a08 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -16,19 +16,26 @@
package com.android.wm.shell.onehanded;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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.om.IOverlayManager;
-import android.os.Handler;
import android.testing.AndroidTestingRunner;
-import android.util.ArrayMap;
+import android.view.Display;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TaskStackListenerImpl;
import org.junit.Before;
import org.junit.Test;
@@ -39,65 +46,94 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
+ Display mDisplay;
+ DisplayLayout mDisplayLayout;
OneHandedTimeoutHandler mTimeoutHandler;
- OneHandedController mOneHandedController;
OneHandedState mSpiedTransitionState;
+ OneHandedTutorialHandler mSpiedTutorialHandler;
@Mock
- OneHandedGestureHandler mMockGestureHandler;
- @Mock
- OneHandedTouchHandler mMockTouchHandler;
- @Mock
- OneHandedTutorialHandler mMockTutorialHandler;
- @Mock
- DisplayController mMockDisplayController;
- @Mock
- OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
- @Mock
- OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
- @Mock
- IOverlayManager mMockOverlayManager;
- @Mock
- TaskStackListenerImpl mMockTaskStackListener;
- @Mock
ShellExecutor mMockShellMainExecutor;
@Mock
- Handler mMockShellMainHandler;
- @Mock
- OneHandedUiEventLogger mMockUiEventLogger;
- @Mock
OneHandedSettingsUtil mMockSettingsUtil;
@Mock
- OneHandedAccessibilityUtil mMockAccessibilityUtil;
+ WindowManager mMockWindowManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
- mSpiedTransitionState = new OneHandedState();
+ when(mMockSettingsUtil.getTutorialShownCounts(any(), anyInt())).thenReturn(0);
- when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
- mOneHandedController = new OneHandedController(
- mContext,
- mMockDisplayController,
- mMockBackgroundOrganizer,
- mMockDisplayAreaOrganizer,
- mMockTouchHandler,
- mMockTutorialHandler,
- mMockGestureHandler,
- mMockSettingsUtil,
- mMockAccessibilityUtil,
- mTimeoutHandler,
- mSpiedTransitionState,
- mMockUiEventLogger,
- mMockOverlayManager,
- mMockTaskStackListener,
- mMockShellMainExecutor,
- mMockShellMainHandler);
+ mDisplay = mContext.getDisplay();
+ mDisplayLayout = new DisplayLayout(mContext, mDisplay);
+ mSpiedTransitionState = spy(new OneHandedState());
+ mSpiedTutorialHandler = spy(
+ new OneHandedTutorialHandler(mContext, mDisplayLayout, mMockWindowManager,
+ mMockSettingsUtil, mMockShellMainExecutor));
+ mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
}
@Test
- public void testRegisterForDisplayAreaOrganizer() {
- verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTutorialHandler);
+ public void testDefaultZeroShownCounts_canShowTutorial() {
+ assertThat(mSpiedTutorialHandler.canShowTutorial()).isTrue();
+ verify(mMockShellMainExecutor, never()).execute(any());
+ }
+
+ @Test
+ public void testDefaultZeroShownCounts_doNotAttachWindow() {
+ verify(mMockShellMainExecutor, never()).execute(any());
+ }
+
+ @Test
+ public void testOnStateChangedEntering_createViewAndAttachToWindow() {
+ when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
+ try {
+ mSpiedTutorialHandler.onStateChanged(STATE_ENTERING);
+ } catch (ClassCastException e) {
+ // no-op, just assert createViewAndAttachToWindow() to be called
+ }
+
+ verify(mSpiedTutorialHandler).createViewAndAttachToWindow(any());
+ }
+
+ @Test
+ public void testOnStateChangedNone_removeViewAndAttachToWindow() {
+ when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
+ try {
+ mSpiedTutorialHandler.onStateChanged(STATE_NONE);
+ } catch (ClassCastException e) {
+ // no-op, just assert removeTutorialFromWindowManager() to be called
+ }
+
+ verify(mSpiedTutorialHandler).removeTutorialFromWindowManager(true);
+ }
+
+ @Test
+ public void testOnStateChangedNone_shouldNotAttachWindow() {
+ when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
+ try {
+ mSpiedTutorialHandler.onStateChanged(STATE_NONE);
+ } catch (ClassCastException e) {
+ // no-op, just assert setTutorialShownCountIncrement() never be called
+ }
+
+ verify(mSpiedTutorialHandler, never()).setTutorialShownCountIncrement();
+ }
+
+ @Test
+ public void testOnConfigurationChanged_shouldUpdateViewContent() {
+ when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
+ try {
+ mSpiedTutorialHandler.onStateChanged(STATE_ENTERING);
+ } catch (ClassCastException e) {
+ // no-op, set current state for test onConfigurationChanged()
+ }
+ try {
+ mSpiedTutorialHandler.onConfigurationChanged();
+ } catch (ClassCastException e) {
+ // no-op, just assert removeTutorialFromWindowManager() be called
+ }
+
+ verify(mSpiedTutorialHandler).removeTutorialFromWindowManager(false);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
index 702e894..1bb5fd1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
@@ -22,6 +22,7 @@
import android.app.ActivityManager;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -36,7 +37,6 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
/** Tests for {@link MainStage} */
@SmallTest
@@ -47,14 +47,16 @@
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private ActivityManager.RunningTaskInfo mRootTaskInfo;
@Mock private SurfaceControl mRootLeash;
- @Spy private WindowContainerTransaction mWct;
+ private WindowContainerTransaction mWct = new WindowContainerTransaction();
+ private SurfaceSession mSurfaceSession = new SurfaceSession();
private MainStage mMainStage;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
- mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue);
+ mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
+ mSurfaceSession);
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index 01888b7..56a0056 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -26,6 +26,7 @@
import android.app.ActivityManager;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -52,13 +53,15 @@
@Mock private ActivityManager.RunningTaskInfo mRootTask;
@Mock private SurfaceControl mRootLeash;
@Spy private WindowContainerTransaction mWct;
+ private SurfaceSession mSurfaceSession = new SurfaceSession();
private SideStage mSideStage;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build();
- mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue);
+ mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
+ mSurfaceSession);
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 08ac2a6..aca80f3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -43,6 +43,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
import android.window.TransitionInfo;
@@ -80,6 +81,7 @@
@Mock private DisplayImeController mDisplayImeController;
@Mock private TransactionPool mTransactionPool;
@Mock private Transitions mTransitions;
+ @Mock private SurfaceSession mSurfaceSession;
private SplitLayout mSplitLayout;
private MainStage mMainStage;
private SideStage mSideStage;
@@ -98,10 +100,10 @@
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
mSplitLayout = SplitTestUtils.createMockSplitLayout();
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue);
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue);
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 924e946..06b0868 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -18,7 +18,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -73,7 +73,7 @@
public void testMoveToSideStage() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
- mStageCoordinator.moveToSideStage(task, STAGE_POSITION_BOTTOM_OR_RIGHT);
+ mStageCoordinator.moveToSideStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT);
verify(mMainStage).activate(any(Rect.class), any(WindowContainerTransaction.class));
verify(mSideStage).addTask(eq(task), any(Rect.class),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index c66e073..90b5b37 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -27,6 +27,7 @@
import android.app.ActivityManager;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -38,16 +39,24 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-/** Tests for {@link StageTaskListener} */
+/**
+ * Tests for {@link StageTaskListener}
+ * Build/Install/Run:
+ * atest WMShellUnitTests:StageTaskListenerTests
+ */
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class StageTaskListenerTests {
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
@Mock private SyncTransactionQueue mSyncQueue;
+ @Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
+ private SurfaceSession mSurfaceSession = new SurfaceSession();
private ActivityManager.RunningTaskInfo mRootTask;
private StageTaskListener mStageTaskListener;
@@ -58,13 +67,24 @@
mTaskOrganizer,
DEFAULT_DISPLAY,
mCallbacks,
- mSyncQueue);
+ mSyncQueue,
+ mSurfaceSession);
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootTask.parentTaskId = INVALID_TASK_ID;
mStageTaskListener.onTaskAppeared(mRootTask, new SurfaceControl());
}
@Test
+ public void testInitsDimLayer() {
+ verify(mSyncQueue).runInSync(mRunnableCaptor.capture());
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ mRunnableCaptor.getValue().runWithTransaction(t);
+ t.apply();
+
+ assertThat(mStageTaskListener.mDimLayer).isNotNull();
+ }
+
+ @Test
public void testRootTaskAppeared() {
assertThat(mStageTaskListener.mRootTaskInfo.taskId).isEqualTo(mRootTask.taskId);
verify(mCallbacks).onRootTaskAppeared();
@@ -101,4 +121,14 @@
mStageTaskListener.onTaskVanished(mRootTask);
verify(mCallbacks).onRootTaskVanished();
}
+
+ @Test
+ public void testTaskInfoChanged_notSupportsMultiWindow() {
+ final ActivityManager.RunningTaskInfo childTask =
+ new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
+ childTask.supportsMultiWindow = false;
+
+ mStageTaskListener.onTaskInfoChanged(childTask);
+ verify(mCallbacks).onNoLongerSupportMultiWindow();
+ }
}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 76366fc..26d8363 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -88,7 +88,7 @@
if (IsFabricatedOverlay(overlay_path)) {
// Fabricated overlays do not contain resource definitions. All of the overlay resource values
// are defined inline in the idmap.
- overlay_assets = EmptyAssetsProvider::Create();
+ overlay_assets = EmptyAssetsProvider::Create(overlay_path);
} else {
// The overlay should be an APK.
overlay_assets = ZipAssetsProvider::Create(overlay_path);
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 7e45f95..3a0153f 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -221,6 +221,16 @@
for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()),
iter->dynamic_ref_table->mAssignedPackageId);
+
+ // Add the alias resources to the dynamic reference table of every package group. Since
+ // staging aliases can only be defined by the framework package (which is not a shared
+ // library), the compile-time package id of the framework is the same across all packages
+ // that compile against the framework.
+ for (const auto& package : iter->packages_) {
+ for (const auto& entry : package.loaded_package_->GetAliasResourceIdMap()) {
+ iter2->dynamic_ref_table->addAlias(entry.first, entry.second);
+ }
+ }
}
}
}
@@ -602,6 +612,11 @@
result->entry = overlay_entry.GetInlineValue();
result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
result->cookie = id_map.cookie;
+
+ if (UNLIKELY(logging_enabled)) {
+ last_resolution_.steps.push_back(
+ Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, String8(), result->cookie});
+ }
continue;
}
@@ -630,7 +645,6 @@
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(
Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(),
- overlay_result->package_name,
overlay_result->cookie});
}
}
@@ -713,7 +727,6 @@
if (UNLIKELY(logging_enabled)) {
resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED,
this_config.toString(),
- &loaded_package->GetPackageName(),
cookie});
}
continue;
@@ -731,7 +744,6 @@
if (UNLIKELY(logging_enabled)) {
resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
this_config.toString(),
- &loaded_package->GetPackageName(),
cookie});
}
continue;
@@ -746,7 +758,6 @@
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
this_config.toString(),
- &loaded_package->GetPackageName(),
cookie});
}
@@ -829,18 +840,18 @@
}
std::stringstream log_stream;
- log_stream << base::StringPrintf("Resolution for 0x%08x ", resid)
- << resource_name_string
- << "\n\tFor config -"
- << configuration_.toString();
+ log_stream << base::StringPrintf("Resolution for 0x%08x %s\n"
+ "\tFor config - %s", resid, resource_name_string.c_str(),
+ configuration_.toString().c_str());
for (const Resolution::Step& step : last_resolution_.steps) {
const static std::unordered_map<Resolution::Step::Type, const char*> kStepStrings = {
- {Resolution::Step::Type::INITIAL, "Found initial"},
- {Resolution::Step::Type::BETTER_MATCH, "Found better"},
- {Resolution::Step::Type::OVERLAID, "Overlaid"},
- {Resolution::Step::Type::SKIPPED, "Skipped"},
- {Resolution::Step::Type::NO_ENTRY, "No entry"}
+ {Resolution::Step::Type::INITIAL, "Found initial"},
+ {Resolution::Step::Type::BETTER_MATCH, "Found better"},
+ {Resolution::Step::Type::OVERLAID, "Overlaid"},
+ {Resolution::Step::Type::OVERLAID_INLINE, "Overlaid inline"},
+ {Resolution::Step::Type::SKIPPED, "Skipped"},
+ {Resolution::Step::Type::NO_ENTRY, "No entry"}
};
const auto prefix = kStepStrings.find(step.type);
@@ -848,10 +859,9 @@
continue;
}
- log_stream << "\n\t" << prefix->second << ": " << *step.package_name << " ("
- << apk_assets_[step.cookie]->GetDebugName() << ")";
+ log_stream << "\n\t" << prefix->second << ": " << apk_assets_[step.cookie]->GetDebugName();
if (!step.config_name.isEmpty()) {
- log_stream << " -" << step.config_name;
+ log_stream << " - " << step.config_name;
}
}
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index 0aaf0b3..6c7a253 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -386,8 +386,15 @@
return primary_->IsUpToDate() && secondary_->IsUpToDate();
}
+EmptyAssetsProvider::EmptyAssetsProvider(std::optional<std::string>&& path) :
+ path_(std::move(path)) {}
+
std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create() {
- return std::make_unique<EmptyAssetsProvider>();
+ return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider({}));
+}
+
+std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create(const std::string& path) {
+ return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider(path));
}
std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* path */,
@@ -406,10 +413,16 @@
}
std::optional<std::string_view> EmptyAssetsProvider::GetPath() const {
+ if (path_.has_value()) {
+ return *path_;
+ }
return {};
}
const std::string& EmptyAssetsProvider::GetDebugName() const {
+ if (path_.has_value()) {
+ return *path_;
+ }
const static std::string kEmpty = kEmptyDebugString;
return kEmpty;
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index cb620cc..d17c328 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -41,6 +41,7 @@
namespace android {
+constexpr const static int kFrameworkPackageId = 0x01;
constexpr const static int kAppPackageId = 0x7f;
namespace {
@@ -675,6 +676,42 @@
}
} break;
+ case RES_TABLE_STAGED_ALIAS_TYPE: {
+ if (loaded_package->package_id_ != kFrameworkPackageId) {
+ LOG(WARNING) << "Alias chunk ignored for non-framework package '"
+ << loaded_package->package_name_ << "'";
+ break;
+ }
+
+ std::unordered_set<uint32_t> finalized_ids;
+ const auto lib_alias = child_chunk.header<ResTable_staged_alias_header>();
+ if (!lib_alias) {
+ return {};
+ }
+ const auto entry_begin = child_chunk.data_ptr().convert<ResTable_staged_alias_entry>();
+ const auto entry_end = entry_begin + dtohl(lib_alias->count);
+ for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
+ if (!entry_iter) {
+ return {};
+ }
+ auto finalized_id = dtohl(entry_iter->finalizedResId);
+ if (!finalized_ids.insert(finalized_id).second) {
+ LOG(ERROR) << StringPrintf("Repeated finalized resource id '%08x' in staged aliases.",
+ finalized_id);
+ return {};
+ }
+
+ auto staged_id = dtohl(entry_iter->stagedResId);
+ auto [_, success] = loaded_package->alias_id_map_.insert(std::make_pair(staged_id,
+ finalized_id));
+ if (!success) {
+ LOG(ERROR) << StringPrintf("Repeated staged resource id '%08x' in staged aliases.",
+ staged_id);
+ return {};
+ }
+ }
+ } break;
+
default:
LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
break;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 30500ab..cae2d0b 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -7079,6 +7079,10 @@
mLookupTable[buildPackageId] = runtimePackageId;
}
+void DynamicRefTable::addAlias(uint32_t stagedId, uint32_t finalizedId) {
+ mAliasId[stagedId] = finalizedId;
+}
+
status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
@@ -7088,8 +7092,16 @@
return NO_ERROR;
}
- if (packageId == APP_PACKAGE_ID && !mAppAsLib) {
- // No lookup needs to be done, app package IDs are absolute.
+ auto alias_id = mAliasId.find(res);
+ if (alias_id != mAliasId.end()) {
+ // Rewrite the resource id to its alias resource id. Since the alias resource id is a
+ // compile-time id, it still needs to be resolved further.
+ res = alias_id->second;
+ }
+
+ if (packageId == SYS_PACKAGE_ID || (packageId == APP_PACKAGE_ID && !mAppAsLib)) {
+ // No lookup needs to be done, app and framework package IDs are absolute.
+ *resId = res;
return NO_ERROR;
}
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 10666ad..df3abf6 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -458,6 +458,7 @@
INITIAL,
BETTER_MATCH,
OVERLAID,
+ OVERLAID_INLINE,
SKIPPED,
NO_ENTRY,
};
@@ -468,10 +469,6 @@
// Built name of configuration for this step.
String8 config_name;
- // Marks the package name of the better resource found in this step.
- const std::string* package_name;
-
- //
ApkAssetsCookie cookie = kInvalidCookie;
};
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index 63bbdcc..ec51c65 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -176,6 +176,7 @@
// Does not provide any assets.
struct EmptyAssetsProvider : public AssetsProvider {
static std::unique_ptr<AssetsProvider> Create();
+ static std::unique_ptr<AssetsProvider> Create(const std::string& path);
bool ForEachFile(const std::string& path,
const std::function<void(const StringPiece&, FileType)>& f) const override;
@@ -188,6 +189,10 @@
protected:
std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
bool* file_exists) const override;
+
+ private:
+ explicit EmptyAssetsProvider(std::optional<std::string>&& path);
+ std::optional<std::string> path_;
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 3b222c5..9bbdede 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -17,6 +17,7 @@
#ifndef LOADEDARSC_H_
#define LOADEDARSC_H_
+#include <map>
#include <memory>
#include <set>
#include <vector>
@@ -171,51 +172,51 @@
incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
// Returns the string pool where type names are stored.
- inline const ResStringPool* GetTypeStringPool() const {
+ const ResStringPool* GetTypeStringPool() const {
return &type_string_pool_;
}
// Returns the string pool where the names of resource entries are stored.
- inline const ResStringPool* GetKeyStringPool() const {
+ const ResStringPool* GetKeyStringPool() const {
return &key_string_pool_;
}
- inline const std::string& GetPackageName() const {
+ const std::string& GetPackageName() const {
return package_name_;
}
- inline int GetPackageId() const {
+ int GetPackageId() const {
return package_id_;
}
// Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
- inline bool IsDynamic() const {
+ bool IsDynamic() const {
return (property_flags_ & PROPERTY_DYNAMIC) != 0;
}
// Returns true if this package is a Runtime Resource Overlay.
- inline bool IsOverlay() const {
+ bool IsOverlay() const {
return (property_flags_ & PROPERTY_OVERLAY) != 0;
}
// Returns true if this package originates from a system provided resource.
- inline bool IsSystem() const {
+ bool IsSystem() const {
return (property_flags_ & PROPERTY_SYSTEM) != 0;
}
// Returns true if this package is a custom loader and should behave like an overlay.
- inline bool IsCustomLoader() const {
+ bool IsCustomLoader() const {
return (property_flags_ & PROPERTY_LOADER) != 0;
}
- inline package_property_t GetPropertyFlags() const {
+ package_property_t GetPropertyFlags() const {
return property_flags_;
}
// Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
// package could have been assigned a different package ID than what this LoadedPackage was
// compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime.
- inline const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
+ const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
return dynamic_package_map_;
}
@@ -270,6 +271,10 @@
return overlayable_map_;
}
+ const std::map<uint32_t, uint32_t>& GetAliasResourceIdMap() const {
+ return alias_id_map_;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
@@ -287,6 +292,7 @@
ByteBucketArray<uint32_t> resource_ids_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
+ std::map<uint32_t, uint32_t> alias_id_map_;
// A map of overlayable name to actor
std::unordered_map<std::string, std::string> overlayable_map_;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 17c1404..3d66244 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -39,6 +39,7 @@
#include <android/configuration.h>
#include <array>
+#include <map>
#include <memory>
namespace android {
@@ -229,30 +230,31 @@
};
enum {
- RES_NULL_TYPE = 0x0000,
- RES_STRING_POOL_TYPE = 0x0001,
- RES_TABLE_TYPE = 0x0002,
- RES_XML_TYPE = 0x0003,
+ RES_NULL_TYPE = 0x0000,
+ RES_STRING_POOL_TYPE = 0x0001,
+ RES_TABLE_TYPE = 0x0002,
+ RES_XML_TYPE = 0x0003,
// Chunk types in RES_XML_TYPE
- RES_XML_FIRST_CHUNK_TYPE = 0x0100,
- RES_XML_START_NAMESPACE_TYPE= 0x0100,
- RES_XML_END_NAMESPACE_TYPE = 0x0101,
- RES_XML_START_ELEMENT_TYPE = 0x0102,
- RES_XML_END_ELEMENT_TYPE = 0x0103,
- RES_XML_CDATA_TYPE = 0x0104,
- RES_XML_LAST_CHUNK_TYPE = 0x017f,
+ RES_XML_FIRST_CHUNK_TYPE = 0x0100,
+ RES_XML_START_NAMESPACE_TYPE = 0x0100,
+ RES_XML_END_NAMESPACE_TYPE = 0x0101,
+ RES_XML_START_ELEMENT_TYPE = 0x0102,
+ RES_XML_END_ELEMENT_TYPE = 0x0103,
+ RES_XML_CDATA_TYPE = 0x0104,
+ RES_XML_LAST_CHUNK_TYPE = 0x017f,
// This contains a uint32_t array mapping strings in the string
// pool back to resource identifiers. It is optional.
- RES_XML_RESOURCE_MAP_TYPE = 0x0180,
+ RES_XML_RESOURCE_MAP_TYPE = 0x0180,
// Chunk types in RES_TABLE_TYPE
- RES_TABLE_PACKAGE_TYPE = 0x0200,
- RES_TABLE_TYPE_TYPE = 0x0201,
- RES_TABLE_TYPE_SPEC_TYPE = 0x0202,
- RES_TABLE_LIBRARY_TYPE = 0x0203,
- RES_TABLE_OVERLAYABLE_TYPE = 0x0204,
+ RES_TABLE_PACKAGE_TYPE = 0x0200,
+ RES_TABLE_TYPE_TYPE = 0x0201,
+ RES_TABLE_TYPE_SPEC_TYPE = 0x0202,
+ RES_TABLE_LIBRARY_TYPE = 0x0203,
+ RES_TABLE_OVERLAYABLE_TYPE = 0x0204,
RES_TABLE_OVERLAYABLE_POLICY_TYPE = 0x0205,
+ RES_TABLE_STAGED_ALIAS_TYPE = 0x0206,
};
/**
@@ -1639,6 +1641,29 @@
};
/**
+ * A map that allows rewriting staged (non-finalized) resource ids to their finalized counterparts.
+ */
+struct ResTable_staged_alias_header
+{
+ struct ResChunk_header header;
+
+ // The number of ResTable_staged_alias_entry that follow this header.
+ uint32_t count;
+};
+
+/**
+ * Maps the staged (non-finalized) resource id to its finalized resource id.
+ */
+struct ResTable_staged_alias_entry
+{
+ // The compile-time staged resource id to rewrite.
+ uint32_t stagedResId;
+
+ // The compile-time finalized resource id to which the staged resource id should be rewritten.
+ uint32_t finalizedResId;
+};
+
+/**
* Specifies the set of resources that are explicitly allowed to be overlaid by RROs.
*/
struct ResTable_overlayable_header
@@ -1751,6 +1776,8 @@
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
+ void addAlias(uint32_t stagedId, uint32_t finalizedId);
+
// Returns whether or not the value must be looked up.
bool requiresLookup(const Res_value* value) const;
@@ -1768,6 +1795,7 @@
uint8_t mLookupTable[256];
KeyedVector<String16, uint8_t> mEntries;
bool mAppAsLib;
+ std::map<uint32_t, uint32_t> mAliasId;
};
bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index e1c0fab7..3c4ee4e 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -765,7 +765,8 @@
auto result = assetmanager.GetLastResourceResolution();
EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
- "\tFor config -de\n\tFound initial: com.android.basic (basic/basic.apk)", result);
+ "\tFor config - de\n"
+ "\tFound initial: basic/basic.apk", result);
}
TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
@@ -784,9 +785,9 @@
auto result = assetmanager.GetLastResourceResolution();
EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
- "\tFor config -de\n"
- "\tFound initial: com.android.basic (basic/basic.apk)\n"
- "\tFound better: com.android.basic (basic/basic_de_fr.apk) -de", result);
+ "\tFor config - de\n"
+ "\tFound initial: basic/basic.apk\n"
+ "\tFound better: basic/basic_de_fr.apk - de", result);
}
TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0212309..c6ab8a2 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -583,6 +583,7 @@
"libhwui_defaults",
"android_graphics_apex",
"android_graphics_jni",
+ "linker_hugepage_aligned",
],
export_header_lib_headers: ["android_graphics_apex_headers"],
target: {
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index a164233..9db47c3 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -157,7 +157,7 @@
static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
if (in.isEmpty()) return;
SkRect temp(in);
- if (Properties::stretchEffectBehavior == StretchEffectBehavior::LinearScale) {
+ if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) {
const StretchEffect& stretch = props.layerProperties().getStretchEffect();
if (!stretch.isEmpty()) {
applyMatrix(stretch.makeLinearStretch(props.getWidth(), props.getHeight()), &temp);
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 9ed801b..f0995c4 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -137,10 +137,6 @@
targetCpuTimePercentage = base::GetIntProperty(PROPERTY_TARGET_CPU_TIME_PERCENTAGE, 70);
if (targetCpuTimePercentage <= 0 || targetCpuTimePercentage > 100) targetCpuTimePercentage = 70;
- int stretchType = base::GetIntProperty(PROPERTY_STRETCH_EFFECT_TYPE, 0);
- stretchType = std::clamp(stretchType, 0, static_cast<int>(StretchEffectBehavior::LinearScale));
- stretchEffectBehavior = static_cast<StretchEffectBehavior>(stretchType);
-
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 9d2b617..f5fd003 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -171,8 +171,6 @@
*/
#define PROPERTY_TARGET_CPU_TIME_PERCENTAGE "debug.hwui.target_cpu_time_percent"
-#define PROPERTY_STRETCH_EFFECT_TYPE "debug.hwui.stretch_mode"
-
/**
* Property for whether this is running in the emulator.
*/
@@ -200,9 +198,9 @@
enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };
enum class StretchEffectBehavior {
- ShaderHWUI, // Stretch shader in HWUI only, matrix scale in SF
- Shader, // Stretch shader in both HWUI and SF
- LinearScale // Linear stretch everywhere
+ ShaderHWUI, // Stretch shader in HWUI only, matrix scale in SF
+ Shader, // Stretch shader in both HWUI and SF
+ UniformScale // Uniform scale stretch everywhere
};
/**
@@ -278,9 +276,26 @@
static bool useHintManager;
static int targetCpuTimePercentage;
- static StretchEffectBehavior stretchEffectBehavior;
+ static StretchEffectBehavior getStretchEffectBehavior() {
+ return stretchEffectBehavior;
+ }
+
+ static void setIsHighEndGfx(bool isHighEndGfx) {
+ stretchEffectBehavior = isHighEndGfx ?
+ StretchEffectBehavior::ShaderHWUI :
+ StretchEffectBehavior::UniformScale;
+ }
+
+ /**
+ * Used for testing. Typical configuration of stretch behavior is done
+ * through setIsHighEndGfx
+ */
+ static void setStretchEffectBehavior(StretchEffectBehavior behavior) {
+ stretchEffectBehavior = behavior;
+ }
private:
+ static StretchEffectBehavior stretchEffectBehavior;
static ProfileType sProfileType;
static bool sDisableProfileBars;
static RenderPipelineType sRenderPipelineType;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 64abd94..ded7994 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -478,7 +478,7 @@
}
}
- if (Properties::stretchEffectBehavior == StretchEffectBehavior::LinearScale) {
+ if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) {
const StretchEffect& stretch = properties().layerProperties().getStretchEffect();
if (!stretch.isEmpty()) {
matrix.multiply(
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 5540e2d..cd622eb 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -553,7 +553,7 @@
bool promotedToLayer() const {
return mLayerProperties.mType == LayerType::None && fitsOnLayer() &&
(mComputedFields.mNeedLayerForFunctors || mLayerProperties.mImageFilter != nullptr ||
- !mLayerProperties.getStretchEffect().isEmpty() ||
+ mLayerProperties.getStretchEffect().requiresLayer() ||
(!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 &&
mPrimitiveFields.mHasOverlappingRendering));
}
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 979678d..d9b9e24 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -221,8 +221,30 @@
return sInstance;
}
+static void validateCallbacks(const WebViewFunctorCallbacks& callbacks) {
+ // TODO: Should we do a stack peek to see if this is really webview?
+ LOG_ALWAYS_FATAL_IF(callbacks.onSync == nullptr, "onSync is null");
+ LOG_ALWAYS_FATAL_IF(callbacks.onContextDestroyed == nullptr, "onContextDestroyed is null");
+ LOG_ALWAYS_FATAL_IF(callbacks.onDestroyed == nullptr, "onDestroyed is null");
+ LOG_ALWAYS_FATAL_IF(callbacks.removeOverlays == nullptr, "removeOverlays is null");
+ switch (auto mode = WebViewFunctor_queryPlatformRenderMode()) {
+ case RenderMode::OpenGL_ES:
+ LOG_ALWAYS_FATAL_IF(callbacks.gles.draw == nullptr, "gles.draw is null");
+ break;
+ case RenderMode::Vulkan:
+ LOG_ALWAYS_FATAL_IF(callbacks.vk.initialize == nullptr, "vk.initialize is null");
+ LOG_ALWAYS_FATAL_IF(callbacks.vk.draw == nullptr, "vk.draw is null");
+ LOG_ALWAYS_FATAL_IF(callbacks.vk.postDraw == nullptr, "vk.postDraw is null");
+ break;
+ default:
+ LOG_ALWAYS_FATAL("unknown platform mode? %d", (int)mode);
+ break;
+ }
+}
+
int WebViewFunctorManager::createFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
RenderMode functorMode) {
+ validateCallbacks(callbacks);
auto object = std::make_unique<WebViewFunctor>(data, callbacks, functorMode);
int id = object->id();
auto handle = object->createHandle();
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index 0599bfa..807fb75 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -94,13 +94,14 @@
float uStretchAffectedDist,
float uInverseStretchAffectedDist,
float distanceStretched,
- float interpolationStrength
+ float interpolationStrength,
+ float viewportDimension
) {
float offsetPos = inPos - reverseStretchDist;
float posBasedVariation = mix(
1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
float stretchIntensity = (-overscroll) * posBasedVariation;
- return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
+ return viewportDimension - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
}
// Prefer usage of return values over out parameters as it enables
@@ -113,54 +114,51 @@
float uInverseStretchAffectedDist,
float distanceStretched,
float distanceDiff,
- float interpolationStrength
+ float interpolationStrength,
+ float viewportDimension
) {
- float outPos = inPos;
if (overscroll > 0) {
- if (inPos <= uStretchAffectedDist) {
- outPos = computeOverscrollStart(
- inPos,
- overscroll,
- uStretchAffectedDist,
- uInverseStretchAffectedDist,
- distanceStretched,
- interpolationStrength
- );
- } else if (inPos >= distanceStretched) {
- outPos = distanceDiff + inPos;
- }
+ if (inPos <= uStretchAffectedDist) {
+ return computeOverscrollStart(
+ inPos,
+ overscroll,
+ uStretchAffectedDist,
+ uInverseStretchAffectedDist,
+ distanceStretched,
+ interpolationStrength
+ );
+ } else {
+ return distanceDiff + inPos;
}
- if (overscroll < 0) {
- float stretchAffectedDist = 1. - uStretchAffectedDist;
- if (inPos >= stretchAffectedDist) {
- outPos = computeOverscrollEnd(
- inPos,
- overscroll,
- stretchAffectedDist,
- uStretchAffectedDist,
- uInverseStretchAffectedDist,
- distanceStretched,
- interpolationStrength
- );
- } else if (inPos < stretchAffectedDist) {
- outPos = -distanceDiff + inPos;
- }
+ } else if (overscroll < 0) {
+ float stretchAffectedDist = viewportDimension - uStretchAffectedDist;
+ if (inPos >= stretchAffectedDist) {
+ return computeOverscrollEnd(
+ inPos,
+ overscroll,
+ stretchAffectedDist,
+ uStretchAffectedDist,
+ uInverseStretchAffectedDist,
+ distanceStretched,
+ interpolationStrength,
+ viewportDimension
+ );
+ } else {
+ return -distanceDiff + inPos;
}
- return outPos;
+ } else {
+ return inPos;
+ }
}
vec4 main(vec2 coord) {
- // Normalize SKSL pixel coordinate into a unit vector
- float inU = coord.x / viewportWidth;
- float inV = coord.y / viewportHeight;
+ float inU = coord.x;
+ float inV = coord.y;
float outU;
float outV;
- float stretchIntensity;
- // Add the normalized scroll position within scrolling list
+
inU += uScrollX;
inV += uScrollY;
- outU = inU;
- outV = inV;
outU = computeOverscroll(
inU,
uOverscrollX,
@@ -168,7 +166,8 @@
uInverseDistanceStretchedX,
uDistanceStretchedX,
uDistDiffX,
- uInterpolationStrength
+ uInterpolationStrength,
+ viewportWidth
);
outV = computeOverscroll(
inV,
@@ -177,15 +176,15 @@
uInverseDistanceStretchedY,
uDistanceStretchedY,
uDistDiffY,
- uInterpolationStrength
+ uInterpolationStrength,
+ viewportHeight
);
- coord.x = outU * viewportWidth;
- coord.y = outV * viewportHeight;
+ coord.x = outU;
+ coord.y = outV;
return sample(uContentTexture, coord);
})");
static const float ZERO = 0.f;
-static const float CONTENT_DISTANCE_STRETCHED = 1.f;
static const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
sk_sp<SkShader> StretchEffect::getShader(float width, float height,
@@ -196,12 +195,12 @@
float normOverScrollDistX = mStretchDirection.x();
float normOverScrollDistY = mStretchDirection.y();
- float distanceStretchedX = CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistX));
- float distanceStretchedY = CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistY));
- float inverseDistanceStretchedX = 1.f / CONTENT_DISTANCE_STRETCHED;
- float inverseDistanceStretchedY = 1.f / CONTENT_DISTANCE_STRETCHED;
- float diffX = distanceStretchedX - CONTENT_DISTANCE_STRETCHED;
- float diffY = distanceStretchedY - CONTENT_DISTANCE_STRETCHED;
+ float distanceStretchedX = width / (1 + abs(normOverScrollDistX));
+ float distanceStretchedY = height / (1 + abs(normOverScrollDistY));
+ float inverseDistanceStretchedX = 1.f / width;
+ float inverseDistanceStretchedY = 1.f / height;
+ float diffX = distanceStretchedX - width;
+ float diffY = distanceStretchedY - height;
if (mBuilder == nullptr) {
mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
@@ -210,8 +209,8 @@
mBuilder->child("uContentTexture") = snapshotImage->makeShader(
SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
- mBuilder->uniform("uStretchAffectedDistX").set(&CONTENT_DISTANCE_STRETCHED, 1);
- mBuilder->uniform("uStretchAffectedDistY").set(&CONTENT_DISTANCE_STRETCHED, 1);
+ mBuilder->uniform("uStretchAffectedDistX").set(&width, 1);
+ mBuilder->uniform("uStretchAffectedDistY").set(&height, 1);
mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
mBuilder->uniform("uInverseDistanceStretchedX").set(&inverseDistanceStretchedX, 1);
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index c49d53a..a92ef5b 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -16,6 +16,7 @@
#pragma once
+#include "Properties.h"
#include "utils/MathUtils.h"
#include <SkImage.h>
@@ -108,6 +109,11 @@
return matrix;
}
+ bool requiresLayer() const {
+ return !(isEmpty() ||
+ Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale);
+ }
+
private:
static sk_sp<SkRuntimeEffect> getStretchEffect();
mutable SkVector mStretchDirection{0, 0};
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index dd78d58..82bc5a1 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -324,6 +324,11 @@
Properties::defaultSdrWhitePoint = sdrWhitePoint;
}
+static void android_view_ThreadedRenderer_setIsHighEndGfx(JNIEnv* env, jobject clazz,
+ jboolean jIsHighEndGfx) {
+ Properties::setIsHighEndGfx(jIsHighEndGfx);
+}
+
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
@@ -795,6 +800,7 @@
{"nSetOpaque", "(JZ)V", (void*)android_view_ThreadedRenderer_setOpaque},
{"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode},
{"nSetSdrWhitePoint", "(JF)V", (void*)android_view_ThreadedRenderer_setSdrWhitePoint},
+ {"nSetIsHighEndGfx", "(Z)V", (void*)android_view_ThreadedRenderer_setIsHighEndGfx},
{"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame},
{"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy},
{"nRegisterAnimatingRenderNode", "(JJ)V",
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index cd0783f..002bd83 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -573,8 +573,8 @@
const RenderProperties& props = node.properties();
uirenderer::Rect bounds(props.getWidth(), props.getHeight());
- bool useStretchShader = Properties::stretchEffectBehavior !=
- StretchEffectBehavior::LinearScale;
+ bool useStretchShader =
+ Properties::getStretchEffectBehavior() != StretchEffectBehavior::UniformScale;
if (useStretchShader && info.stretchEffectCount) {
handleStretchEffect(info, bounds);
}
@@ -680,7 +680,8 @@
stretchTargetBounds(*effect, result.width, result.height,
childRelativeBounds,targetBounds);
- if (Properties::stretchEffectBehavior == StretchEffectBehavior::Shader) {
+ if (Properties::getStretchEffectBehavior() ==
+ StretchEffectBehavior::Shader) {
JNIEnv* env = jnienv();
jobject localref = env->NewLocalRef(mWeakRef);
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
index 10889e7..d173782 100644
--- a/libs/hwui/pipeline/skia/AnimatedDrawables.h
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -77,6 +77,7 @@
setUniform(effectBuilder, "in_radius", params.radius);
setUniform(effectBuilder, "in_progress", params.progress);
setUniform(effectBuilder, "in_turbulencePhase", params.turbulencePhase);
+ setUniform(effectBuilder, "in_noisePhase", params.turbulencePhase->value * 0.001);
SkRuntimeShaderBuilder::BuilderUniform uniform = effectBuilder.uniform("in_color");
if (uniform.fVar != nullptr) {
@@ -120,17 +121,24 @@
static constexpr float PI_ROTATE_LEFT = PI * -0.0078125;
static constexpr float SCALE = 1.5;
- static void setUniform(SkRuntimeShaderBuilder& effectBuilder, std::string name,
+ static void setUniform(SkRuntimeShaderBuilder& effectBuilder, const char* name,
sp<uirenderer::CanvasPropertyPrimitive> property) {
- SkRuntimeShaderBuilder::BuilderUniform uniform = effectBuilder.uniform(name.c_str());
+ SkRuntimeShaderBuilder::BuilderUniform uniform = effectBuilder.uniform(name);
if (uniform.fVar != nullptr) {
uniform = property->value;
}
}
- static void setUniform2f(SkRuntimeShaderBuilder& effectBuilder, std::string name, float a,
+ static void setUniform(SkRuntimeShaderBuilder& effectBuilder, const char* name, float value) {
+ SkRuntimeShaderBuilder::BuilderUniform uniform = effectBuilder.uniform(name);
+ if (uniform.fVar != nullptr) {
+ uniform = value;
+ }
+ }
+
+ static void setUniform2f(SkRuntimeShaderBuilder& effectBuilder, const char* name, float a,
float b) {
- SkRuntimeShaderBuilder::BuilderUniform uniform = effectBuilder.uniform(name.c_str());
+ SkRuntimeShaderBuilder::BuilderUniform uniform = effectBuilder.uniform(name);
if (uniform.fVar != nullptr) {
uniform = SkV2{a, b};
}
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 57cdde2..c8247e7 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -171,16 +171,11 @@
displayList->mProjectedOutline = nullptr;
}
-static bool stretchNeedsLayer(const LayerProperties& properties) {
- return Properties::stretchEffectBehavior != StretchEffectBehavior::LinearScale &&
- !properties.getStretchEffect().isEmpty();
-}
-
static bool layerNeedsPaint(const sk_sp<SkImage>& snapshotImage, const LayerProperties& properties,
float alphaMultiplier, SkPaint* paint) {
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
- properties.getImageFilter() != nullptr || stretchNeedsLayer(properties)) {
+ properties.getImageFilter() != nullptr || properties.getStretchEffect().requiresLayer()) {
paint->setAlpha(properties.alpha() * alphaMultiplier);
paint->setBlendMode(properties.xferMode());
paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
@@ -253,7 +248,7 @@
const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
if (stretch.isEmpty() ||
- Properties::stretchEffectBehavior == StretchEffectBehavior::LinearScale) {
+ Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) {
// If we don't have any stretch effects, issue the filtered
// canvas draw calls to make sure we still punch a hole
// with the same canvas transformation + clip into the target
@@ -332,7 +327,7 @@
canvas->concat(*properties.getTransformMatrix());
}
}
- if (Properties::stretchEffectBehavior == StretchEffectBehavior::LinearScale) {
+ if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) {
const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
if (!stretch.isEmpty()) {
canvas->concat(
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
index e48ecf4..1042703 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
@@ -86,12 +86,9 @@
}
}
- // if we don't have a resource name then we don't know how to label the
- // data and should abort.
+ // if we don't have a pretty name then use the dumpName
if (resourceName == nullptr) {
- mCurrentElement.clear();
- mCurrentValues.clear();
- return;
+ resourceName = mCurrentElement.c_str();
}
auto result = mResults.find(resourceName);
@@ -126,6 +123,12 @@
mCurrentValues.insert({valueName, {units, value}});
}
+bool SkiaMemoryTracer::hasOutput() {
+ // process any remaining elements
+ processElement();
+ return mResults.size() > 0;
+}
+
void SkiaMemoryTracer::logOutput(String8& log) {
// process any remaining elements
processElement();
@@ -151,6 +154,14 @@
}
}
+size_t SkiaMemoryTracer::total() {
+ processElement();
+ if (!strcmp("bytes", mTotalSize.units)) {
+ return mTotalSize.value;
+ }
+ return 0;
+}
+
void SkiaMemoryTracer::logTotals(String8& log) {
TraceValue total = convertUnits(mTotalSize);
TraceValue purgeable = convertUnits(mPurgeableSize);
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
index e9a7981..cba3b04 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
@@ -34,8 +34,10 @@
SkiaMemoryTracer(const char* categoryKey, bool itemizeType);
~SkiaMemoryTracer() override {}
+ bool hasOutput();
void logOutput(String8& log);
void logTotals(String8& log);
+ size_t total();
void dumpNumericValue(const char* dumpName, const char* valueName, const char* units,
uint64_t value) override;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 100bfb6..4658035 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -137,9 +137,10 @@
int mCaptureSequence = 0;
// Multi frame serialization stream and writer used when serializing more than one frame.
+ std::unique_ptr<SkSharingSerialContext> mSerialContext; // Must be declared before any other
+ // serializing member
std::unique_ptr<SkFILEWStream> mOpenMultiPicStream;
sk_sp<SkDocument> mMultiPic;
- std::unique_ptr<SkSharingSerialContext> mSerialContext;
/**
* mRecorder holds the current picture recorder when serializing in either SingleFrameSKP or
diff --git a/libs/hwui/pipeline/skia/StretchMask.cpp b/libs/hwui/pipeline/skia/StretchMask.cpp
index 2bbd8a4..1c58c6a 100644
--- a/libs/hwui/pipeline/skia/StretchMask.cpp
+++ b/libs/hwui/pipeline/skia/StretchMask.cpp
@@ -46,9 +46,16 @@
if (mIsDirty) {
SkCanvas* maskCanvas = mMaskSurface->getCanvas();
+ // Make sure to apply target transformation to the mask canvas
+ // to ensure the replayed drawing commands generate the same result
+ auto previousMatrix = displayList->mParentMatrix;
+ displayList->mParentMatrix = maskCanvas->getTotalMatrix();
+ maskCanvas->save();
maskCanvas->drawColor(0, SkBlendMode::kClear);
TransformCanvas transformCanvas(maskCanvas, SkBlendMode::kSrcOver);
displayList->draw(&transformCanvas);
+ maskCanvas->restore();
+ displayList->mParentMatrix = previousMatrix;
}
sk_sp<SkImage> maskImage = mMaskSurface->makeImageSnapshot();
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index d998e50..46e8060 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -130,39 +130,54 @@
mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
}
+void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
+ *cpuUsage = 0;
+ *gpuUsage = 0;
+ if (!mGrContext) {
+ return;
+ }
+
+ skiapipeline::SkiaMemoryTracer cpuTracer("category", true);
+ SkGraphics::DumpMemoryStatistics(&cpuTracer);
+ *cpuUsage += cpuTracer.total();
+
+ skiapipeline::SkiaMemoryTracer gpuTracer("category", true);
+ mGrContext->dumpMemoryStatistics(&gpuTracer);
+ *gpuUsage += gpuTracer.total();
+}
+
void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
if (!mGrContext) {
log.appendFormat("No valid cache instance.\n");
return;
}
- log.appendFormat("Font Cache (CPU):\n");
- log.appendFormat(" Size: %.2f kB \n", SkGraphics::GetFontCacheUsed() / 1024.0f);
- log.appendFormat(" Glyph Count: %d \n", SkGraphics::GetFontCacheCountUsed());
-
- log.appendFormat("CPU Caches:\n");
std::vector<skiapipeline::ResourcePair> cpuResourceMap = {
{"skia/sk_resource_cache/bitmap_", "Bitmaps"},
{"skia/sk_resource_cache/rrect-blur_", "Masks"},
{"skia/sk_resource_cache/rects-blur_", "Masks"},
{"skia/sk_resource_cache/tessellated", "Shadows"},
+ {"skia/sk_glyph_cache", "Glyph Cache"},
};
skiapipeline::SkiaMemoryTracer cpuTracer(cpuResourceMap, false);
SkGraphics::DumpMemoryStatistics(&cpuTracer);
- cpuTracer.logOutput(log);
+ if (cpuTracer.hasOutput()) {
+ log.appendFormat("CPU Caches:\n");
+ cpuTracer.logOutput(log);
+ log.appendFormat(" Glyph Count: %d \n", SkGraphics::GetFontCacheCountUsed());
+ log.appendFormat("Total CPU memory usage:\n");
+ cpuTracer.logTotals(log);
+ }
- log.appendFormat("GPU Caches:\n");
skiapipeline::SkiaMemoryTracer gpuTracer("category", true);
mGrContext->dumpMemoryStatistics(&gpuTracer);
- gpuTracer.logOutput(log);
+ if (gpuTracer.hasOutput()) {
+ log.appendFormat("GPU Caches:\n");
+ gpuTracer.logOutput(log);
+ }
- log.appendFormat("Other Caches:\n");
- log.appendFormat(" Current / Maximum\n");
-
- if (renderState) {
- if (renderState->mActiveLayers.size() > 0) {
- log.appendFormat(" Layer Info:\n");
- }
+ if (renderState && renderState->mActiveLayers.size() > 0) {
+ log.appendFormat("Layer Info:\n");
const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL
? "GlLayer"
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 0a6b8dc..713ea99 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -47,6 +47,7 @@
void trimMemory(TrimMemoryMode mode);
void trimStaleResources();
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
+ void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index bba2207..c1f61e08 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -195,6 +195,10 @@
auto funcs = mRenderThread.getASurfaceControlFunctions();
+ if (surfaceControl == nullptr) {
+ setASurfaceTransactionCallback(nullptr);
+ }
+
if (mSurfaceControl != nullptr) {
funcs.unregisterListenerFunc(this, &onSurfaceStatsAvailable);
funcs.releaseFunc(mSurfaceControl);
@@ -467,11 +471,11 @@
mRenderThread.pushBackFrameCallback(this);
}
-void CanvasContext::draw() {
+nsecs_t CanvasContext::draw() {
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
- return;
+ return 0;
}
}
SkRect dirty;
@@ -486,7 +490,7 @@
std::invoke(func, mFrameNumber);
}
mFrameCompleteCallbacks.clear();
- return;
+ return 0;
}
ScopedActiveContext activeContext(this);
@@ -616,6 +620,7 @@
}
mRenderThread.cacheManager().onFrameCompleted();
+ return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
}
void CanvasContext::reportMetricsWithPresentTime() {
@@ -672,20 +677,8 @@
}
if (frameInfo != nullptr) {
- if (gpuCompleteTime == -1) {
- gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
- }
- if (gpuCompleteTime < frameInfo->get(FrameInfoIndex::IssueDrawCommandsStart)) {
- // On Vulkan the GPU commands are flushed to the GPU during IssueDrawCommands rather
- // than after SwapBuffers. So if the GPU signals before issue draw commands, then
- // something probably went wrong. Anything after that could just be expected
- // pipeline differences
- ALOGW("Impossible GPU complete time issueCommandsStart=%" PRIi64
- " gpuComplete=%" PRIi64,
- frameInfo->get(FrameInfoIndex::IssueDrawCommandsStart), gpuCompleteTime);
- gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
- }
- frameInfo->set(FrameInfoIndex::FrameCompleted) = gpuCompleteTime;
+ frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime,
+ frameInfo->get(FrameInfoIndex::SwapBuffersCompleted));
frameInfo->set(FrameInfoIndex::GpuCompleted) = gpuCompleteTime;
instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter);
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index af1ebb2..4f8e4ca 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -127,7 +127,8 @@
void setColorMode(ColorMode mode);
bool makeCurrent();
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
- void draw();
+ // Returns the DequeueBufferDuration.
+ nsecs_t draw();
void destroy();
// IFrameCallback, Choreographer-driven frame callback entry point
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index cb92aa1..8448b87 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -18,6 +18,7 @@
#include <utils/Log.h>
#include <utils/TraceUtils.h>
+#include <algorithm>
#include "../DeferredLayerUpdater.h"
#include "../DisplayList.h"
@@ -91,6 +92,7 @@
void DrawFrameTask::run() {
const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
+ nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
bool canUnblockUiThread;
bool canDrawThisFrame;
@@ -124,8 +126,9 @@
[callback, frameNr = context->getFrameNumber()]() { callback(frameNr); });
}
+ nsecs_t dequeueBufferDuration = 0;
if (CC_LIKELY(canDrawThisFrame)) {
- context->draw();
+ dequeueBufferDuration = context->draw();
} else {
// wait on fences so tasks don't overlap next frame
context->waitOnFences();
@@ -149,10 +152,14 @@
mUpdateTargetWorkDuration(targetWorkDuration);
}
int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
- if (frameDuration > kSanityCheckLowerBound && frameDuration < kSanityCheckUpperBound) {
- mReportActualWorkDuration(frameDuration);
+ int64_t actualDuration = frameDuration -
+ (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+ dequeueBufferDuration;
+ if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
+ mReportActualWorkDuration(actualDuration);
}
}
+ mLastDequeueBufferDuration = dequeueBufferDuration;
}
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 3bb574a..2455ea8 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -110,6 +110,7 @@
std::function<void(int64_t)> mFrameCallback;
std::function<void(int64_t)> mFrameCompleteCallback;
+ nsecs_t mLastDequeueBufferDuration = 0;
nsecs_t mLastTargetWorkDuration = 0;
std::function<void(int64_t)> mUpdateTargetWorkDuration;
std::function<void(int64_t)> mReportActualWorkDuration;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 1b4b4b9..95aa29d 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -195,6 +195,17 @@
}
}
+void RenderProxy::purgeCaches() {
+ if (RenderThread::hasInstance()) {
+ RenderThread& thread = RenderThread::getInstance();
+ thread.queue().post([&thread]() {
+ if (thread.getGrContext()) {
+ thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ }
+ });
+ }
+}
+
void RenderProxy::overrideProperty(const char* name, const char* value) {
// expensive, but block here since name/value pointers owned by caller
RenderThread::getInstance().queue().runSync(
@@ -249,10 +260,17 @@
});
}
-void RenderProxy::dumpGraphicsMemory(int fd) {
+void RenderProxy::dumpGraphicsMemory(int fd, bool includeProfileData) {
if (RenderThread::hasInstance()) {
auto& thread = RenderThread::getInstance();
- thread.queue().runSync([&]() { thread.dumpGraphicsMemory(fd); });
+ thread.queue().runSync([&]() { thread.dumpGraphicsMemory(fd, includeProfileData); });
+ }
+}
+
+void RenderProxy::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
+ if (RenderThread::hasInstance()) {
+ auto& thread = RenderThread::getInstance();
+ thread.queue().runSync([&]() { thread.getMemoryUsage(cpuUsage, gpuUsage); });
}
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 288f555..0681dc5 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -98,6 +98,7 @@
void destroyHardwareResources();
static void trimMemory(int level);
+ static void purgeCaches();
static void overrideProperty(const char* name, const char* value);
void fence();
@@ -109,7 +110,8 @@
// Not exported, only used for testing
void resetProfileInfo();
uint32_t frameTimePercentile(int p);
- static void dumpGraphicsMemory(int fd);
+ static void dumpGraphicsMemory(int fd, bool includeProfileData = true);
+ static void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
static void rotateProcessStatsBuffer();
static void setProcessStatsBuffer(int fd);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3421e01..0268bfd7 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -302,30 +302,29 @@
return *mVkManager.get();
}
-void RenderThread::dumpGraphicsMemory(int fd) {
- globalProfileData()->dump(fd);
-
- String8 cachesOutput;
- String8 pipeline;
- auto renderType = Properties::getRenderPipelineType();
- switch (renderType) {
- case RenderPipelineType::SkiaGL: {
- mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState);
- pipeline.appendFormat("Skia (OpenGL)");
- break;
- }
- case RenderPipelineType::SkiaVulkan: {
- mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState);
- pipeline.appendFormat("Skia (Vulkan)");
- break;
- }
+static const char* pipelineToString() {
+ switch (auto renderType = Properties::getRenderPipelineType()) {
+ case RenderPipelineType::SkiaGL:
+ return "Skia (OpenGL)";
+ case RenderPipelineType::SkiaVulkan:
+ return "Skia (Vulkan)";
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
- break;
+ }
+}
+
+void RenderThread::dumpGraphicsMemory(int fd, bool includeProfileData) {
+ if (includeProfileData) {
+ globalProfileData()->dump(fd);
}
- dprintf(fd, "\n%s\n", cachesOutput.string());
- dprintf(fd, "\nPipeline=%s\n", pipeline.string());
+ String8 cachesOutput;
+ mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState);
+ dprintf(fd, "\nPipeline=%s\n%s\n", pipelineToString(), cachesOutput.string());
+}
+
+void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
+ mCacheManager->getMemoryUsage(cpuUsage, gpuUsage);
}
Readback& RenderThread::readback() {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index cd9b923..5021085 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -150,7 +150,8 @@
VulkanManager& vulkanManager();
sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap);
- void dumpGraphicsMemory(int fd);
+ void dumpGraphicsMemory(int fd, bool includeProfileData);
+ void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
void requireGlContext();
void requireVkContext();
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 0112686..07146e8 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -340,6 +340,8 @@
}
void VulkanManager::initialize() {
+ std::lock_guard _lock{mInitializeLock};
+
if (mDevice != VK_NULL_HANDLE) {
return;
}
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 7b5fe19..b816649 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -220,6 +220,8 @@
VkSemaphore mSwapSemaphore = VK_NULL_HANDLE;
void* mDestroySemaphoreContext = nullptr;
+
+ std::mutex mInitializeLock;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 474d2cc..c559425 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -329,20 +329,16 @@
if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
int err = mNativeWindow->cancelBuffer(mNativeWindow.get(), bufferInfo.buffer.get(),
- bufferInfo.dequeue_fence);
+ bufferInfo.dequeue_fence.release());
if (err != 0) {
ALOGE("cancelBuffer[%u] failed during destroy: %s (%d)", i, strerror(-err), err);
}
bufferInfo.dequeued = false;
-
- if (bufferInfo.dequeue_fence >= 0) {
- close(bufferInfo.dequeue_fence);
- bufferInfo.dequeue_fence = -1;
- }
+ bufferInfo.dequeue_fence.reset();
}
LOG_ALWAYS_FATAL_IF(bufferInfo.dequeued);
- LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence != -1);
+ LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence.ok());
bufferInfo.skSurface.reset();
bufferInfo.buffer.clear();
@@ -365,8 +361,12 @@
// Since auto pre-rotation is enabled, dequeueBuffer to get the consumer driven buffer size
// from ANativeWindowBuffer.
ANativeWindowBuffer* buffer;
- int fence_fd;
- err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd);
+ base::unique_fd fence_fd;
+ {
+ int rawFd = -1;
+ err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &rawFd);
+ fence_fd.reset(rawFd);
+ }
if (err != 0) {
ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
return nullptr;
@@ -387,7 +387,7 @@
if (err != 0) {
ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", transformHint,
strerror(-err), err);
- mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
+ mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd.release());
return nullptr;
}
mWindowInfo.transform = transformHint;
@@ -405,19 +405,19 @@
for (idx = 0; idx < mWindowInfo.bufferCount; idx++) {
if (mNativeBuffers[idx].buffer.get() == buffer) {
mNativeBuffers[idx].dequeued = true;
- mNativeBuffers[idx].dequeue_fence = fence_fd;
+ mNativeBuffers[idx].dequeue_fence = std::move(fence_fd);
break;
} else if (mNativeBuffers[idx].buffer.get() == nullptr) {
// increasing the number of buffers we have allocated
mNativeBuffers[idx].buffer = buffer;
mNativeBuffers[idx].dequeued = true;
- mNativeBuffers[idx].dequeue_fence = fence_fd;
+ mNativeBuffers[idx].dequeue_fence = std::move(fence_fd);
break;
}
}
if (idx == mWindowInfo.bufferCount) {
ALOGE("dequeueBuffer returned unrecognized buffer");
- mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
+ mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd.release());
return nullptr;
}
@@ -429,7 +429,7 @@
kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, nullptr);
if (bufferInfo->skSurface.get() == nullptr) {
ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
- mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
+ mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd.release());
return nullptr;
}
}
@@ -460,25 +460,23 @@
LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
- int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence;
+ // queueBuffer always closes fence, even on error
+ int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence.release();
int err = mNativeWindow->queueBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), queuedFd);
currentBuffer.dequeued = false;
- // queueBuffer always closes fence, even on error
if (err != 0) {
ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+ // cancelBuffer takes ownership of the fence
mNativeWindow->cancelBuffer(mNativeWindow.get(), currentBuffer.buffer.get(),
- currentBuffer.dequeue_fence);
+ currentBuffer.dequeue_fence.release());
} else {
currentBuffer.hasValidContents = true;
currentBuffer.lastPresentedCount = mPresentCount;
mPresentCount++;
}
- if (currentBuffer.dequeue_fence >= 0) {
- close(currentBuffer.dequeue_fence);
- currentBuffer.dequeue_fence = -1;
- }
+ currentBuffer.dequeue_fence.reset();
return err == 0;
}
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index 7c25545..beb71b72 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -15,6 +15,7 @@
*/
#pragma once
+#include <android-base/unique_fd.h>
#include <system/graphics.h>
#include <system/window.h>
#include <vulkan/vulkan.h>
@@ -57,7 +58,7 @@
// -1 any other time. When valid, we own the fd, and must ensure it is
// closed: either by closing it explicitly when queueing the buffer,
// or by passing ownership e.g. to ANativeWindow::cancelBuffer().
- int dequeue_fence = -1;
+ base::unique_fd dequeue_fence;
bool dequeued = false;
uint32_t lastPresentedCount = 0;
bool hasValidContents = false;
diff --git a/libs/hwui/tests/common/TestScene.cpp b/libs/hwui/tests/common/TestScene.cpp
index 02bcd47..2c532b0 100644
--- a/libs/hwui/tests/common/TestScene.cpp
+++ b/libs/hwui/tests/common/TestScene.cpp
@@ -22,8 +22,9 @@
// Not a static global because we need to force the map to be constructed
// before we try to add things to it.
-std::unordered_map<std::string, TestScene::Info>& TestScene::testMap() {
- static std::unordered_map<std::string, TestScene::Info> testMap;
+// std::map because tests sorted by name is a prettier output
+std::map<std::string, TestScene::Info>& TestScene::testMap() {
+ static std::map<std::string, TestScene::Info> testMap;
return testMap;
}
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 91022cf..6b0be53 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -19,8 +19,8 @@
#include <gui/Surface.h>
#include <utils/StrongPointer.h>
+#include <map>
#include <string>
-#include <unordered_map>
namespace android {
@@ -35,9 +35,12 @@
class TestScene {
public:
struct Options {
- int count = 0;
+ int frameCount = 150;
+ int repeatCount = 1;
int reportFrametimeWeight = 0;
bool renderOffscreen = true;
+ bool reportGpuMemoryUsage = false;
+ bool reportGpuMemoryUsageVerbose = false;
};
template <class T>
@@ -67,7 +70,7 @@
virtual void createContent(int width, int height, Canvas& renderer) = 0;
virtual void doFrame(int frameNr) = 0;
- static std::unordered_map<std::string, Info>& testMap();
+ static std::map<std::string, Info>& testMap();
static void registerScene(const Info& info);
sp<Surface> renderTarget;
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index cf8fc82..5092675 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -287,6 +287,7 @@
int sync = 0;
int contextDestroyed = 0;
int destroyed = 0;
+ int removeOverlays = 0;
int glesDraw = 0;
};
@@ -311,6 +312,12 @@
expectOnRenderThread("onDestroyed");
sMockFunctorCounts[functor].destroyed++;
},
+ .removeOverlays =
+ [](int functor, void* data,
+ void (*mergeTransaction)(ASurfaceTransaction*)) {
+ expectOnRenderThread("removeOverlays");
+ sMockFunctorCounts[functor].removeOverlays++;
+ },
};
switch (mode) {
case RenderMode::OpenGL_ES:
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
new file mode 100644
index 0000000..e677549
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <SkFont.h>
+#include <cstdio>
+#include "TestSceneBase.h"
+#include "hwui/Paint.h"
+#include "tests/common/TestUtils.h"
+
+class StretchyListViewAnimation;
+class StretchyListViewHolePunch;
+class StretchyUniformListView;
+class StretchyUniformListViewHolePunch;
+class StretchyUniformLayerListView;
+class StretchyUniformLayerListViewHolePunch;
+
+static TestScene::Registrar _StretchyListViewAnimation(TestScene::Info{
+ "stretchylistview",
+ "A mock ListView of scrolling content that's stretching. Doesn't re-bind/re-record views "
+ "as they are recycled, so won't upload much content (either glyphs, or bitmaps).",
+ TestScene::simpleCreateScene<StretchyListViewAnimation>});
+
+static TestScene::Registrar _StretchyListViewHolePunch(TestScene::Info{
+ "stretchylistview_holepunch",
+ "A mock ListView of scrolling content that's stretching. Includes a hole punch",
+ TestScene::simpleCreateScene<StretchyListViewHolePunch>});
+
+static TestScene::Registrar _StretchyUniformListView(TestScene::Info{
+ "stretchylistview_uniform",
+ "A mock ListView of scrolling content that's stretching using a uniform stretch effect.",
+ TestScene::simpleCreateScene<StretchyUniformListView>});
+
+static TestScene::Registrar _StretchyUniformListViewHolePunch(TestScene::Info{
+ "stretchylistview_uniform_holepunch",
+ "A mock ListView of scrolling content that's stretching using a uniform stretch effect. "
+ "Includes a hole punch",
+ TestScene::simpleCreateScene<StretchyUniformListViewHolePunch>});
+
+static TestScene::Registrar _StretchyUniformLayerListView(TestScene::Info{
+ "stretchylistview_uniform_layer",
+ "A mock ListView of scrolling content that's stretching using a uniform stretch effect. "
+ "Uses a layer",
+ TestScene::simpleCreateScene<StretchyUniformLayerListView>});
+
+static TestScene::Registrar _StretchyUniformLayerListViewHolePunch(TestScene::Info{
+ "stretchylistview_uniform_layer_holepunch",
+ "A mock ListView of scrolling content that's stretching using a uniform stretch effect. "
+ "Uses a layer & includes a hole punch",
+ TestScene::simpleCreateScene<StretchyUniformLayerListViewHolePunch>});
+
+class StretchyListViewAnimation : public TestScene {
+protected:
+ virtual StretchEffectBehavior stretchBehavior() { return StretchEffectBehavior::Shader; }
+ virtual bool haveHolePunch() { return false; }
+ virtual bool forceLayer() { return false; }
+
+private:
+ int mItemHeight;
+ int mItemSpacing;
+ int mItemWidth;
+ int mItemLeft;
+ sp<RenderNode> mListView;
+ std::vector<sp<RenderNode> > mListItems;
+
+ sk_sp<Bitmap> createRandomCharIcon(int cardHeight) {
+ SkBitmap skBitmap;
+ int size = cardHeight - (dp(10) * 2);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(size, size, &skBitmap));
+ SkCanvas canvas(skBitmap);
+ canvas.clear(0);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ SkColor randomColor = BrightColors[rand() % BrightColorsCount];
+ paint.setColor(randomColor);
+ canvas.drawCircle(size / 2, size / 2, size / 2, paint);
+
+ bool bgDark =
+ SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) <
+ 128 * 3;
+ paint.setColor(bgDark ? Color::White : Color::Grey_700);
+
+ SkFont font;
+ font.setSize(size / 2);
+ char charToShow = 'A' + (rand() % 26);
+ const SkPoint pos = {SkIntToScalar(size / 2),
+ /*approximate centering*/ SkFloatToScalar(size * 0.7f)};
+ canvas.drawSimpleText(&charToShow, 1, SkTextEncoding::kUTF8, pos.fX, pos.fY, font, paint);
+ return bitmap;
+ }
+
+ static sk_sp<Bitmap> createBoxBitmap(bool filled) {
+ int size = dp(20);
+ int stroke = dp(2);
+ SkBitmap skBitmap;
+ auto bitmap = TestUtils::createBitmap(size, size, &skBitmap);
+ SkCanvas canvas(skBitmap);
+ canvas.clear(Color::Transparent);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(filled ? Color::Yellow_500 : Color::Grey_700);
+ paint.setStyle(filled ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style);
+ paint.setStrokeWidth(stroke);
+ canvas.drawRect(SkRect::MakeLTRB(stroke, stroke, size - stroke, size - stroke), paint);
+ return bitmap;
+ }
+
+ void createListItem(RenderProperties& props, Canvas& canvas, int cardId, int itemWidth,
+ int itemHeight) {
+ static sk_sp<Bitmap> filledBox(createBoxBitmap(true));
+ static sk_sp<Bitmap> strokedBox(createBoxBitmap(false));
+ const bool addHolePunch = cardId == 2 && haveHolePunch();
+ // TODO: switch to using round rect clipping, once merging correctly handles that
+ Paint roundRectPaint;
+ roundRectPaint.setAntiAlias(true);
+ roundRectPaint.setColor(Color::White);
+ if (addHolePunch) {
+ // Punch a hole but then cover it up, we don't want to actually see it
+ canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight)));
+ }
+ canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint);
+
+ Paint textPaint;
+ textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500);
+ textPaint.getSkFont().setSize(dp(20));
+ textPaint.setAntiAlias(true);
+ char buf[256];
+ snprintf(buf, sizeof(buf), "This card is #%d", cardId);
+ TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, itemHeight, dp(25));
+ textPaint.getSkFont().setSize(dp(15));
+ if (addHolePunch) {
+ TestUtils::drawUtf8ToCanvas(&canvas, "I have a hole punch", textPaint, itemHeight,
+ dp(45));
+ } else {
+ TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint,
+ itemHeight, dp(45));
+ }
+
+ auto randomIcon = createRandomCharIcon(itemHeight);
+ canvas.drawBitmap(*randomIcon, dp(10), dp(10), nullptr);
+
+ auto box = rand() % 2 ? filledBox : strokedBox;
+ canvas.drawBitmap(*box, itemWidth - dp(10) - box->width(), dp(10), nullptr);
+ }
+
+ void createContent(int width, int height, Canvas& canvas) override {
+ srand(0);
+ mItemHeight = dp(60);
+ mItemSpacing = dp(16);
+ mItemWidth = std::min((height - mItemSpacing * 2), (int)dp(300));
+ mItemLeft = (width - mItemWidth) / 2;
+ int heightWithSpacing = mItemHeight + mItemSpacing;
+ for (int y = 0; y < height + (heightWithSpacing - 1); y += heightWithSpacing) {
+ int id = mListItems.size();
+ auto node = TestUtils::createNode(mItemLeft, y, mItemLeft + mItemWidth, y + mItemHeight,
+ [this, id](RenderProperties& props, Canvas& canvas) {
+ createListItem(props, canvas, id, mItemWidth,
+ mItemHeight);
+ });
+ mListItems.push_back(node);
+ }
+ mListView = TestUtils::createNode(0, 0, width, height,
+ [this](RenderProperties& props, Canvas& canvas) {
+ for (size_t ci = 0; ci < mListItems.size(); ci++) {
+ canvas.drawRenderNode(mListItems[ci].get());
+ }
+ });
+
+ canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver);
+ canvas.drawRenderNode(mListView.get());
+ }
+
+ void doFrame(int frameNr) override {
+ if (frameNr == 0) {
+ Properties::setStretchEffectBehavior(stretchBehavior());
+ if (forceLayer()) {
+ mListView->mutateStagingProperties().mutateLayerProperties().setType(
+ LayerType::RenderLayer);
+ }
+ }
+ auto& props = mListView->mutateStagingProperties();
+ auto& stretch = props.mutateLayerProperties().mutableStretchEffect();
+ stretch.setEmpty();
+ frameNr = frameNr % 150;
+ // Animate from 0f to .1f
+ const float sY = (frameNr > 75 ? 150 - frameNr : frameNr) / 1500.f;
+ stretch.mergeWith({{.fX = 0, .fY = sY},
+ static_cast<float>(props.getWidth()),
+ static_cast<float>(props.getHeight())});
+ mListView->setPropertyFieldsDirty(RenderNode::GENERIC);
+ }
+};
+
+class StretchyListViewHolePunch : public StretchyListViewAnimation {
+ bool haveHolePunch() override { return true; }
+};
+
+class StretchyUniformListView : public StretchyListViewAnimation {
+ StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
+};
+
+class StretchyUniformListViewHolePunch : public StretchyListViewAnimation {
+ StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
+ bool haveHolePunch() override { return true; }
+};
+
+class StretchyUniformLayerListView : public StretchyListViewAnimation {
+ StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
+ bool forceLayer() override { return true; }
+};
+
+class StretchyUniformLayerListViewHolePunch : public StretchyListViewAnimation {
+ StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
+ bool haveHolePunch() override { return true; }
+ bool forceLayer() override { return true; }
+};
\ No newline at end of file
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 13ac367..b640b90 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -28,6 +28,20 @@
#include <log/log.h>
#include <ui/PixelFormat.h>
+// These are unstable internal APIs in google-benchmark. We should just implement our own variant
+// of these instead, but this was quicker. Disabled-by-default to avoid any breakages when
+// google-benchmark updates if they change anything
+#if 0
+#define USE_SKETCHY_INTERNAL_STATS
+namespace benchmark {
+std::vector<BenchmarkReporter::Run> ComputeStats(
+ const std::vector<BenchmarkReporter::Run> &reports);
+double StatisticsMean(const std::vector<double>& v);
+double StatisticsMedian(const std::vector<double>& v);
+double StatisticsStdDev(const std::vector<double>& v);
+}
+#endif
+
using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::renderthread;
@@ -62,53 +76,34 @@
T mAverage;
};
+using BenchmarkResults = std::vector<benchmark::BenchmarkReporter::Run>;
+
void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts,
- benchmark::BenchmarkReporter* reporter, RenderProxy* proxy,
- double durationInS) {
+ double durationInS, int repetationIndex, BenchmarkResults* reports) {
using namespace benchmark;
-
- struct ReportInfo {
- int percentile;
- const char* suffix;
- };
-
- static std::array<ReportInfo, 4> REPORTS = {
- ReportInfo{50, "_50th"}, ReportInfo{90, "_90th"}, ReportInfo{95, "_95th"},
- ReportInfo{99, "_99th"},
- };
-
- // Although a vector is used, it must stay with only a single element
- // otherwise the BenchmarkReporter will automatically compute
- // mean and stddev which doesn't make sense for our usage
- std::vector<BenchmarkReporter::Run> reports;
- BenchmarkReporter::Run report;
+ benchmark::BenchmarkReporter::Run report;
+ report.repetitions = opts.repeatCount;
+ report.repetition_index = repetationIndex;
report.run_name.function_name = info.name;
- report.iterations = static_cast<int64_t>(opts.count);
+ report.iterations = static_cast<int64_t>(opts.frameCount);
report.real_accumulated_time = durationInS;
report.cpu_accumulated_time = durationInS;
- report.counters["items_per_second"] = opts.count / durationInS;
- reports.push_back(report);
- reporter->ReportRuns(reports);
-
- // Pretend the percentiles are single-iteration runs of the test
- // If rendering offscreen skip this as it's fps that's more interesting
- // in that test case than percentiles.
- if (!opts.renderOffscreen) {
- for (auto& ri : REPORTS) {
- reports[0].run_name.function_name = info.name;
- reports[0].run_name.function_name += ri.suffix;
- durationInS = proxy->frameTimePercentile(ri.percentile) / 1000.0;
- reports[0].real_accumulated_time = durationInS;
- reports[0].cpu_accumulated_time = durationInS;
- reports[0].iterations = 1;
- reports[0].counters["items_per_second"] = 0;
- reporter->ReportRuns(reports);
- }
+ report.counters["FPS"] = opts.frameCount / durationInS;
+ if (opts.reportGpuMemoryUsage) {
+ size_t cpuUsage, gpuUsage;
+ RenderProxy::getMemoryUsage(&cpuUsage, &gpuUsage);
+ report.counters["Rendering RAM"] = Counter{static_cast<double>(cpuUsage + gpuUsage),
+ Counter::kDefaults, Counter::kIs1024};
}
+ reports->push_back(report);
}
-void run(const TestScene::Info& info, const TestScene::Options& opts,
- benchmark::BenchmarkReporter* reporter) {
+static void doRun(const TestScene::Info& info, const TestScene::Options& opts, int repetitionIndex,
+ BenchmarkResults* reports) {
+ if (opts.reportGpuMemoryUsage) {
+ // If we're reporting GPU memory usage we need to first start with a clean slate
+ RenderProxy::purgeCaches();
+ }
Properties::forceDrawFrame = true;
TestContext testContext;
testContext.setRenderOffscreen(opts.renderOffscreen);
@@ -158,7 +153,7 @@
ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight);
nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
- for (int i = 0; i < opts.count; i++) {
+ for (int i = 0; i < opts.frameCount; i++) {
testContext.waitForVsync();
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
{
@@ -182,9 +177,38 @@
proxy->fence();
nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC);
- if (reporter) {
- outputBenchmarkReport(info, opts, reporter, proxy.get(), (end - start) / (double)s2ns(1));
+ if (reports) {
+ outputBenchmarkReport(info, opts, (end - start) / (double)s2ns(1), repetitionIndex,
+ reports);
} else {
proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats);
}
}
+
+void run(const TestScene::Info& info, const TestScene::Options& opts,
+ benchmark::BenchmarkReporter* reporter) {
+ BenchmarkResults results;
+ for (int i = 0; i < opts.repeatCount; i++) {
+ doRun(info, opts, i, reporter ? &results : nullptr);
+ }
+ if (reporter) {
+ reporter->ReportRuns(results);
+ if (results.size() > 1) {
+#ifdef USE_SKETCHY_INTERNAL_STATS
+ std::vector<benchmark::internal::Statistics> stats;
+ stats.reserve(3);
+ stats.emplace_back("mean", benchmark::StatisticsMean);
+ stats.emplace_back("median", benchmark::StatisticsMedian);
+ stats.emplace_back("stddev", benchmark::StatisticsStdDev);
+ for (auto& it : results) {
+ it.statistics = &stats;
+ }
+ auto summary = benchmark::ComputeStats(results);
+ reporter->ReportRuns(summary);
+#endif
+ }
+ }
+ if (opts.reportGpuMemoryUsageVerbose) {
+ RenderProxy::dumpGraphicsMemory(STDOUT_FILENO, false);
+ }
+}
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 174a140..f3f32eb 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -23,6 +23,7 @@
#include "renderthread/RenderProxy.h"
#include <benchmark/benchmark.h>
+#include <fnmatch.h>
#include <getopt.h>
#include <pthread.h>
#include <stdio.h>
@@ -40,9 +41,9 @@
using namespace android::uirenderer;
using namespace android::uirenderer::test;
-static int gRepeatCount = 1;
static std::vector<TestScene::Info> gRunTests;
static TestScene::Options gOpts;
+static bool gRunLeakCheck = true;
std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter;
void run(const TestScene::Info& info, const TestScene::Options& opts,
@@ -69,6 +70,8 @@
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
--renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk
+ --skip-leak-check Skips the memory leak check
+ --report-gpu-memory[=verbose] Dumps the GPU memory usage after each test run
)");
}
@@ -139,7 +142,7 @@
} else if (!strcmp(format, "json")) {
gBenchmarkReporter.reset(new benchmark::JSONReporter());
} else {
- fprintf(stderr, "Unknown format '%s'", format);
+ fprintf(stderr, "Unknown format '%s'\n", format);
return false;
}
return true;
@@ -151,7 +154,7 @@
} else if (!strcmp(renderer, "skiavk")) {
Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan);
} else {
- fprintf(stderr, "Unknown format '%s'", renderer);
+ fprintf(stderr, "Unknown format '%s'\n", renderer);
return false;
}
return true;
@@ -170,6 +173,8 @@
Onscreen,
Offscreen,
Renderer,
+ SkipLeakCheck,
+ ReportGpuMemory,
};
}
@@ -185,6 +190,8 @@
{"onscreen", no_argument, nullptr, LongOpts::Onscreen},
{"offscreen", no_argument, nullptr, LongOpts::Offscreen},
{"renderer", required_argument, nullptr, LongOpts::Renderer},
+ {"skip-leak-check", no_argument, nullptr, LongOpts::SkipLeakCheck},
+ {"report-gpu-memory", optional_argument, nullptr, LongOpts::ReportGpuMemory},
{0, 0, 0, 0}};
static const char* SHORT_OPTIONS = "c:r:h";
@@ -214,20 +221,20 @@
break;
case 'c':
- gOpts.count = atoi(optarg);
- if (!gOpts.count) {
+ gOpts.frameCount = atoi(optarg);
+ if (!gOpts.frameCount) {
fprintf(stderr, "Invalid frames argument '%s'\n", optarg);
error = true;
}
break;
case 'r':
- gRepeatCount = atoi(optarg);
- if (!gRepeatCount) {
+ gOpts.repeatCount = atoi(optarg);
+ if (!gOpts.repeatCount) {
fprintf(stderr, "Invalid repeat argument '%s'\n", optarg);
error = true;
} else {
- gRepeatCount = (gRepeatCount > 0 ? gRepeatCount : INT_MAX);
+ gOpts.repeatCount = (gOpts.repeatCount > 0 ? gOpts.repeatCount : INT_MAX);
}
break;
@@ -283,6 +290,22 @@
gOpts.renderOffscreen = true;
break;
+ case LongOpts::SkipLeakCheck:
+ gRunLeakCheck = false;
+ break;
+
+ case LongOpts::ReportGpuMemory:
+ gOpts.reportGpuMemoryUsage = true;
+ if (optarg) {
+ if (!strcmp("verbose", optarg)) {
+ gOpts.reportGpuMemoryUsageVerbose = true;
+ } else {
+ fprintf(stderr, "Invalid report gpu memory option '%s'\n", optarg);
+ error = true;
+ }
+ }
+ break;
+
case 'h':
printHelp();
exit(EXIT_SUCCESS);
@@ -298,7 +321,7 @@
}
if (error) {
- fprintf(stderr, "Try 'hwuitest --help' for more information.\n");
+ fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
exit(EXIT_FAILURE);
}
@@ -306,12 +329,21 @@
if (optind < argc) {
do {
const char* test = argv[optind++];
- auto pos = TestScene::testMap().find(test);
- if (pos == TestScene::testMap().end()) {
- fprintf(stderr, "Unknown test '%s'\n", test);
- exit(EXIT_FAILURE);
+ if (strchr(test, '*')) {
+ // Glob match
+ for (auto& iter : TestScene::testMap()) {
+ if (!fnmatch(test, iter.first.c_str(), 0)) {
+ gRunTests.push_back(iter.second);
+ }
+ }
} else {
- gRunTests.push_back(pos->second);
+ auto pos = TestScene::testMap().find(test);
+ if (pos == TestScene::testMap().end()) {
+ fprintf(stderr, "Unknown test '%s'\n", test);
+ exit(EXIT_FAILURE);
+ } else {
+ gRunTests.push_back(pos->second);
+ }
}
} while (optind < argc);
} else {
@@ -322,9 +354,6 @@
}
int main(int argc, char* argv[]) {
- // set defaults
- gOpts.count = 150;
-
Typeface::setRobotoTypefaceForTest();
parseOptions(argc, argv);
@@ -345,10 +374,8 @@
gBenchmarkReporter->ReportContext(context);
}
- for (int i = 0; i < gRepeatCount; i++) {
- for (auto&& test : gRunTests) {
- run(test, gOpts, gBenchmarkReporter.get());
- }
+ for (auto&& test : gRunTests) {
+ run(test, gOpts, gBenchmarkReporter.get());
}
if (gBenchmarkReporter) {
@@ -358,6 +385,8 @@
renderthread::RenderProxy::trimMemory(100);
HardwareBitmapUploader::terminate();
- LeakChecker::checkForLeaks();
+ if (gRunLeakCheck) {
+ LeakChecker::checkForLeaks();
+ }
return 0;
}
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 402cb58..10c874e 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -62,8 +62,10 @@
gSigChain.insert(pair<int, struct sigaction>(sig, old_sa));
}
- // Replace the default GLES driver
+ // Avoid talking to SF
Properties::isolatedProcess = true;
+ // Default to GLES (Vulkan-aware tests will override this)
+ Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
// Run the tests
testing::InitGoogleTest(&argc, argv);
diff --git a/location/java/android/location/SatellitePvt.java b/location/java/android/location/SatellitePvt.java
index 1456f74..bb9ae6e 100644
--- a/location/java/android/location/SatellitePvt.java
+++ b/location/java/android/location/SatellitePvt.java
@@ -45,8 +45,41 @@
*/
@SystemApi
public final class SatellitePvt implements Parcelable {
+ /**
+ * Bit mask for {@link #mFlags} indicating valid satellite position, velocity and clock info
+ * fields are stored in the SatellitePvt.
+ */
+ private static final int HAS_POSITION_VELOCITY_CLOCK_INFO = 1 << 0;
+
+ /**
+ * Bit mask for {@link #mFlags} indicating a valid iono delay field is stored in the
+ * SatellitePvt.
+ */
+ private static final int HAS_IONO = 1 << 1;
+
+ /**
+ * Bit mask for {@link #mFlags} indicating a valid tropo delay field is stored in the
+ * SatellitePvt.
+ */
+ private static final int HAS_TROPO = 1 << 2;
+
+ /**
+ * A bitfield of flags indicating the validity of the fields in this SatellitePvt.
+ * The bit masks are defined in the constants with prefix HAS_*
+ *
+ * <p>Fields for which there is no corresponding flag must be filled in with a valid value.
+ * For convenience, these are marked as mandatory.
+ *
+ * <p>Others fields may have invalid information in them, if not marked as valid by the
+ * corresponding bit in flags.
+ */
+ private final int mFlags;
+
+ @Nullable
private final PositionEcef mPositionEcef;
+ @Nullable
private final VelocityEcef mVelocityEcef;
+ @Nullable
private final ClockInfo mClockInfo;
private final double mIonoDelayMeters;
private final double mTropoDelayMeters;
@@ -351,20 +384,13 @@
}
private SatellitePvt(
- @NonNull PositionEcef positionEcef,
- @NonNull VelocityEcef velocityEcef,
- @NonNull ClockInfo clockInfo,
+ int flags,
+ @Nullable PositionEcef positionEcef,
+ @Nullable VelocityEcef velocityEcef,
+ @Nullable ClockInfo clockInfo,
double ionoDelayMeters,
double tropoDelayMeters) {
- if (positionEcef == null) {
- throw new IllegalArgumentException("Position Ecef cannot be null.");
- }
- if (velocityEcef == null) {
- throw new IllegalArgumentException("Velocity Ecef cannot be null.");
- }
- if (clockInfo == null) {
- throw new IllegalArgumentException("Clock Info cannot be null.");
- }
+ mFlags = flags;
mPositionEcef = positionEcef;
mVelocityEcef = velocityEcef;
mClockInfo = clockInfo;
@@ -373,10 +399,17 @@
}
/**
+ * Gets a bitmask of fields present in this object
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
* Returns a {@link PositionEcef} object that contains estimates of the satellite
* position fields in ECEF coordinate frame.
*/
- @NonNull
+ @Nullable
public PositionEcef getPositionEcef() {
return mPositionEcef;
}
@@ -385,7 +418,7 @@
* Returns a {@link VelocityEcef} object that contains estimates of the satellite
* velocity fields in the ECEF coordinate frame.
*/
- @NonNull
+ @Nullable
public VelocityEcef getVelocityEcef() {
return mVelocityEcef;
}
@@ -394,7 +427,7 @@
* Returns a {@link ClockInfo} object that contains estimates of the satellite
* clock info.
*/
- @NonNull
+ @Nullable
public ClockInfo getClockInfo() {
return mClockInfo;
}
@@ -415,11 +448,29 @@
return mTropoDelayMeters;
}
+ /** Returns {@code true} if {@link #getPositionEcef()}, {@link #getVelocityEcef()},
+ * and {@link #getClockInfo()} are valid.
+ */
+ public boolean hasPositionVelocityClockInfo() {
+ return (mFlags & HAS_POSITION_VELOCITY_CLOCK_INFO) != 0;
+ }
+
+ /** Returns {@code true} if {@link #getIonoDelayMeters()} is valid. */
+ public boolean hasIono() {
+ return (mFlags & HAS_IONO) != 0;
+ }
+
+ /** Returns {@code true} if {@link #getTropoDelayMeters()} is valid. */
+ public boolean hasTropo() {
+ return (mFlags & HAS_TROPO) != 0;
+ }
+
public static final @android.annotation.NonNull Creator<SatellitePvt> CREATOR =
new Creator<SatellitePvt>() {
@Override
@Nullable
public SatellitePvt createFromParcel(Parcel in) {
+ int flags = in.readInt();
ClassLoader classLoader = getClass().getClassLoader();
PositionEcef positionEcef = in.readParcelable(classLoader);
VelocityEcef velocityEcef = in.readParcelable(classLoader);
@@ -428,6 +479,7 @@
double tropoDelayMeters = in.readDouble();
return new SatellitePvt(
+ flags,
positionEcef,
velocityEcef,
clockInfo,
@@ -448,6 +500,7 @@
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mFlags);
parcel.writeParcelable(mPositionEcef, flags);
parcel.writeParcelable(mVelocityEcef, flags);
parcel.writeParcelable(mClockInfo, flags);
@@ -458,7 +511,8 @@
@Override
public String toString() {
return "SatellitePvt{"
- + "PositionEcef=" + mPositionEcef
+ + "Flags=" + mFlags
+ + ", PositionEcef=" + mPositionEcef
+ ", VelocityEcef=" + mVelocityEcef
+ ", ClockInfo=" + mClockInfo
+ ", IonoDelayMeters=" + mIonoDelayMeters
@@ -470,13 +524,30 @@
* Builder class for SatellitePvt.
*/
public static final class Builder {
- private PositionEcef mPositionEcef;
- private VelocityEcef mVelocityEcef;
- private ClockInfo mClockInfo;
+ /**
+ * For documentation of below fields, see corresponding fields in {@link
+ * SatellitePvt}.
+ */
+ private int mFlags;
+ @Nullable private PositionEcef mPositionEcef;
+ @Nullable private VelocityEcef mVelocityEcef;
+ @Nullable private ClockInfo mClockInfo;
private double mIonoDelayMeters;
private double mTropoDelayMeters;
/**
+ * Sets a bitmask of fields present in this object
+ *
+ * @param flags int flags
+ * @return Builder builder object
+ */
+ @NonNull
+ public Builder setFlags(int flags) {
+ mFlags = flags;
+ return this;
+ }
+
+ /**
* Set position ECEF.
*
* @param positionEcef position ECEF object
@@ -546,7 +617,7 @@
*/
@NonNull
public SatellitePvt build() {
- return new SatellitePvt(mPositionEcef, mVelocityEcef, mClockInfo,
+ return new SatellitePvt(mFlags, mPositionEcef, mVelocityEcef, mClockInfo,
mIonoDelayMeters, mTropoDelayMeters);
}
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 39b7922..5f6fc17 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -131,6 +131,9 @@
public static final int SAMPLE_RATE_HZ_MIN = native_getMinSampleRate();
private static native int native_getMinSampleRate();
+ /** @hide */
+ public static final int FCC_24 = 24; // fixed channel count 24; do not change.
+
// Expose only the getter method publicly so we can change it in the future
private static final int NUM_STREAM_TYPES = 12;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index b2b2f8e..23d9532 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1720,9 +1720,10 @@
mChannelCount = 0;
break; // channel index configuration only
}
- if (!isMultichannelConfigSupported(channelConfig)) {
- // input channel configuration features unsupported channels
- throw new IllegalArgumentException("Unsupported channel configuration.");
+ if (!isMultichannelConfigSupported(channelConfig, audioFormat)) {
+ throw new IllegalArgumentException(
+ "Unsupported channel mask configuration " + channelConfig
+ + " for encoding " + audioFormat);
}
mChannelMask = channelConfig;
mChannelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
@@ -1730,13 +1731,17 @@
// check the channel index configuration (if present)
mChannelIndexMask = channelIndexMask;
if (mChannelIndexMask != 0) {
- // restrictive: indexMask could allow up to AUDIO_CHANNEL_BITS_LOG2
- final int indexMask = (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1;
- if ((channelIndexMask & ~indexMask) != 0) {
- throw new IllegalArgumentException("Unsupported channel index configuration "
- + channelIndexMask);
+ // As of S, we accept up to 24 channel index mask.
+ final int fullIndexMask = (1 << AudioSystem.FCC_24) - 1;
+ final int channelIndexCount = Integer.bitCount(channelIndexMask);
+ final boolean accepted = (channelIndexMask & ~fullIndexMask) == 0
+ && (!AudioFormat.isEncodingLinearFrames(audioFormat) // compressed OK
+ || channelIndexCount <= AudioSystem.OUT_CHANNEL_COUNT_MAX); // PCM
+ if (!accepted) {
+ throw new IllegalArgumentException(
+ "Unsupported channel index mask configuration " + channelIndexMask
+ + " for encoding " + audioFormat);
}
- int channelIndexCount = Integer.bitCount(channelIndexMask);
if (mChannelCount == 0) {
mChannelCount = channelIndexCount;
} else if (mChannelCount != channelIndexCount) {
@@ -1789,16 +1794,19 @@
* @param channelConfig the mask to validate
* @return false if the AudioTrack can't be used with such a mask
*/
- private static boolean isMultichannelConfigSupported(int channelConfig) {
+ private static boolean isMultichannelConfigSupported(int channelConfig, int encoding) {
// check for unsupported channels
if ((channelConfig & SUPPORTED_OUT_CHANNELS) != channelConfig) {
loge("Channel configuration features unsupported channels");
return false;
}
final int channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
- if (channelCount > AudioSystem.OUT_CHANNEL_COUNT_MAX) {
- loge("Channel configuration contains too many channels " +
- channelCount + ">" + AudioSystem.OUT_CHANNEL_COUNT_MAX);
+ final int channelCountLimit = AudioFormat.isEncodingLinearFrames(encoding)
+ ? AudioSystem.OUT_CHANNEL_COUNT_MAX // PCM limited to OUT_CHANNEL_COUNT_MAX
+ : AudioSystem.FCC_24; // Compressed limited to 24 channels
+ if (channelCount > channelCountLimit) {
+ loge("Channel configuration contains too many channels for encoding "
+ + encoding + "(" + channelCount + " > " + channelCountLimit + ")");
return false;
}
// check for unsupported multichannel combinations:
@@ -2310,7 +2318,7 @@
channelCount = 2;
break;
default:
- if (!isMultichannelConfigSupported(channelConfig)) {
+ if (!isMultichannelConfigSupported(channelConfig, audioFormat)) {
loge("getMinBufferSize(): Invalid channel configuration.");
return ERROR_BAD_VALUE;
} else {
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index 5873677..60f0e9e 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -88,6 +88,7 @@
?audio/x-mpeg mp3
?image/bmp bmp
+?image/gif gif
?image/heic heic
?image/heic-sequence heics
?image/heif heif hif
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index a8c2ea5..93a5444 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -454,28 +454,7 @@
sourceRect.makeInvalid();
}
transaction->setBufferCrop(surfaceControl, sourceRect);
-
- int destW = destRect.width();
- int destH = destRect.height();
- if (destRect.left < 0) {
- destRect.left = 0;
- destRect.right = destW;
- }
- if (destRect.top < 0) {
- destRect.top = 0;
- destRect.bottom = destH;
- }
-
- if (!sourceRect.isEmpty()) {
- float sx = destW / static_cast<float>(sourceRect.width());
- float sy = destH / static_cast<float>(sourceRect.height());
- transaction->setPosition(surfaceControl, destRect.left - (sourceRect.left * sx),
- destRect.top - (sourceRect.top * sy));
- transaction->setMatrix(surfaceControl, sx, 0, 0, sy);
- } else {
- transaction->setPosition(surfaceControl, destRect.left, destRect.top);
- }
-
+ transaction->setDestinationFrame(surfaceControl, destRect);
transaction->setTransform(surfaceControl, transform);
bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 60b0f1e..9fe7929 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -329,7 +329,9 @@
static_cast<minikin::FamilyVariant>(matcher->mFamilyVariant),
1 /* maxRun */);
- const std::shared_ptr<minikin::Font>& font = runs[0].fakedFont.font;
+ const std::shared_ptr<minikin::Font>& font =
+ fc->getBestFont(minikin::U16StringPiece(text, textLength), runs[0], matcher->mFontStyle)
+ .font;
std::unique_ptr<AFont> result = std::make_unique<AFont>();
const android::MinikinFontSkia* minikinFontSkia =
reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get());
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
index ea57ea0..472e0a4 100644
--- a/native/webview/plat_support/draw_functor.cpp
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -192,44 +192,43 @@
int CreateFunctor_v3(void* data, int version,
AwDrawFnFunctorCallbacks* functor_callbacks) {
- static bool callbacks_initialized = false;
- static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = {
- .onSync = &onSync,
- .onContextDestroyed = &onContextDestroyed,
- .onDestroyed = &onDestroyed,
- .removeOverlays = &removeOverlays,
- };
- if (!callbacks_initialized) {
- switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
- case uirenderer::RenderMode::OpenGL_ES:
- webview_functor_callbacks.gles.draw = &draw_gl;
- break;
- case uirenderer::RenderMode::Vulkan:
- webview_functor_callbacks.vk.initialize = &initializeVk;
- webview_functor_callbacks.vk.draw = &drawVk;
- webview_functor_callbacks.vk.postDraw = &postDrawVk;
- break;
+ static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = [] {
+ uirenderer::WebViewFunctorCallbacks ret = {
+ .onSync = &onSync,
+ .onContextDestroyed = &onContextDestroyed,
+ .onDestroyed = &onDestroyed,
+ .removeOverlays = &removeOverlays,
+ };
+ switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
+ case uirenderer::RenderMode::OpenGL_ES:
+ ret.gles.draw = &draw_gl;
+ break;
+ case uirenderer::RenderMode::Vulkan:
+ ret.vk.initialize = &initializeVk;
+ ret.vk.draw = &drawVk;
+ ret.vk.postDraw = &postDrawVk;
+ break;
+ }
+ return ret;
+ }();
+ SupportData* support = new SupportData{
+ .data = data,
+ };
+
+ // These callbacks are available on all versions.
+ support->callbacks = {
+ .on_sync = functor_callbacks->on_sync,
+ .on_context_destroyed = functor_callbacks->on_context_destroyed,
+ .on_destroyed = functor_callbacks->on_destroyed,
+ .draw_gl = functor_callbacks->draw_gl,
+ .init_vk = functor_callbacks->init_vk,
+ .draw_vk = functor_callbacks->draw_vk,
+ .post_draw_vk = functor_callbacks->post_draw_vk,
+ };
+
+ if (version >= 3) {
+ support->callbacks.remove_overlays = functor_callbacks->remove_overlays;
}
- callbacks_initialized = true;
- }
- SupportData* support = new SupportData{
- .data = data,
- };
-
- // These callbacks are available on all versions.
- support->callbacks = {
- .on_sync = functor_callbacks->on_sync,
- .on_context_destroyed = functor_callbacks->on_context_destroyed,
- .on_destroyed = functor_callbacks->on_destroyed,
- .draw_gl = functor_callbacks->draw_gl,
- .init_vk = functor_callbacks->init_vk,
- .draw_vk = functor_callbacks->draw_vk,
- .post_draw_vk = functor_callbacks->post_draw_vk,
- };
-
- if (version >= 3) {
- support->callbacks.remove_overlays = functor_callbacks->remove_overlays;
- }
int functor = uirenderer::WebViewFunctor_create(
support, webview_functor_callbacks,
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 7dd62f6..c3f1e73 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -20,7 +20,7 @@
<string name="chooser_title" msgid="2262294130493605839">"اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديره تطبيق <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"السماح لـ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بإدارة جهاز <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> لديك"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"السماح للتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بإدارة <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_summary" msgid="2059360676631420073">"هذا التطبيق مطلوب لإدارة <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
<string name="consent_yes" msgid="8344487259618762872">"السماح"</string>
<string name="consent_no" msgid="2640796915611404382">"عدم السماح"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index 88c8a4b..ee79921 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -20,7 +20,7 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> کے ذریعے نظم کئے جانے کیلئے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کو منتخب کریں"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"اپنے <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> کا نظم کرنے کے لیے <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اجازت دیں"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"اپنے <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> کا نظم کرنے کے لیے <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اجازت دیں"</string>
<string name="profile_summary" msgid="2059360676631420073">"اس ایپ کو آپ کے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کا نظم کرنے کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
<string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string>
<string name="consent_no" msgid="2640796915611404382">"اجازت نہ دیں"</string>
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index bb93af9..6eb8348 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -59,6 +59,22 @@
],
}
+java_library {
+ name: "framework-connectivity-annotations",
+ sdk_version: "module_current",
+ srcs: [
+ "src/android/net/ConnectivityAnnotations.java",
+ ],
+ libs: [
+ "framework-annotations-lib",
+ "framework-connectivity",
+ ],
+ visibility: [
+ "//frameworks/base:__subpackages__",
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+}
+
java_sdk_library {
name: "framework-connectivity",
sdk_version: "module_current",
@@ -102,7 +118,7 @@
// Tests using hidden APIs
"//cts/tests/netlegacy22.api",
"//external/sl4a:__subpackages__",
- "//frameworks/base/tests/net:__subpackages__",
+ "//frameworks/base/packages/Connectivity/tests:__subpackages__",
"//frameworks/libs/net/common/testutils",
"//frameworks/libs/net/common/tests:__subpackages__",
"//frameworks/opt/telephony/tests/telephonytests",
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 13ecb123..33f4d14 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -70,7 +70,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork();
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo();
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo();
- method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
method @Deprecated public boolean getBackgroundDataSetting();
method @Nullable public android.net.Network getBoundNetworkForProcess();
method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress);
@@ -406,6 +406,7 @@
method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
method public android.net.NetworkRequest.Builder removeCapability(int);
method public android.net.NetworkRequest.Builder removeTransportType(int);
+ method @NonNull public android.net.NetworkRequest.Builder setIncludeOtherUidNetworks(boolean);
method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
}
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 78dff21..6c454bc 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -48,6 +48,7 @@
public class ConnectivitySettingsManager {
method public static void clearGlobalProxy(@NonNull android.content.Context);
+ method @NonNull public static java.util.Set<java.lang.String> getAppsAllowedOnRestrictedNetworks(@NonNull android.content.Context);
method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context);
method public static int getCaptivePortalMode(@NonNull android.content.Context, int);
method @NonNull public static java.time.Duration getConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
@@ -57,7 +58,7 @@
method @Nullable public static android.net.ProxyInfo getGlobalProxy(@NonNull android.content.Context);
method @NonNull public static java.time.Duration getMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
method public static boolean getMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
- method @Nullable public static String getMobileDataPreferredApps(@NonNull android.content.Context);
+ method @NonNull public static java.util.Set<java.lang.Integer> getMobileDataPreferredUids(@NonNull android.content.Context);
method public static int getNetworkAvoidBadWifi(@NonNull android.content.Context);
method @Nullable public static String getNetworkMeteredMultipathPreference(@NonNull android.content.Context);
method public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, int);
@@ -67,6 +68,7 @@
method public static int getPrivateDnsMode(@NonNull android.content.Context);
method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean);
method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setAppsAllowedOnRestrictedNetworks(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.String>);
method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String);
method public static void setCaptivePortalMode(@NonNull android.content.Context, int);
method public static void setConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
@@ -76,7 +78,7 @@
method public static void setGlobalProxy(@NonNull android.content.Context, @NonNull android.net.ProxyInfo);
method public static void setMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
method public static void setMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
- method public static void setMobileDataPreferredApps(@NonNull android.content.Context, @Nullable String);
+ method public static void setMobileDataPreferredUids(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.Integer>);
method public static void setNetworkAvoidBadWifi(@NonNull android.content.Context, int);
method public static void setNetworkMeteredMultipathPreference(@NonNull android.content.Context, @NonNull String);
method public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, @IntRange(from=0) int);
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 52673c9..d1d51da 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -296,7 +296,6 @@
method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
method @NonNull public android.net.NetworkCapabilities build();
- method @NonNull public android.net.NetworkCapabilities.Builder clearAll();
method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
@@ -310,6 +309,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
method @NonNull public android.net.NetworkCapabilities.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
+ method @NonNull public static android.net.NetworkCapabilities.Builder withoutDefaultCapabilities();
}
public class NetworkProvider {
@@ -398,6 +398,7 @@
public abstract class QosFilter {
method @NonNull public abstract android.net.Network getNetwork();
method public abstract boolean matchesLocalAddress(@NonNull java.net.InetAddress, int, int);
+ method public abstract boolean matchesRemoteAddress(@NonNull java.net.InetAddress, int, int);
}
public final class QosSession implements android.os.Parcelable {
@@ -420,6 +421,7 @@
method public int describeContents();
method @NonNull public java.net.InetSocketAddress getLocalSocketAddress();
method @NonNull public android.net.Network getNetwork();
+ method @Nullable public java.net.InetSocketAddress getRemoteSocketAddress();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR;
}
diff --git a/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
index 9bf910b..7478b3e 100644
--- a/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
+++ b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
@@ -64,7 +64,7 @@
filter_code,
};
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -74,7 +74,7 @@
static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
int optval_ignored = 0;
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) !=
0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
@@ -107,7 +107,7 @@
static jint android_net_utils_bindSocketToNetworkHandle(JNIEnv *env, jobject thiz, jobject javaFd,
jlong netHandle) {
- return android_setsocknetwork(netHandle, AFileDescriptor_getFD(env, javaFd));
+ return android_setsocknetwork(netHandle, AFileDescriptor_getFd(env, javaFd));
}
static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
@@ -156,7 +156,7 @@
}
static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
int rcode;
uint8_t buf[MAXPACKETSIZE] = {0};
@@ -182,7 +182,7 @@
}
static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
android_res_cancel(fd);
jniSetFileDescriptorOfFD(env, javaFd, -1);
}
@@ -210,7 +210,7 @@
return NULL;
}
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
struct tcp_repair_window trw = {};
socklen_t size = sizeof(trw);
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
new file mode 100644
index 0000000..eb1faa0
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Type annotations for constants used in the connectivity API surface.
+ *
+ * The annotations are maintained in a separate class so that it can be built as
+ * a separate library that other modules can build against, as Typedef should not
+ * be exposed as SystemApi.
+ *
+ * @hide
+ */
+public final class ConnectivityAnnotations {
+ private ConnectivityAnnotations() {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER,
+ ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY,
+ ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE,
+ })
+ public @interface MultipathPreference {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, value = {
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED,
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED,
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED,
+ })
+ public @interface RestrictBackgroundStatus {}
+}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index df81603..93455bc 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -40,6 +40,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.net.ConnectivityAnnotations.MultipathPreference;
+import android.net.ConnectivityAnnotations.RestrictBackgroundStatus;
import android.net.ConnectivityDiagnosticsManager.DataStallReport.DetectionMethod;
import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.SocketKeepalive.Callback;
@@ -1431,10 +1433,18 @@
* Returns an array of all {@link Network} currently tracked by the
* framework.
*
+ * @deprecated This method does not provide any notification of network state changes, forcing
+ * apps to call it repeatedly. This is inefficient and prone to race conditions.
+ * Apps should use methods such as
+ * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} instead.
+ * Apps that desire to obtain information about networks that do not apply to them
+ * can use {@link NetworkRequest.Builder#setIncludeOtherUidNetworks}.
+ *
* @return an array of {@link Network} objects.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@NonNull
+ @Deprecated
public Network[] getAllNetworks() {
try {
return mService.getAllNetworks();
@@ -4799,16 +4809,6 @@
MULTIPATH_PREFERENCE_RELIABILITY |
MULTIPATH_PREFERENCE_PERFORMANCE;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {
- MULTIPATH_PREFERENCE_HANDOVER,
- MULTIPATH_PREFERENCE_RELIABILITY,
- MULTIPATH_PREFERENCE_PERFORMANCE,
- })
- public @interface MultipathPreference {
- }
-
/**
* Provides a hint to the calling application on whether it is desirable to use the
* multinetwork APIs (e.g., {@link Network#openConnection}, {@link Network#bindSocket}, etc.)
@@ -5030,16 +5030,6 @@
public static final String ACTION_RESTRICT_BACKGROUND_CHANGED =
"android.net.conn.RESTRICT_BACKGROUND_CHANGED";
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = false, value = {
- RESTRICT_BACKGROUND_STATUS_DISABLED,
- RESTRICT_BACKGROUND_STATUS_WHITELISTED,
- RESTRICT_BACKGROUND_STATUS_ENABLED,
- })
- public @interface RestrictBackgroundStatus {
- }
-
/**
* Determines if the calling application is subject to metered network restrictions while
* running on background.
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
index 31e1fb0..1a69099 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
@@ -27,9 +27,12 @@
import android.annotation.SystemApi;
import android.content.ContentResolver;
import android.content.Context;
-import android.net.ConnectivityManager.MultipathPreference;
+import android.net.ConnectivityAnnotations.MultipathPreference;
+import android.os.Process;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Range;
import com.android.net.module.util.ProxyUtils;
@@ -38,6 +41,8 @@
import java.lang.annotation.RetentionPolicy;
import java.time.Duration;
import java.util.List;
+import java.util.Set;
+import java.util.StringJoiner;
/**
* A manager class for connectivity module settings.
@@ -330,12 +335,12 @@
"network_metered_multipath_preference";
/**
- * A list of apps that should go on cellular networks in preference even when higher-priority
+ * A list of uids that should go on cellular networks in preference even when higher-priority
* networks are connected.
*
* @hide
*/
- public static final String MOBILE_DATA_PREFERRED_APPS = "mobile_data_preferred_apps";
+ public static final String MOBILE_DATA_PREFERRED_UIDS = "mobile_data_preferred_uids";
/**
* One of the private DNS modes that indicates the private DNS mode is off.
@@ -369,6 +374,14 @@
private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME_STRING = "hostname";
/**
+ * A list of apps that is allowed on restricted networks.
+ *
+ * @hide
+ */
+ public static final String APPS_ALLOWED_ON_RESTRICTED_NETWORKS =
+ "apps_allowed_on_restricted_networks";
+
+ /**
* Get mobile data activity timeout from {@link Settings}.
*
* @param context The {@link Context} to query the setting.
@@ -991,27 +1004,84 @@
}
/**
- * Get the list of apps(from {@link Settings}) that should go on cellular networks in preference
+ * Get the list of uids(from {@link Settings}) that should go on cellular networks in preference
* even when higher-priority networks are connected.
*
* @param context The {@link Context} to query the setting.
- * @return A list of apps that should go on cellular networks in preference even when
+ * @return A list of uids that should go on cellular networks in preference even when
* higher-priority networks are connected or null if no setting value.
*/
- @Nullable
- public static String getMobileDataPreferredApps(@NonNull Context context) {
- return Settings.Secure.getString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS);
+ @NonNull
+ public static Set<Integer> getMobileDataPreferredUids(@NonNull Context context) {
+ final String uidList = Settings.Secure.getString(
+ context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS);
+ final Set<Integer> uids = new ArraySet<>();
+ if (TextUtils.isEmpty(uidList)) {
+ return uids;
+ }
+ for (String uid : uidList.split(";")) {
+ uids.add(Integer.valueOf(uid));
+ }
+ return uids;
}
/**
- * Set the list of apps(to {@link Settings}) that should go on cellular networks in preference
+ * Set the list of uids(to {@link Settings}) that should go on cellular networks in preference
* even when higher-priority networks are connected.
*
* @param context The {@link Context} to set the setting.
- * @param list A list of apps that should go on cellular networks in preference even when
+ * @param uidList A list of uids that should go on cellular networks in preference even when
* higher-priority networks are connected.
*/
- public static void setMobileDataPreferredApps(@NonNull Context context, @Nullable String list) {
- Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS, list);
+ public static void setMobileDataPreferredUids(@NonNull Context context,
+ @NonNull Set<Integer> uidList) {
+ final StringJoiner joiner = new StringJoiner(";");
+ for (Integer uid : uidList) {
+ if (uid < 0 || UserHandle.getAppId(uid) > Process.LAST_APPLICATION_UID) {
+ throw new IllegalArgumentException("Invalid uid");
+ }
+ joiner.add(uid.toString());
+ }
+ Settings.Secure.putString(
+ context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, joiner.toString());
+ }
+
+ /**
+ * Get the list of apps(from {@link Settings}) that is allowed on restricted networks.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @return A list of apps that is allowed on restricted networks or null if no setting
+ * value.
+ */
+ @NonNull
+ public static Set<String> getAppsAllowedOnRestrictedNetworks(@NonNull Context context) {
+ final String appList = Settings.Secure.getString(
+ context.getContentResolver(), APPS_ALLOWED_ON_RESTRICTED_NETWORKS);
+ if (TextUtils.isEmpty(appList)) {
+ return new ArraySet<>();
+ }
+ return new ArraySet<>(appList.split(";"));
+ }
+
+ /**
+ * Set the list of apps(from {@link Settings}) that is allowed on restricted networks.
+ *
+ * Note: Please refer to android developer guidelines for valid app(package name).
+ * https://developer.android.com/guide/topics/manifest/manifest-element.html#package
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param list A list of apps that is allowed on restricted networks.
+ */
+ public static void setAppsAllowedOnRestrictedNetworks(@NonNull Context context,
+ @NonNull Set<String> list) {
+ final StringJoiner joiner = new StringJoiner(";");
+ for (String app : list) {
+ if (app == null || app.contains(";")) {
+ throw new IllegalArgumentException("Invalid app(package name)");
+ }
+ joiner.add(app);
+ }
+ Settings.Secure.putString(context.getContentResolver(), APPS_ALLOWED_ON_RESTRICTED_NETWORKS,
+ joiner.toString());
}
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 0bafd5b..4932952 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -139,19 +139,13 @@
*/
private String mRequestorPackageName;
- /**
- * Indicates what fields should be redacted from this instance.
- */
- private final @RedactionType long mRedactions;
-
public NetworkCapabilities() {
- mRedactions = REDACT_ALL;
clearAll();
mNetworkCapabilities = DEFAULT_CAPABILITIES;
}
public NetworkCapabilities(NetworkCapabilities nc) {
- this(nc, REDACT_ALL);
+ this(nc, REDACT_NONE);
}
/**
@@ -163,10 +157,12 @@
* @hide
*/
public NetworkCapabilities(@Nullable NetworkCapabilities nc, @RedactionType long redactions) {
- mRedactions = redactions;
if (nc != null) {
set(nc);
}
+ if (mTransportInfo != null) {
+ mTransportInfo = nc.mTransportInfo.makeCopy(redactions);
+ }
}
/**
@@ -175,14 +171,6 @@
* @hide
*/
public void clearAll() {
- // Ensures that the internal copies maintained by the connectivity stack does not set it to
- // anything other than |REDACT_ALL|.
- if (mRedactions != REDACT_ALL) {
- // This is needed because the current redaction mechanism relies on redaction while
- // parceling.
- throw new UnsupportedOperationException(
- "Cannot clear NetworkCapabilities when mRedactions is set");
- }
mNetworkCapabilities = mTransportTypes = mForbiddenNetworkCapabilities = 0;
mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
mNetworkSpecifier = null;
@@ -211,7 +199,7 @@
mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
mNetworkSpecifier = nc.mNetworkSpecifier;
if (nc.getTransportInfo() != null) {
- setTransportInfo(nc.getTransportInfo().makeCopy(mRedactions));
+ setTransportInfo(nc.getTransportInfo());
} else {
setTransportInfo(null);
}
@@ -839,8 +827,17 @@
final int[] originalAdministratorUids = getAdministratorUids();
final TransportInfo originalTransportInfo = getTransportInfo();
clearAll();
- mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS)
- | (1 << TRANSPORT_TEST);
+ if (0 != (originalCapabilities & NET_CAPABILITY_NOT_RESTRICTED)) {
+ // If the test network is not restricted, then it is only allowed to declare some
+ // specific transports. This is to minimize impact on running apps in case an app
+ // run from the shell creates a test a network.
+ mTransportTypes =
+ (originalTransportTypes & UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS)
+ | (1 << TRANSPORT_TEST);
+ } else {
+ // If the test transport is restricted, then it may declare any transport.
+ mTransportTypes = (originalTransportTypes | (1 << TRANSPORT_TEST));
+ }
mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
mNetworkSpecifier = originalSpecifier;
mSignalStrength = originalSignalStrength;
@@ -951,9 +948,10 @@
};
/**
- * Allowed transports on a test network, in addition to TRANSPORT_TEST.
+ * Allowed transports on an unrestricted test network (in addition to TRANSPORT_TEST).
*/
- private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST
+ private static final int UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS =
+ 1 << TRANSPORT_TEST
// Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces
| 1 << TRANSPORT_ETHERNET
// Test VPN networks can be created but their UID ranges must be empty.
@@ -2418,6 +2416,11 @@
return mTransportInfo.getApplicableRedactions();
}
+ private NetworkCapabilities removeDefaultCapabilites() {
+ mNetworkCapabilities &= ~DEFAULT_CAPABILITIES;
+ return this;
+ }
+
/**
* Builder class for NetworkCapabilities.
*
@@ -2454,6 +2457,16 @@
}
/**
+ * Creates a new Builder without the default capabilities.
+ */
+ @NonNull
+ public static Builder withoutDefaultCapabilities() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.removeDefaultCapabilites();
+ return new Builder(nc);
+ }
+
+ /**
* Adds the given transport type.
*
* Multiple transports may be added. Note that when searching for a network to satisfy a
@@ -2461,7 +2474,8 @@
* For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
* {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
* to be selected. This is logically different than
- * {@code NetworkCapabilities.NET_CAPABILITY_*}.
+ * {@code NetworkCapabilities.NET_CAPABILITY_*}. Also note that multiple networks with the
+ * same transport type may be active concurrently.
*
* @param transportType the transport type to be added or removed.
* @return this builder
@@ -2513,17 +2527,6 @@
}
/**
- * Completely clears the contents of this object, removing even the capabilities that are
- * set by default when the object is constructed.
- * @return this builder
- */
- @NonNull
- public Builder clearAll() {
- mCaps.clearAll();
- return this;
- }
-
- /**
* Sets the owner UID.
*
* The default value is {@link Process#INVALID_UID}. Pass this value to reset.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index a384109..a71dbbf 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -287,10 +287,15 @@
}
/**
- * Set the watched UIDs for this request. This will be reset and wiped out unless
- * the calling app holds the CHANGE_NETWORK_STATE permission.
+ * Sets this request to match only networks that apply to the specified UIDs.
*
- * @param uids The watched UIDs as a set of {@code Range<Integer>}, or null for everything.
+ * By default, the set of UIDs is the UID of the calling app, and this request will match
+ * any network that applies to the app. Setting it to {@code null} will observe any
+ * network on the system, even if it does not apply to this app. In this case, any
+ * {@link NetworkSpecifier} set on this request will be redacted or removed to prevent the
+ * application deducing restricted information such as location.
+ *
+ * @param uids The UIDs as a set of {@code Range<Integer>}, or null for everything.
* @return The builder to facilitate chaining.
* @hide
*/
@@ -517,6 +522,30 @@
mNetworkCapabilities.setSubscriptionIds(subIds);
return this;
}
+
+ /**
+ * Specifies whether the built request should also match networks that do not apply to the
+ * calling UID.
+ *
+ * By default, the built request will only match networks that apply to the calling UID.
+ * If this method is called with {@code true}, the built request will match any network on
+ * the system that matches the other parameters of the request. In this case, any
+ * information in the built request that is subject to redaction for security or privacy
+ * purposes, such as a {@link NetworkSpecifier}, will be redacted or removed to prevent the
+ * application deducing sensitive information.
+ *
+ * @param include Whether to match networks that do not apply to the calling UID.
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder setIncludeOtherUidNetworks(boolean include) {
+ if (include) {
+ mNetworkCapabilities.setUids(null);
+ } else {
+ mNetworkCapabilities.setSingleUid(Process.myUid());
+ }
+ return this;
+ }
}
// implement the Parcelable interface
diff --git a/packages/Connectivity/framework/src/android/net/QosFilter.java b/packages/Connectivity/framework/src/android/net/QosFilter.java
index ab55002..957c867 100644
--- a/packages/Connectivity/framework/src/android/net/QosFilter.java
+++ b/packages/Connectivity/framework/src/android/net/QosFilter.java
@@ -71,5 +71,16 @@
*/
public abstract boolean matchesLocalAddress(@NonNull InetAddress address,
int startPort, int endPort);
+
+ /**
+ * Determines whether or not the parameters is a match for the filter.
+ *
+ * @param address the remote address
+ * @param startPort the start of the port range
+ * @param endPort the end of the port range
+ * @return whether the parameters match the remote address of the filter
+ */
+ public abstract boolean matchesRemoteAddress(@NonNull InetAddress address,
+ int startPort, int endPort);
}
diff --git a/packages/Connectivity/framework/src/android/net/QosSocketFilter.java b/packages/Connectivity/framework/src/android/net/QosSocketFilter.java
index 2080e68..69da7f4 100644
--- a/packages/Connectivity/framework/src/android/net/QosSocketFilter.java
+++ b/packages/Connectivity/framework/src/android/net/QosSocketFilter.java
@@ -138,13 +138,26 @@
if (mQosSocketInfo.getLocalSocketAddress() == null) {
return false;
}
-
- return matchesLocalAddress(mQosSocketInfo.getLocalSocketAddress(), address, startPort,
+ return matchesAddress(mQosSocketInfo.getLocalSocketAddress(), address, startPort,
endPort);
}
/**
- * Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)} with the
+ * @inheritDoc
+ */
+ @Override
+ public boolean matchesRemoteAddress(@NonNull final InetAddress address, final int startPort,
+ final int endPort) {
+ if (mQosSocketInfo.getRemoteSocketAddress() == null) {
+ return false;
+ }
+ return matchesAddress(mQosSocketInfo.getRemoteSocketAddress(), address, startPort,
+ endPort);
+ }
+
+ /**
+ * Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)}
+ * and {@link QosSocketFilter#matchesRemoteAddress(InetAddress, int, int)} with the
* filterSocketAddress coming from {@link QosSocketInfo#getLocalSocketAddress()}.
* <p>
* This method exists for testing purposes since {@link QosSocketInfo} couldn't be mocked
@@ -156,7 +169,7 @@
* @param endPort the end of the port range to check
*/
@VisibleForTesting
- public static boolean matchesLocalAddress(@NonNull final InetSocketAddress filterSocketAddress,
+ public static boolean matchesAddress(@NonNull final InetSocketAddress filterSocketAddress,
@NonNull final InetAddress address,
final int startPort, final int endPort) {
return startPort <= filterSocketAddress.getPort()
diff --git a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
index 53d9669..a45d507 100644
--- a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
+++ b/packages/Connectivity/framework/src/android/net/QosSocketInfo.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.ParcelFileDescriptor;
@@ -32,7 +33,8 @@
/**
* Used in conjunction with
* {@link ConnectivityManager#registerQosCallback}
- * in order to receive Qos Sessions related to the local address and port of a bound {@link Socket}.
+ * in order to receive Qos Sessions related to the local address and port of a bound {@link Socket}
+ * and/or remote address and port of a connected {@link Socket}.
*
* @hide
*/
@@ -48,6 +50,9 @@
@NonNull
private final InetSocketAddress mLocalSocketAddress;
+ @Nullable
+ private final InetSocketAddress mRemoteSocketAddress;
+
/**
* The {@link Network} the socket is on.
*
@@ -81,6 +86,18 @@
}
/**
+ * The remote address of the socket passed into {@link QosSocketInfo(Network, Socket)}.
+ * The value does not reflect any changes that occur to the socket after it is first set
+ * in the constructor.
+ *
+ * @return the remote address of the socket if socket is connected, null otherwise
+ */
+ @Nullable
+ public InetSocketAddress getRemoteSocketAddress() {
+ return mRemoteSocketAddress;
+ }
+
+ /**
* Creates a {@link QosSocketInfo} given a {@link Network} and bound {@link Socket}. The
* {@link Socket} must remain bound in order to receive {@link QosSession}s.
*
@@ -95,6 +112,12 @@
mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(socket);
mLocalSocketAddress =
new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
+
+ if (socket.isConnected()) {
+ mRemoteSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
+ } else {
+ mRemoteSocketAddress = null;
+ }
}
/* Parcelable methods */
@@ -102,11 +125,15 @@
mNetwork = Objects.requireNonNull(Network.CREATOR.createFromParcel(in));
mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
- final int addressLength = in.readInt();
- mLocalSocketAddress = readSocketAddress(in, addressLength);
+ final int localAddressLength = in.readInt();
+ mLocalSocketAddress = readSocketAddress(in, localAddressLength);
+
+ final int remoteAddressLength = in.readInt();
+ mRemoteSocketAddress = remoteAddressLength == 0 ? null
+ : readSocketAddress(in, remoteAddressLength);
}
- private InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) {
+ private @NonNull InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) {
final byte[] address = new byte[addressLength];
in.readByteArray(address);
final int port = in.readInt();
@@ -130,10 +157,19 @@
mNetwork.writeToParcel(dest, 0);
mParcelFileDescriptor.writeToParcel(dest, 0);
- final byte[] address = mLocalSocketAddress.getAddress().getAddress();
- dest.writeInt(address.length);
- dest.writeByteArray(address);
+ final byte[] localAddress = mLocalSocketAddress.getAddress().getAddress();
+ dest.writeInt(localAddress.length);
+ dest.writeByteArray(localAddress);
dest.writeInt(mLocalSocketAddress.getPort());
+
+ if (mRemoteSocketAddress == null) {
+ dest.writeInt(0);
+ } else {
+ final byte[] remoteAddress = mRemoteSocketAddress.getAddress().getAddress();
+ dest.writeInt(remoteAddress.length);
+ dest.writeByteArray(remoteAddress);
+ dest.writeInt(mRemoteSocketAddress.getPort());
+ }
}
@NonNull
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index 20ccf06..7265426 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -54,7 +54,7 @@
sdk_version: "system_server_current",
min_sdk_version: "30",
srcs: [
- ":connectivity-service-srcs",
+ "src/**/*.java",
":framework-connectivity-shared-srcs",
":services-connectivity-shared-srcs",
// TODO: move to net-utils-device-common, enable shrink optimization to avoid extra classes
@@ -65,6 +65,7 @@
"stable.core.platform.api.stubs",
"android_system_server_stubs_current",
"framework-annotations-lib",
+ "framework-connectivity-annotations",
"framework-connectivity.impl",
"framework-tethering.stubs.module_lib",
"framework-wifi.stubs.module_lib",
diff --git a/packages/Connectivity/service/lint-baseline.xml b/packages/Connectivity/service/lint-baseline.xml
index 35ea2d3..95c169c 100644
--- a/packages/Connectivity/service/lint-baseline.xml
+++ b/packages/Connectivity/service/lint-baseline.xml
@@ -7,8 +7,8 @@
errorLine1=" if (tm.isDataCapable()) {"
errorLine2=" ~~~~~~~~~~~~~">
<location
- file="frameworks/base/services/core/java/com/android/server/ConnectivityService.java"
- line="781"
+ file="frameworks/base/packages/Connectivity/service/src/com/android/server/ConnectivityService.java"
+ line="787"
column="20"/>
</issue>
@@ -18,8 +18,8 @@
errorLine1=" mUserAllContext.sendStickyBroadcast(intent, options);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
- file="frameworks/base/services/core/java/com/android/server/ConnectivityService.java"
- line="2633"
+ file="frameworks/base/packages/Connectivity/service/src/com/android/server/ConnectivityService.java"
+ line="2681"
column="33"/>
</issue>
@@ -29,8 +29,8 @@
errorLine1=" final int callingVersion = pm.getTargetSdkVersion(callingPackageName);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
- file="frameworks/base/services/core/java/com/android/server/ConnectivityService.java"
- line="5784"
+ file="frameworks/base/packages/Connectivity/service/src/com/android/server/ConnectivityService.java"
+ line="5851"
column="43"/>
</issue>
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java
similarity index 99%
rename from services/core/java/com/android/server/ConnectivityService.java
rename to packages/Connectivity/service/src/com/android/server/ConnectivityService.java
index ae14e37..daf231f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java
@@ -105,12 +105,12 @@
import android.net.CaptivePortal;
import android.net.CaptivePortalData;
import android.net.ConnectionInfo;
+import android.net.ConnectivityAnnotations.RestrictBackgroundStatus;
import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.BlockedReason;
import android.net.ConnectivityManager.NetworkCallback;
-import android.net.ConnectivityManager.RestrictBackgroundStatus;
import android.net.ConnectivityResources;
import android.net.ConnectivitySettingsManager;
import android.net.DataStallReportParcelable;
@@ -133,6 +133,8 @@
import android.net.IpPrefix;
import android.net.LinkProperties;
import android.net.MatchAllNetworkSpecifier;
+import android.net.NativeNetworkConfig;
+import android.net.NativeNetworkType;
import android.net.NattSocketKeepalive;
import android.net.Network;
import android.net.NetworkAgent;
@@ -1043,14 +1045,10 @@
} else {
// ConnectivityService publishes binder service using publishBinderService() with
// no priority assigned will be treated as NORMAL priority. Dumpsys does not send
- // "--dump-priority" arguments to the service. Thus, dump both NORMAL and HIGH to
- // align the legacy design.
+ // "--dump-priority" arguments to the service. Thus, dump NORMAL only to align the
+ // legacy output for dumpsys connectivity.
// TODO: Integrate into signal dump.
dumpNormal(fd, pw, args);
- pw.println();
- pw.println("DUMP OF SERVICE HIGH connectivity");
- pw.println();
- dumpHigh(fd, pw);
}
}
}
@@ -1393,7 +1391,7 @@
// arguments like the handler or the DnsResolver.
// TODO : remove this ; it is probably better handled with a sentinel request.
mNoServiceNetwork = new NetworkAgentInfo(null,
- new Network(NO_SERVICE_NET_ID),
+ new Network(INetd.UNREACHABLE_NET_ID),
new NetworkInfo(TYPE_NONE, 0, "", ""),
new LinkProperties(), new NetworkCapabilities(),
new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null,
@@ -2154,10 +2152,22 @@
private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc,
int callerUid, String callerPackageName) {
+ // There is no need to track the effective UID of the request here. If the caller
+ // lacks the settings permission, the effective UID is the same as the calling ID.
if (!checkSettingsPermission()) {
- // There is no need to track the effective UID of the request here. If the caller lacks
- // the settings permission, the effective UID is the same as the calling ID.
- nc.setSingleUid(callerUid);
+ // Unprivileged apps can only pass in null or their own UID.
+ if (nc.getUids() == null) {
+ // If the caller passes in null, the callback will also match networks that do not
+ // apply to its UID, similarly to what it would see if it called getAllNetworks.
+ // In this case, redact everything in the request immediately. This ensures that the
+ // app is not able to get any redacted information by filing an unredacted request
+ // and observing whether the request matches something.
+ if (nc.getNetworkSpecifier() != null) {
+ nc.setNetworkSpecifier(nc.getNetworkSpecifier().redact());
+ }
+ } else {
+ nc.setSingleUid(callerUid);
+ }
}
nc.setRequestorUidAndPackageName(callerUid, callerPackageName);
nc.setAdministratorUids(new int[0]);
@@ -3821,36 +3831,43 @@
nai.onNetworkDestroyed();
}
- private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
+ private boolean createNativeNetwork(@NonNull NetworkAgentInfo nai) {
try {
// This should never fail. Specifying an already in use NetID will cause failure.
- if (networkAgent.isVPN()) {
- mNetd.networkCreateVpn(networkAgent.network.getNetId(),
- (networkAgent.networkAgentConfig == null
- || !networkAgent.networkAgentConfig.allowBypass));
+ final NativeNetworkConfig config;
+ if (nai.isVPN()) {
+ if (getVpnType(nai) == VpnManager.TYPE_VPN_NONE) {
+ Log.wtf(TAG, "Unable to get VPN type from network " + nai.toShortString());
+ return false;
+ }
+ config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.VIRTUAL,
+ INetd.PERMISSION_NONE,
+ (nai.networkAgentConfig == null || !nai.networkAgentConfig.allowBypass),
+ getVpnType(nai));
} else {
- mNetd.networkCreatePhysical(networkAgent.network.getNetId(),
- getNetworkPermission(networkAgent.networkCapabilities));
+ config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.PHYSICAL,
+ getNetworkPermission(nai.networkCapabilities), /*secure=*/ false,
+ VpnManager.TYPE_VPN_NONE);
}
- mDnsResolver.createNetworkCache(networkAgent.network.getNetId());
- mDnsManager.updateTransportsForNetwork(networkAgent.network.getNetId(),
- networkAgent.networkCapabilities.getTransportTypes());
+ mNetd.networkCreate(config);
+ mDnsResolver.createNetworkCache(nai.network.getNetId());
+ mDnsManager.updateTransportsForNetwork(nai.network.getNetId(),
+ nai.networkCapabilities.getTransportTypes());
return true;
} catch (RemoteException | ServiceSpecificException e) {
- loge("Error creating network " + networkAgent.network.getNetId() + ": "
- + e.getMessage());
+ loge("Error creating network " + nai.toShortString() + ": " + e.getMessage());
return false;
}
}
- private void destroyNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
+ private void destroyNativeNetwork(@NonNull NetworkAgentInfo nai) {
try {
- mNetd.networkDestroy(networkAgent.network.getNetId());
+ mNetd.networkDestroy(nai.network.getNetId());
} catch (RemoteException | ServiceSpecificException e) {
loge("Exception destroying network(networkDestroy): " + e);
}
try {
- mDnsResolver.destroyNetworkCache(networkAgent.network.getNetId());
+ mDnsResolver.destroyNetworkCache(nai.network.getNetId());
} catch (RemoteException | ServiceSpecificException e) {
loge("Exception destroying network: " + e);
}
@@ -6457,8 +6474,6 @@
// Request used to optionally keep vehicle internal network always active
private final NetworkRequest mDefaultVehicleRequest;
- // TODO replace with INetd.UNREACHABLE_NET_ID when available.
- private static final int NO_SERVICE_NET_ID = 52;
// Sentinel NAI used to direct apps with default networks that should have no connectivity to a
// network with no service. This NAI should never be matched against, nor should any public API
// ever return the associated network. For this reason, this NAI is not in the list of available
@@ -9134,7 +9149,8 @@
}
private NetworkCapabilities getNetworkCapabilitiesWithoutUids(@NonNull NetworkCapabilities nc) {
- final NetworkCapabilities sanitized = new NetworkCapabilities(nc);
+ final NetworkCapabilities sanitized = new NetworkCapabilities(nc,
+ NetworkCapabilities.REDACT_ALL);
sanitized.setUids(null);
sanitized.setAdministratorUids(new int[0]);
sanitized.setOwnerUid(Process.INVALID_UID);
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/packages/Connectivity/service/src/com/android/server/ConnectivityServiceInitializer.java
similarity index 100%
rename from services/core/java/com/android/server/ConnectivityServiceInitializer.java
rename to packages/Connectivity/service/src/com/android/server/ConnectivityServiceInitializer.java
diff --git a/services/core/java/com/android/server/NetIdManager.java b/packages/Connectivity/service/src/com/android/server/NetIdManager.java
similarity index 100%
rename from services/core/java/com/android/server/NetIdManager.java
rename to packages/Connectivity/service/src/com/android/server/NetIdManager.java
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/packages/Connectivity/service/src/com/android/server/TestNetworkService.java
similarity index 100%
rename from services/core/java/com/android/server/TestNetworkService.java
rename to packages/Connectivity/service/src/com/android/server/TestNetworkService.java
diff --git a/services/core/java/com/android/server/connectivity/AutodestructReference.java b/packages/Connectivity/service/src/com/android/server/connectivity/AutodestructReference.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/AutodestructReference.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/AutodestructReference.java
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/packages/Connectivity/service/src/com/android/server/connectivity/DnsManager.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/DnsManager.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/DnsManager.java
diff --git a/services/core/java/com/android/server/connectivity/FullScore.java b/packages/Connectivity/service/src/com/android/server/connectivity/FullScore.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/FullScore.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/FullScore.java
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/KeepaliveTracker.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/KeepaliveTracker.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/KeepaliveTracker.java
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/packages/Connectivity/service/src/com/android/server/connectivity/LingerMonitor.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/LingerMonitor.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/LingerMonitor.java
diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/packages/Connectivity/service/src/com/android/server/connectivity/MockableSystemProperties.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/MockableSystemProperties.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/MockableSystemProperties.java
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/packages/Connectivity/service/src/com/android/server/connectivity/Nat464Xlat.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/Nat464Xlat.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/Nat464Xlat.java
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkAgentInfo.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/NetworkAgentInfo.java
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkDiagnostics.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/NetworkDiagnostics.java
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkNotificationManager.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/NetworkNotificationManager.java
diff --git a/services/core/java/com/android/server/connectivity/NetworkOffer.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkOffer.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/NetworkOffer.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/NetworkOffer.java
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkRanker.java
similarity index 76%
rename from services/core/java/com/android/server/connectivity/NetworkRanker.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/NetworkRanker.java
index e839837..d7eb9c8 100644
--- a/services/core/java/com/android/server/connectivity/NetworkRanker.java
+++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkRanker.java
@@ -108,7 +108,58 @@
}
}
- @Nullable private <T extends Scoreable> T getBestNetworkByPolicy(
+ private <T extends Scoreable> boolean isBadWiFi(@NonNull final T candidate) {
+ return candidate.getScore().hasPolicy(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD)
+ && candidate.getCapsNoCopy().hasTransport(TRANSPORT_WIFI);
+ }
+
+ /**
+ * Apply the "yield to bad WiFi" policy.
+ *
+ * This function must run immediately after the validation policy.
+ *
+ * If any of the accepted networks has the "yield to bad WiFi" policy AND there are some
+ * bad WiFis in the rejected list, then move the networks with the policy to the rejected
+ * list. If this leaves no accepted network, then move the bad WiFis back to the accepted list.
+ *
+ * This function returns nothing, but will have updated accepted and rejected in-place.
+ *
+ * @param accepted networks accepted by the validation policy
+ * @param rejected networks rejected by the validation policy
+ */
+ private <T extends Scoreable> void applyYieldToBadWifiPolicy(@NonNull ArrayList<T> accepted,
+ @NonNull ArrayList<T> rejected) {
+ if (!CollectionUtils.any(accepted, n -> n.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI))) {
+ // No network with the policy : do nothing.
+ return;
+ }
+ if (!CollectionUtils.any(rejected, n -> isBadWiFi(n))) {
+ // No bad WiFi : do nothing.
+ return;
+ }
+ if (CollectionUtils.all(accepted, n -> n.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI))) {
+ // All validated networks yield to bad WiFis : keep bad WiFis alongside with the
+ // yielders. This is important because the yielders need to be compared to the bad
+ // wifis by the following policies (e.g. exiting).
+ final ArrayList<T> acceptedYielders = new ArrayList<>(accepted);
+ final ArrayList<T> rejectedWithBadWiFis = new ArrayList<>(rejected);
+ partitionInto(rejectedWithBadWiFis, n -> isBadWiFi(n), accepted, rejected);
+ accepted.addAll(acceptedYielders);
+ return;
+ }
+ // Only some of the validated networks yield to bad WiFi : keep only the ones who don't.
+ final ArrayList<T> acceptedWithYielders = new ArrayList<>(accepted);
+ partitionInto(acceptedWithYielders, n -> !n.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI),
+ accepted, rejected);
+ }
+
+ /**
+ * Get the best network among a list of candidates according to policy.
+ * @param candidates the candidates
+ * @param currentSatisfier the current satisfier, or null if none
+ * @return the best network
+ */
+ @Nullable public <T extends Scoreable> T getBestNetworkByPolicy(
@NonNull List<T> candidates,
@Nullable final T currentSatisfier) {
// Used as working areas.
@@ -148,24 +199,15 @@
if (accepted.size() == 1) return accepted.get(0);
if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
- // Yield to bad wifi policy : if any wifi has ever been validated (even if it's now
- // unvalidated), and unless it's been explicitly avoided when bad in UI, then keep only
- // networks that don't yield to such a wifi network.
- final boolean anyWiFiEverValidated = CollectionUtils.any(candidates,
- nai -> nai.getScore().hasPolicy(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD)
- && nai.getCapsNoCopy().hasTransport(TRANSPORT_WIFI));
- if (anyWiFiEverValidated) {
- partitionInto(candidates, nai -> !nai.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI),
- accepted, rejected);
- if (accepted.size() == 1) return accepted.get(0);
- if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
- }
-
// If any network is validated (or should be accepted even if it's not validated), then
// don't choose one that isn't.
partitionInto(candidates, nai -> nai.getScore().hasPolicy(POLICY_IS_VALIDATED)
|| nai.getScore().hasPolicy(POLICY_ACCEPT_UNVALIDATED),
accepted, rejected);
+ // Yield to bad wifi policy : if any network has the "yield to bad WiFi" policy and
+ // there are bad WiFis connected, then accept the bad WiFis and reject the networks with
+ // the policy.
+ applyYieldToBadWifiPolicy(accepted, rejected);
if (accepted.size() == 1) return accepted.get(0);
if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
@@ -194,16 +236,26 @@
// subscription with the same transport.
partitionInto(candidates, nai -> nai.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY),
accepted, rejected);
- for (final Scoreable defaultSubNai : accepted) {
- // Remove all networks without the DEFAULT_SUBSCRIPTION policy and the same transports
- // as a network that has it.
- final int[] transports = defaultSubNai.getCapsNoCopy().getTransportTypes();
- candidates.removeIf(nai -> !nai.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY)
- && Arrays.equals(transports, nai.getCapsNoCopy().getTransportTypes()));
+ if (accepted.size() > 0) {
+ // Some networks are primary for their transport. For each transport, keep only the
+ // primary, but also keep all networks for which there isn't a primary (which are now
+ // in the |rejected| array).
+ // So for each primary network, remove from |rejected| all networks with the same
+ // transports as one of the primary networks. The remaining networks should be accepted.
+ for (final T defaultSubNai : accepted) {
+ final int[] transports = defaultSubNai.getCapsNoCopy().getTransportTypes();
+ rejected.removeIf(
+ nai -> Arrays.equals(transports, nai.getCapsNoCopy().getTransportTypes()));
+ }
+ // Now the |rejected| list contains networks with transports for which there isn't
+ // a primary network. Add them back to the candidates.
+ accepted.addAll(rejected);
+ candidates = new ArrayList<>(accepted);
}
if (1 == candidates.size()) return candidates.get(0);
- // It's guaranteed candidates.size() > 0 because there is at least one with the
- // TRANSPORT_PRIMARY policy and only those without it were removed.
+ // If there were no primary network, then candidates.size() > 0 because it didn't
+ // change from the previous result. If there were, it's guaranteed candidates.size() > 0
+ // because accepted.size() > 0 above.
// If some of the networks have a better transport than others, keep only the ones with
// the best transports.
diff --git a/services/core/java/com/android/server/connectivity/OsCompat.java b/packages/Connectivity/service/src/com/android/server/connectivity/OsCompat.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/OsCompat.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/OsCompat.java
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java
similarity index 99%
rename from services/core/java/com/android/server/connectivity/PermissionMonitor.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java
index 28f208b..5886b1a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -338,7 +338,8 @@
return currentPermission;
}
try {
- final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
+ final PackageInfo app = mPackageManager.getPackageInfo(name,
+ GET_PERMISSIONS | MATCH_ANY_USER);
final boolean isNetwork = hasNetworkPermission(app);
final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
if (isNetwork || hasRestrictedPermission) {
@@ -664,6 +665,7 @@
break;
case INetd.PERMISSION_UNINSTALLED:
uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
default:
Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
+ netdPermissionsAppIds.keyAt(i));
diff --git a/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java b/packages/Connectivity/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/ProxyTracker.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/ProxyTracker.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/ProxyTracker.java
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackTracker.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/QosCallbackTracker.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackTracker.java
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/packages/Connectivity/service/src/com/android/server/connectivity/TcpKeepaliveController.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/TcpKeepaliveController.java
diff --git a/packages/Connectivity/test/Android.bp b/packages/Connectivity/test/Android.bp
deleted file mode 100644
index a6cad2e..0000000
--- a/packages/Connectivity/test/Android.bp
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-// defaults for tests that need to build against framework-connectivity's @hide APIs
-// Only usable from targets that have visibility on framework-connectivity.impl.
-// Instead of using this, consider avoiding to depend on hidden connectivity APIs in
-// tests.
-java_defaults {
- name: "framework-connectivity-test-defaults",
- sdk_version: "core_platform", // tests can use @CorePlatformApi's
- libs: [
- // order matters: classes in framework-connectivity are resolved before framework,
- // meaning @hide APIs in framework-connectivity are resolved before @SystemApi
- // stubs in framework
- "framework-connectivity.impl",
- "framework",
-
- // if sdk_version="" this gets automatically included, but here we need to add manually.
- "framework-res",
- ],
-}
diff --git a/tests/net/OWNERS b/packages/Connectivity/tests/OWNERS
similarity index 100%
rename from tests/net/OWNERS
rename to packages/Connectivity/tests/OWNERS
diff --git a/tests/net/TEST_MAPPING b/packages/Connectivity/tests/TEST_MAPPING
similarity index 100%
rename from tests/net/TEST_MAPPING
rename to packages/Connectivity/tests/TEST_MAPPING
diff --git a/tests/net/common/Android.bp b/packages/Connectivity/tests/common/Android.bp
similarity index 66%
rename from tests/net/common/Android.bp
rename to packages/Connectivity/tests/common/Android.bp
index 439665b..7331453 100644
--- a/tests/net/common/Android.bp
+++ b/packages/Connectivity/tests/common/Android.bp
@@ -46,3 +46,22 @@
"android.test.base.stubs",
],
}
+
+// defaults for tests that need to build against framework-connectivity's @hide APIs
+// Only usable from targets that have visibility on framework-connectivity.impl.
+// Instead of using this, consider avoiding to depend on hidden connectivity APIs in
+// tests.
+java_defaults {
+ name: "framework-connectivity-test-defaults",
+ sdk_version: "core_platform", // tests can use @CorePlatformApi's
+ libs: [
+ // order matters: classes in framework-connectivity are resolved before framework,
+ // meaning @hide APIs in framework-connectivity are resolved before @SystemApi
+ // stubs in framework
+ "framework-connectivity.impl",
+ "framework",
+
+ // if sdk_version="" this gets automatically included, but here we need to add manually.
+ "framework-res",
+ ],
+}
diff --git a/tests/net/common/java/ParseExceptionTest.kt b/packages/Connectivity/tests/common/java/ParseExceptionTest.kt
similarity index 100%
rename from tests/net/common/java/ParseExceptionTest.kt
rename to packages/Connectivity/tests/common/java/ParseExceptionTest.kt
diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/packages/Connectivity/tests/common/java/android/net/CaptivePortalDataTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/CaptivePortalDataTest.kt
rename to packages/Connectivity/tests/common/java/android/net/CaptivePortalDataTest.kt
diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/packages/Connectivity/tests/common/java/android/net/CaptivePortalTest.java
similarity index 100%
rename from tests/net/common/java/android/net/CaptivePortalTest.java
rename to packages/Connectivity/tests/common/java/android/net/CaptivePortalTest.java
diff --git a/tests/net/common/java/android/net/DependenciesTest.java b/packages/Connectivity/tests/common/java/android/net/DependenciesTest.java
similarity index 100%
rename from tests/net/common/java/android/net/DependenciesTest.java
rename to packages/Connectivity/tests/common/java/android/net/DependenciesTest.java
diff --git a/tests/net/common/java/android/net/DhcpInfoTest.java b/packages/Connectivity/tests/common/java/android/net/DhcpInfoTest.java
similarity index 100%
rename from tests/net/common/java/android/net/DhcpInfoTest.java
rename to packages/Connectivity/tests/common/java/android/net/DhcpInfoTest.java
diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/packages/Connectivity/tests/common/java/android/net/IpPrefixTest.java
similarity index 100%
rename from tests/net/common/java/android/net/IpPrefixTest.java
rename to packages/Connectivity/tests/common/java/android/net/IpPrefixTest.java
diff --git a/tests/net/common/java/android/net/KeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/KeepalivePacketDataTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/KeepalivePacketDataTest.kt
rename to packages/Connectivity/tests/common/java/android/net/KeepalivePacketDataTest.kt
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/packages/Connectivity/tests/common/java/android/net/LinkAddressTest.java
similarity index 100%
rename from tests/net/common/java/android/net/LinkAddressTest.java
rename to packages/Connectivity/tests/common/java/android/net/LinkAddressTest.java
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/packages/Connectivity/tests/common/java/android/net/LinkPropertiesTest.java
similarity index 100%
rename from tests/net/common/java/android/net/LinkPropertiesTest.java
rename to packages/Connectivity/tests/common/java/android/net/LinkPropertiesTest.java
diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/packages/Connectivity/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
rename to packages/Connectivity/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt
diff --git a/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt
rename to packages/Connectivity/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkAgentConfigTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/NetworkAgentConfigTest.kt
rename to packages/Connectivity/tests/common/java/android/net/NetworkAgentConfigTest.kt
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java
similarity index 99%
rename from tests/net/common/java/android/net/NetworkCapabilitiesTest.java
rename to packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java
index 1c8a1bf..9efdde4 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -341,7 +341,7 @@
private void testParcelSane(NetworkCapabilities cap) {
if (isAtLeastS()) {
- assertParcelSane(cap, 17);
+ assertParcelSane(cap, 16);
} else if (isAtLeastR()) {
assertParcelSane(cap, 15);
} else {
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkProviderTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/NetworkProviderTest.kt
rename to packages/Connectivity/tests/common/java/android/net/NetworkProviderTest.kt
diff --git a/tests/net/common/java/android/net/NetworkSpecifierTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkSpecifierTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/NetworkSpecifierTest.kt
rename to packages/Connectivity/tests/common/java/android/net/NetworkSpecifierTest.kt
diff --git a/tests/net/common/java/android/net/NetworkStackTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkStackTest.java
similarity index 100%
rename from tests/net/common/java/android/net/NetworkStackTest.java
rename to packages/Connectivity/tests/common/java/android/net/NetworkStackTest.java
diff --git a/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkStateSnapshotTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/NetworkStateSnapshotTest.kt
rename to packages/Connectivity/tests/common/java/android/net/NetworkStateSnapshotTest.kt
diff --git a/tests/net/common/java/android/net/NetworkTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkTest.java
similarity index 100%
rename from tests/net/common/java/android/net/NetworkTest.java
rename to packages/Connectivity/tests/common/java/android/net/NetworkTest.java
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/packages/Connectivity/tests/common/java/android/net/OemNetworkPreferencesTest.java
similarity index 100%
rename from tests/net/common/java/android/net/OemNetworkPreferencesTest.java
rename to packages/Connectivity/tests/common/java/android/net/OemNetworkPreferencesTest.java
diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/packages/Connectivity/tests/common/java/android/net/RouteInfoTest.java
similarity index 100%
rename from tests/net/common/java/android/net/RouteInfoTest.java
rename to packages/Connectivity/tests/common/java/android/net/RouteInfoTest.java
diff --git a/tests/net/common/java/android/net/StaticIpConfigurationTest.java b/packages/Connectivity/tests/common/java/android/net/StaticIpConfigurationTest.java
similarity index 100%
rename from tests/net/common/java/android/net/StaticIpConfigurationTest.java
rename to packages/Connectivity/tests/common/java/android/net/StaticIpConfigurationTest.java
diff --git a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
rename to packages/Connectivity/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt
diff --git a/tests/net/common/java/android/net/UidRangeTest.java b/packages/Connectivity/tests/common/java/android/net/UidRangeTest.java
similarity index 100%
rename from tests/net/common/java/android/net/UidRangeTest.java
rename to packages/Connectivity/tests/common/java/android/net/UidRangeTest.java
diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
rename to packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt
diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
similarity index 100%
rename from tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
rename to packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
diff --git a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ApfProgramEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/ApfProgramEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ApfStatsTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/ApfStatsTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/ApfStatsTest.kt
diff --git a/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpClientEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/DhcpClientEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpErrorEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/DhcpErrorEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java b/packages/Connectivity/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
similarity index 100%
rename from tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java
rename to packages/Connectivity/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
diff --git a/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/IpManagerEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/IpManagerEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/IpManagerEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/NetworkEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/NetworkEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/NetworkEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/NetworkEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/RaEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/RaEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/RaEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/RaEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt
diff --git a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt b/packages/Connectivity/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
rename to packages/Connectivity/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt
diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/packages/Connectivity/tests/common/java/android/net/util/SocketUtilsTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/util/SocketUtilsTest.kt
rename to packages/Connectivity/tests/common/java/android/net/util/SocketUtilsTest.kt
diff --git a/tests/net/deflake/Android.bp b/packages/Connectivity/tests/deflake/Android.bp
similarity index 100%
rename from tests/net/deflake/Android.bp
rename to packages/Connectivity/tests/deflake/Android.bp
diff --git a/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt b/packages/Connectivity/tests/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt
similarity index 100%
rename from tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt
rename to packages/Connectivity/tests/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt
diff --git a/tests/net/integration/Android.bp b/packages/Connectivity/tests/integration/Android.bp
similarity index 100%
rename from tests/net/integration/Android.bp
rename to packages/Connectivity/tests/integration/Android.bp
diff --git a/tests/net/integration/AndroidManifest.xml b/packages/Connectivity/tests/integration/AndroidManifest.xml
similarity index 100%
rename from tests/net/integration/AndroidManifest.xml
rename to packages/Connectivity/tests/integration/AndroidManifest.xml
diff --git a/tests/net/integration/res/values/config.xml b/packages/Connectivity/tests/integration/res/values/config.xml
similarity index 100%
rename from tests/net/integration/res/values/config.xml
rename to packages/Connectivity/tests/integration/res/values/config.xml
diff --git a/tests/net/integration/src/android/net/TestNetworkStackClient.kt b/packages/Connectivity/tests/integration/src/android/net/TestNetworkStackClient.kt
similarity index 100%
rename from tests/net/integration/src/android/net/TestNetworkStackClient.kt
rename to packages/Connectivity/tests/integration/src/android/net/TestNetworkStackClient.kt
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
diff --git a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt b/packages/Connectivity/tests/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
similarity index 100%
rename from tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
rename to packages/Connectivity/tests/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java
similarity index 98%
rename from tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
rename to packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index 40d068d..17db179 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -381,4 +381,8 @@
}
}
}
+
+ public boolean isBypassableVpn() {
+ return mNetworkAgentConfig.isBypassableVpn();
+ }
}
diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/packages/Connectivity/tests/integration/util/com/android/server/TestNetIdManager.kt
similarity index 100%
rename from tests/net/integration/util/com/android/server/TestNetIdManager.kt
rename to packages/Connectivity/tests/integration/util/com/android/server/TestNetIdManager.kt
diff --git a/tests/net/smoketest/Android.bp b/packages/Connectivity/tests/smoketest/Android.bp
similarity index 100%
rename from tests/net/smoketest/Android.bp
rename to packages/Connectivity/tests/smoketest/Android.bp
diff --git a/tests/net/smoketest/AndroidManifest.xml b/packages/Connectivity/tests/smoketest/AndroidManifest.xml
similarity index 100%
rename from tests/net/smoketest/AndroidManifest.xml
rename to packages/Connectivity/tests/smoketest/AndroidManifest.xml
diff --git a/tests/net/smoketest/AndroidTest.xml b/packages/Connectivity/tests/smoketest/AndroidTest.xml
similarity index 100%
rename from tests/net/smoketest/AndroidTest.xml
rename to packages/Connectivity/tests/smoketest/AndroidTest.xml
diff --git a/tests/net/smoketest/java/SmokeTest.java b/packages/Connectivity/tests/smoketest/java/SmokeTest.java
similarity index 100%
rename from tests/net/smoketest/java/SmokeTest.java
rename to packages/Connectivity/tests/smoketest/java/SmokeTest.java
diff --git a/tests/net/Android.bp b/packages/Connectivity/tests/unit/Android.bp
similarity index 100%
rename from tests/net/Android.bp
rename to packages/Connectivity/tests/unit/Android.bp
diff --git a/tests/net/AndroidManifest.xml b/packages/Connectivity/tests/unit/AndroidManifest.xml
similarity index 100%
rename from tests/net/AndroidManifest.xml
rename to packages/Connectivity/tests/unit/AndroidManifest.xml
diff --git a/tests/net/AndroidTest.xml b/packages/Connectivity/tests/unit/AndroidTest.xml
similarity index 100%
rename from tests/net/AndroidTest.xml
rename to packages/Connectivity/tests/unit/AndroidTest.xml
diff --git a/tests/net/jarjar-rules.txt b/packages/Connectivity/tests/unit/jarjar-rules.txt
similarity index 100%
rename from tests/net/jarjar-rules.txt
rename to packages/Connectivity/tests/unit/jarjar-rules.txt
diff --git a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java b/packages/Connectivity/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
similarity index 100%
rename from tests/net/java/android/app/usage/NetworkStatsManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/ConnectivityDiagnosticsManagerTest.java
similarity index 100%
rename from tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/net/ConnectivityDiagnosticsManagerTest.java
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/ConnectivityManagerTest.java
similarity index 100%
rename from tests/net/java/android/net/ConnectivityManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/net/ConnectivityManagerTest.java
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java
similarity index 100%
rename from tests/net/java/android/net/Ikev2VpnProfileTest.java
rename to packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/packages/Connectivity/tests/unit/java/android/net/IpMemoryStoreTest.java
similarity index 100%
rename from tests/net/java/android/net/IpMemoryStoreTest.java
rename to packages/Connectivity/tests/unit/java/android/net/IpMemoryStoreTest.java
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecAlgorithmTest.java
similarity index 100%
rename from tests/net/java/android/net/IpSecAlgorithmTest.java
rename to packages/Connectivity/tests/unit/java/android/net/IpSecAlgorithmTest.java
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecConfigTest.java
similarity index 100%
rename from tests/net/java/android/net/IpSecConfigTest.java
rename to packages/Connectivity/tests/unit/java/android/net/IpSecConfigTest.java
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecManagerTest.java
similarity index 100%
rename from tests/net/java/android/net/IpSecManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/net/IpSecManagerTest.java
diff --git a/tests/net/java/android/net/IpSecTransformTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecTransformTest.java
similarity index 100%
rename from tests/net/java/android/net/IpSecTransformTest.java
rename to packages/Connectivity/tests/unit/java/android/net/IpSecTransformTest.java
diff --git a/tests/net/java/android/net/KeepalivePacketDataUtilTest.java b/packages/Connectivity/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
similarity index 100%
rename from tests/net/java/android/net/KeepalivePacketDataUtilTest.java
rename to packages/Connectivity/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
diff --git a/tests/net/java/android/net/MacAddressTest.java b/packages/Connectivity/tests/unit/java/android/net/MacAddressTest.java
similarity index 100%
rename from tests/net/java/android/net/MacAddressTest.java
rename to packages/Connectivity/tests/unit/java/android/net/MacAddressTest.java
diff --git a/tests/net/java/android/net/NetworkIdentityTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkIdentityTest.kt
similarity index 100%
rename from tests/net/java/android/net/NetworkIdentityTest.kt
rename to packages/Connectivity/tests/unit/java/android/net/NetworkIdentityTest.kt
diff --git a/tests/net/java/android/net/NetworkStatsHistoryTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsHistoryTest.java
similarity index 100%
rename from tests/net/java/android/net/NetworkStatsHistoryTest.java
rename to packages/Connectivity/tests/unit/java/android/net/NetworkStatsHistoryTest.java
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsTest.java
similarity index 100%
rename from tests/net/java/android/net/NetworkStatsTest.java
rename to packages/Connectivity/tests/unit/java/android/net/NetworkStatsTest.java
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt
similarity index 100%
rename from tests/net/java/android/net/NetworkTemplateTest.kt
rename to packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkUtilsTest.java
similarity index 100%
rename from tests/net/java/android/net/NetworkUtilsTest.java
rename to packages/Connectivity/tests/unit/java/android/net/NetworkUtilsTest.java
diff --git a/tests/net/java/android/net/QosSocketFilterTest.java b/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java
similarity index 89%
rename from tests/net/java/android/net/QosSocketFilterTest.java
rename to packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java
index ad58960..40f8f1b 100644
--- a/tests/net/java/android/net/QosSocketFilterTest.java
+++ b/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java
@@ -35,7 +35,7 @@
public void testPortExactMatch() {
final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
- assertTrue(QosSocketFilter.matchesLocalAddress(
+ assertTrue(QosSocketFilter.matchesAddress(
new InetSocketAddress(addressA, 10), addressB, 10, 10));
}
@@ -44,7 +44,7 @@
public void testPortLessThanStart() {
final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
- assertFalse(QosSocketFilter.matchesLocalAddress(
+ assertFalse(QosSocketFilter.matchesAddress(
new InetSocketAddress(addressA, 8), addressB, 10, 10));
}
@@ -52,7 +52,7 @@
public void testPortGreaterThanEnd() {
final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
- assertFalse(QosSocketFilter.matchesLocalAddress(
+ assertFalse(QosSocketFilter.matchesAddress(
new InetSocketAddress(addressA, 18), addressB, 10, 10));
}
@@ -60,7 +60,7 @@
public void testPortBetweenStartAndEnd() {
final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
- assertTrue(QosSocketFilter.matchesLocalAddress(
+ assertTrue(QosSocketFilter.matchesAddress(
new InetSocketAddress(addressA, 10), addressB, 8, 18));
}
@@ -68,7 +68,7 @@
public void testAddressesDontMatch() {
final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.5");
- assertFalse(QosSocketFilter.matchesLocalAddress(
+ assertFalse(QosSocketFilter.matchesAddress(
new InetSocketAddress(addressA, 10), addressB, 10, 10));
}
}
diff --git a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java b/packages/Connectivity/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java
similarity index 100%
rename from tests/net/java/android/net/TelephonyNetworkSpecifierTest.java
rename to packages/Connectivity/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java
diff --git a/tests/net/java/android/net/VpnManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java
similarity index 85%
rename from tests/net/java/android/net/VpnManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java
index c548e30..3135062 100644
--- a/tests/net/java/android/net/VpnManagerTest.java
+++ b/packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java
@@ -28,11 +28,13 @@
import android.content.ComponentName;
import android.content.Intent;
import android.test.mock.MockContext;
+import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.net.VpnProfile;
+import com.android.internal.util.MessageUtils;
import org.junit.Before;
import org.junit.Test;
@@ -119,4 +121,18 @@
.setAuthPsk(PSK_BYTES)
.build();
}
+
+ @Test
+ public void testVpnTypesEqual() throws Exception {
+ SparseArray<String> vmVpnTypes = MessageUtils.findMessageNames(
+ new Class[] { VpnManager.class }, new String[]{ "TYPE_VPN_" });
+ SparseArray<String> nativeVpnType = MessageUtils.findMessageNames(
+ new Class[] { NativeVpnType.class }, new String[]{ "" });
+
+ // TYPE_VPN_NONE = -1 is only defined in VpnManager.
+ assertEquals(vmVpnTypes.size() - 1, nativeVpnType.size());
+ for (int i = VpnManager.TYPE_VPN_SERVICE; i < vmVpnTypes.size(); i++) {
+ assertEquals(vmVpnTypes.get(i), "TYPE_VPN_" + nativeVpnType.get(i));
+ }
+ }
}
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/packages/Connectivity/tests/unit/java/android/net/VpnTransportInfoTest.java
similarity index 100%
rename from tests/net/java/android/net/VpnTransportInfoTest.java
rename to packages/Connectivity/tests/unit/java/android/net/VpnTransportInfoTest.java
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/packages/Connectivity/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java
similarity index 100%
rename from tests/net/java/android/net/ipmemorystore/ParcelableTests.java
rename to packages/Connectivity/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java
diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdManagerTest.java
similarity index 100%
rename from tests/net/java/android/net/nsd/NsdManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/net/nsd/NsdManagerTest.java
diff --git a/tests/net/java/android/net/nsd/NsdServiceInfoTest.java b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
similarity index 100%
rename from tests/net/java/android/net/nsd/NsdServiceInfoTest.java
rename to packages/Connectivity/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
diff --git a/tests/net/java/android/net/util/DnsUtilsTest.java b/packages/Connectivity/tests/unit/java/android/net/util/DnsUtilsTest.java
similarity index 100%
rename from tests/net/java/android/net/util/DnsUtilsTest.java
rename to packages/Connectivity/tests/unit/java/android/net/util/DnsUtilsTest.java
diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/packages/Connectivity/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
similarity index 100%
rename from tests/net/java/android/net/util/KeepaliveUtilsTest.kt
rename to packages/Connectivity/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/packages/Connectivity/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
similarity index 100%
rename from tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
rename to packages/Connectivity/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
diff --git a/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java
similarity index 100%
rename from tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java
rename to packages/Connectivity/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java
diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java
similarity index 100%
rename from tests/net/java/com/android/internal/net/VpnProfileTest.java
rename to packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java
diff --git a/tests/net/java/com/android/internal/util/BitUtilsTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/util/BitUtilsTest.java
similarity index 100%
rename from tests/net/java/com/android/internal/util/BitUtilsTest.java
rename to packages/Connectivity/tests/unit/java/com/android/internal/util/BitUtilsTest.java
diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/util/RingBufferTest.java
similarity index 100%
rename from tests/net/java/com/android/internal/util/RingBufferTest.java
rename to packages/Connectivity/tests/unit/java/com/android/internal/util/RingBufferTest.java
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java
similarity index 97%
rename from tests/net/java/com/android/server/ConnectivityServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index f277e94..29a411e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.LOCAL_MAC_ADDRESS;
import static android.Manifest.permission.NETWORK_FACTORY;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
@@ -127,6 +128,7 @@
import static com.android.testutils.MiscAsserts.assertEmpty;
import static com.android.testutils.MiscAsserts.assertLength;
import static com.android.testutils.MiscAsserts.assertRunsInAtMost;
+import static com.android.testutils.MiscAsserts.assertSameElements;
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertEquals;
@@ -212,6 +214,8 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MatchAllNetworkSpecifier;
+import android.net.NativeNetworkConfig;
+import android.net.NativeNetworkType;
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
@@ -1240,6 +1244,8 @@
verify(mMockNetd, never())
.networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()), any());
mAgentRegistered = true;
+ verify(mMockNetd).networkCreate(nativeNetworkConfigVpn(getNetwork().netId,
+ !mMockNetworkAgent.isBypassableVpn(), mVpnType));
updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent");
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
@@ -2777,8 +2783,9 @@
private void grantUsingBackgroundNetworksPermissionForUid(
final int uid, final String packageName) throws Exception {
- when(mPackageManager.getPackageInfo(eq(packageName), eq(GET_PERMISSIONS)))
- .thenReturn(buildPackageInfo(true, uid));
+ when(mPackageManager.getPackageInfo(
+ eq(packageName), eq(GET_PERMISSIONS | MATCH_ANY_USER)))
+ .thenReturn(buildPackageInfo(true /* hasSystemPermission */, uid));
mService.mPermissionMonitor.onPackageAdded(packageName, uid);
}
@@ -2828,6 +2835,16 @@
mCm.unregisterNetworkCallback(callback);
}
+ private NativeNetworkConfig nativeNetworkConfigPhysical(int netId, int permission) {
+ return new NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL, permission,
+ /*secure=*/ false, VpnManager.TYPE_VPN_NONE);
+ }
+
+ private NativeNetworkConfig nativeNetworkConfigVpn(int netId, boolean secure, int vpnType) {
+ return new NativeNetworkConfig(netId, NativeNetworkType.VIRTUAL, INetd.PERMISSION_NONE,
+ secure, vpnType);
+ }
+
@Test
public void testNetworkAgentCallbacks() throws Exception {
// Keeps track of the order of events that happen in this test.
@@ -2849,8 +2866,8 @@
wifiNetwork.set(mWiFiNetworkAgent.getNetwork());
assertNotNull(wifiNetwork.get());
try {
- verify(mMockNetd).networkCreatePhysical(wifiNetwork.get().getNetId(),
- INetd.PERMISSION_NONE);
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ wifiNetwork.get().getNetId(), INetd.PERMISSION_NONE));
} catch (RemoteException impossible) {
fail();
}
@@ -4260,6 +4277,124 @@
mCm.unregisterNetworkCallback(callback);
}
+ @Test
+ public void testNetworkCallbackWithNullUids() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ // Attempt to file a callback for networks applying to another UID. This does not actually
+ // work, because this code does not currently have permission to do so. The callback behaves
+ // exactly the same as the one registered just above.
+ final int otherUid = UserHandle.getUid(RESTRICTED_USER, VPN_UID);
+ final NetworkRequest otherUidRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .setUids(UidRange.toIntRanges(uidRangesForUids(otherUid)))
+ .build();
+ final TestNetworkCallback otherUidCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(otherUidRequest, otherUidCallback);
+
+ final NetworkRequest includeOtherUidsRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .setIncludeOtherUidNetworks(true)
+ .build();
+ final TestNetworkCallback includeOtherUidsCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(includeOtherUidsRequest, includeOtherUidsCallback);
+
+ // Both callbacks see a network with no specifier that applies to their UID.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false /* validated */);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ otherUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ includeOtherUidsCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ otherUidCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ includeOtherUidsCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // Only the includeOtherUidsCallback sees a VPN that does not apply to its UID.
+ final UidRange range = UidRange.createForUser(UserHandle.of(RESTRICTED_USER));
+ final Set<UidRange> vpnRanges = Collections.singleton(range);
+ mMockVpn.establish(new LinkProperties(), VPN_UID, vpnRanges);
+ includeOtherUidsCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ callback.assertNoCallback();
+ otherUidCallback.assertNoCallback();
+
+ mMockVpn.disconnect();
+ includeOtherUidsCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ callback.assertNoCallback();
+ otherUidCallback.assertNoCallback();
+ }
+
+ private static class RedactableNetworkSpecifier extends NetworkSpecifier {
+ public static final int ID_INVALID = -1;
+
+ public final int networkId;
+
+ RedactableNetworkSpecifier(int networkId) {
+ this.networkId = networkId;
+ }
+
+ @Override
+ public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+ return other instanceof RedactableNetworkSpecifier
+ && this.networkId == ((RedactableNetworkSpecifier) other).networkId;
+ }
+
+ @Override
+ public NetworkSpecifier redact() {
+ return new RedactableNetworkSpecifier(ID_INVALID);
+ }
+ }
+
+ @Test
+ public void testNetworkCallbackWithNullUidsRedactsSpecifier() throws Exception {
+ final RedactableNetworkSpecifier specifier = new RedactableNetworkSpecifier(42);
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier)
+ .build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ // Attempt to file a callback for networks applying to another UID. This does not actually
+ // work, because this code does not currently have permission to do so. The callback behaves
+ // exactly the same as the one registered just above.
+ final int otherUid = UserHandle.getUid(RESTRICTED_USER, VPN_UID);
+ final NetworkRequest otherUidRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier)
+ .setUids(UidRange.toIntRanges(uidRangesForUids(otherUid)))
+ .build();
+ final TestNetworkCallback otherUidCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(otherUidRequest, otherUidCallback);
+
+ final NetworkRequest includeOtherUidsRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier)
+ .setIncludeOtherUidNetworks(true)
+ .build();
+ final TestNetworkCallback includeOtherUidsCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(includeOtherUidsRequest, callback);
+
+ // Only the regular callback sees the network, because callbacks filed with no UID have
+ // their specifiers redacted.
+ final LinkProperties emptyLp = new LinkProperties();
+ final NetworkCapabilities ncTemplate = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, emptyLp, ncTemplate);
+ mWiFiNetworkAgent.connect(false /* validated */);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ otherUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ includeOtherUidsCallback.assertNoCallback();
+ }
+
private void setCaptivePortalMode(int mode) {
ContentResolver cr = mServiceContext.getContentResolver();
Settings.Global.putInt(cr, ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE, mode);
@@ -5869,20 +6004,8 @@
mCm.unregisterNetworkCallback(networkCallback);
}
- private <T> void assertSameElementsNoDuplicates(T[] expected, T[] actual) {
- // Easier to implement than a proper "assertSameElements" method that also correctly deals
- // with duplicates.
- final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual);
- assertEquals(msg, expected.length, actual.length);
- Set expectedSet = new ArraySet<>(Arrays.asList(expected));
- assertEquals("expected contains duplicates", expectedSet.size(), expected.length);
- // actual cannot have duplicates because it's the same length and has the same elements.
- Set actualSet = new ArraySet<>(Arrays.asList(actual));
- assertEquals(expectedSet, actualSet);
- }
-
- private void expectNetworkStatus(Network[] networks, String defaultIface,
- Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
+ private void expectNotifyNetworkStatus(List<Network> networks, String defaultIface,
+ Integer vpnUid, String vpnIfname, List<String> underlyingIfaces) throws Exception {
ArgumentCaptor<List<Network>> networksCaptor = ArgumentCaptor.forClass(List.class);
ArgumentCaptor<List<UnderlyingNetworkInfo>> vpnInfosCaptor =
ArgumentCaptor.forClass(List.class);
@@ -5890,26 +6013,24 @@
verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(),
any(List.class), eq(defaultIface), vpnInfosCaptor.capture());
- assertSameElementsNoDuplicates(networksCaptor.getValue().toArray(), networks);
+ assertSameElements(networksCaptor.getValue(), networks);
- UnderlyingNetworkInfo[] infos =
- vpnInfosCaptor.getValue().toArray(new UnderlyingNetworkInfo[0]);
+ List<UnderlyingNetworkInfo> infos = vpnInfosCaptor.getValue();
if (vpnUid != null) {
- assertEquals("Should have exactly one VPN:", 1, infos.length);
- UnderlyingNetworkInfo info = infos[0];
+ assertEquals("Should have exactly one VPN:", 1, infos.size());
+ UnderlyingNetworkInfo info = infos.get(0);
assertEquals("Unexpected VPN owner:", (int) vpnUid, info.getOwnerUid());
assertEquals("Unexpected VPN interface:", vpnIfname, info.getInterface());
- assertSameElementsNoDuplicates(underlyingIfaces,
- info.getUnderlyingInterfaces().toArray(new String[0]));
+ assertSameElements(underlyingIfaces, info.getUnderlyingInterfaces());
} else {
- assertEquals(0, infos.length);
+ assertEquals(0, infos.size());
return;
}
}
- private void expectNetworkStatus(
- Network[] networks, String defaultIface) throws Exception {
- expectNetworkStatus(networks, defaultIface, null, null, new String[0]);
+ private void expectNotifyNetworkStatus(
+ List<Network> networks, String defaultIface) throws Exception {
+ expectNotifyNetworkStatus(networks, defaultIface, null, null, List.of());
}
@Test
@@ -5917,8 +6038,8 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
- final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
+ final List<Network> onlyCell = List.of(mCellNetworkAgent.getNetwork());
+ final List<Network> onlyWifi = List.of(mWiFiNetworkAgent.getNetwork());
LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -5929,7 +6050,7 @@
mCellNetworkAgent.connect(false);
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+ expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
reset(mStatsManager);
// Default network switch should update ifaces.
@@ -5937,37 +6058,37 @@
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
- expectNetworkStatus(onlyWifi, WIFI_IFNAME);
+ expectNotifyNetworkStatus(onlyWifi, WIFI_IFNAME);
reset(mStatsManager);
// Disconnect should update ifaces.
mWiFiNetworkAgent.disconnect();
waitForIdle();
- expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+ expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
reset(mStatsManager);
// Metered change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+ expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
reset(mStatsManager);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+ expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
reset(mStatsManager);
// Temp metered change shouldn't update ifaces
mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
waitForIdle();
- verify(mStatsManager, never()).notifyNetworkStatus(eq(Arrays.asList(onlyCell)),
+ verify(mStatsManager, never()).notifyNetworkStatus(eq(onlyCell),
any(List.class), eq(MOBILE_IFNAME), any(List.class));
reset(mStatsManager);
// Roaming change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
waitForIdle();
- expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+ expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
reset(mStatsManager);
// Test VPNs.
@@ -5977,29 +6098,29 @@
mMockVpn.establishForMyUid(lp);
assertUidRangesUpdatedForMyUid(true);
- final Network[] cellAndVpn = new Network[] {
- mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
+ final List<Network> cellAndVpn =
+ List.of(mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork());
// A VPN with default (null) underlying networks sets the underlying network's interfaces...
- expectNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
- new String[]{MOBILE_IFNAME});
+ expectNotifyNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ List.of(MOBILE_IFNAME));
// ...and updates them as the default network switches.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
final Network[] onlyNull = new Network[]{null};
- final Network[] wifiAndVpn = new Network[] {
- mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
- final Network[] cellAndWifi = new Network[] {
- mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
- final Network[] cellNullAndWifi = new Network[] {
- mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()};
+ final List<Network> wifiAndVpn =
+ List.of(mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork());
+ final List<Network> cellAndWifi =
+ List.of(mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork());
+ final Network[] cellNullAndWifi =
+ new Network[]{mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()};
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
- expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
- new String[]{WIFI_IFNAME});
+ expectNotifyNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+ List.of(WIFI_IFNAME));
reset(mStatsManager);
// A VPN that sets its underlying networks passes the underlying interfaces, and influences
@@ -6008,23 +6129,23 @@
// MOBILE_IFNAME even though the default network is wifi.
// TODO: fix this to pass in the actual default network interface. Whether or not the VPN
// applies to the system server UID should not have any bearing on network stats.
- mMockVpn.setUnderlyingNetworks(onlyCell);
+ mMockVpn.setUnderlyingNetworks(onlyCell.toArray(new Network[0]));
waitForIdle();
- expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
- new String[]{MOBILE_IFNAME});
+ expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ List.of(MOBILE_IFNAME));
reset(mStatsManager);
- mMockVpn.setUnderlyingNetworks(cellAndWifi);
+ mMockVpn.setUnderlyingNetworks(cellAndWifi.toArray(new Network[0]));
waitForIdle();
- expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
- new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+ expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ List.of(MOBILE_IFNAME, WIFI_IFNAME));
reset(mStatsManager);
// Null underlying networks are ignored.
mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
waitForIdle();
- expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
- new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+ expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ List.of(MOBILE_IFNAME, WIFI_IFNAME));
reset(mStatsManager);
// If an underlying network disconnects, that interface should no longer be underlying.
@@ -6037,8 +6158,8 @@
mCellNetworkAgent.disconnect();
waitForIdle();
assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork()));
- expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
- new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+ expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ List.of(MOBILE_IFNAME, WIFI_IFNAME));
// Confirm that we never tell NetworkStatsService that cell is no longer the underlying
// network for the VPN...
@@ -6073,26 +6194,26 @@
// Also, for the same reason as above, the active interface passed in is null.
mMockVpn.setUnderlyingNetworks(new Network[0]);
waitForIdle();
- expectNetworkStatus(wifiAndVpn, null);
+ expectNotifyNetworkStatus(wifiAndVpn, null);
reset(mStatsManager);
// Specifying only a null underlying network is the same as no networks.
mMockVpn.setUnderlyingNetworks(onlyNull);
waitForIdle();
- expectNetworkStatus(wifiAndVpn, null);
+ expectNotifyNetworkStatus(wifiAndVpn, null);
reset(mStatsManager);
// Specifying networks that are all disconnected is the same as specifying no networks.
- mMockVpn.setUnderlyingNetworks(onlyCell);
+ mMockVpn.setUnderlyingNetworks(onlyCell.toArray(new Network[0]));
waitForIdle();
- expectNetworkStatus(wifiAndVpn, null);
+ expectNotifyNetworkStatus(wifiAndVpn, null);
reset(mStatsManager);
// Passing in null again means follow the default network again.
mMockVpn.setUnderlyingNetworks(null);
waitForIdle();
- expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
- new String[]{WIFI_IFNAME});
+ expectNotifyNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+ List.of(WIFI_IFNAME));
reset(mStatsManager);
}
@@ -8404,7 +8525,8 @@
final int cellNetId = mCellNetworkAgent.getNetwork().netId;
waitForIdle();
- verify(mMockNetd, times(1)).networkCreatePhysical(eq(cellNetId), anyInt());
+ verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(cellNetId,
+ INetd.PERMISSION_NONE));
assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
@@ -9469,9 +9591,9 @@
@Override
public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) {
return new TestTransportInfo(
- (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0,
- (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0,
- (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0
+ locationRedacted | (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0,
+ localMacAddressRedacted | (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0,
+ settingsRedacted | (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0
);
}
@@ -9494,8 +9616,26 @@
public int hashCode() {
return Objects.hash(locationRedacted, localMacAddressRedacted, settingsRedacted);
}
+
+ @Override
+ public String toString() {
+ return String.format(
+ "TestTransportInfo{locationRedacted=%s macRedacted=%s settingsRedacted=%s}",
+ locationRedacted, localMacAddressRedacted, settingsRedacted);
+ }
}
+ private TestTransportInfo getTestTransportInfo(NetworkCapabilities nc) {
+ return (TestTransportInfo) nc.getTransportInfo();
+ }
+
+ private TestTransportInfo getTestTransportInfo(TestNetworkAgentWrapper n) {
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(n.getNetwork());
+ assertNotNull(nc);
+ return getTestTransportInfo(nc);
+ }
+
+
private void verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps(
@NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid,
@NonNull TransportInfo actualTransportInfo, int expectedOwnerUid,
@@ -9524,7 +9664,6 @@
wifiNetworkCallback.expectCapabilitiesThat(mWiFiNetworkAgent,
nc -> Objects.equals(expectedOwnerUid, nc.getOwnerUid())
&& Objects.equals(expectedTransportInfo, nc.getTransportInfo()));
-
}
@Test
@@ -9545,6 +9684,40 @@
wifiNetworkCallack, ownerUid, transportInfo, INVALID_UID, sanitizedTransportInfo);
}
+ @Test
+ public void testTransportInfoRedactionInSynchronousCalls() throws Exception {
+ final NetworkCapabilities ncTemplate = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .setTransportInfo(new TestTransportInfo());
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(),
+ ncTemplate);
+ mWiFiNetworkAgent.connect(true /* validated; waits for callback */);
+
+ // NETWORK_SETTINGS redaction is controlled by the NETWORK_SETTINGS permission
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted);
+ withPermission(NETWORK_SETTINGS, () -> {
+ assertFalse(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted);
+ });
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted);
+
+ // LOCAL_MAC_ADDRESS redaction is controlled by the LOCAL_MAC_ADDRESS permission
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted);
+ withPermission(LOCAL_MAC_ADDRESS, () -> {
+ assertFalse(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted);
+ });
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted);
+
+ // Synchronous getNetworkCapabilities calls never return unredacted location-sensitive
+ // information.
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted);
+ setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted);
+ denyAllLocationPrivilegedPermissions();
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted);
+ }
+
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE);
@@ -9903,12 +10076,27 @@
// Connect the cell agent verify that it notifies TestNetworkCallback that it is available
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(callback);
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+
+ final NetworkCapabilities ncTemplate = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setTransportInfo(new TestTransportInfo());
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(),
+ ncTemplate);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
callback.assertNoCallback();
}
+ private boolean areConnDiagCapsRedacted(NetworkCapabilities nc) {
+ TestTransportInfo ti = (TestTransportInfo) nc.getTransportInfo();
+ return nc.getUids() == null
+ && nc.getAdministratorUids().length == 0
+ && nc.getOwnerUid() == Process.INVALID_UID
+ && getTestTransportInfo(nc).locationRedacted
+ && getTestTransportInfo(nc).localMacAddressRedacted
+ && getTestTransportInfo(nc).settingsRedacted;
+ }
+
@Test
public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable()
throws Exception {
@@ -9919,12 +10107,7 @@
// Verify onConnectivityReport fired
verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable(
- argThat(report -> {
- final NetworkCapabilities nc = report.getNetworkCapabilities();
- return nc.getUids() == null
- && nc.getAdministratorUids().length == 0
- && nc.getOwnerUid() == Process.INVALID_UID;
- }));
+ argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities())));
}
@Test
@@ -9940,12 +10123,7 @@
// Verify onDataStallSuspected fired
verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(
- argThat(report -> {
- final NetworkCapabilities nc = report.getNetworkCapabilities();
- return nc.getUids() == null
- && nc.getAdministratorUids().length == 0
- && nc.getOwnerUid() == Process.INVALID_UID;
- }));
+ argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities())));
}
@Test
@@ -12255,8 +12433,9 @@
mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
- INetd.PERMISSION_NONE);
+ inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
+
final TestOnCompleteListener listener = new TestOnCompleteListener();
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
@@ -12283,8 +12462,8 @@
mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
mSystemDefaultNetworkCallback.assertNoCallback();
mDefaultNetworkCallback.assertNoCallback();
- inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
- INetd.PERMISSION_SYSTEM);
+ inOrder.verify(mMockNetd).networkCreate(
+ nativeNetworkConfigPhysical(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM));
inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
uidRangeFor(testHandle));
inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
@@ -12327,8 +12506,8 @@
mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mProfileDefaultNetworkCallback.assertNoCallback();
- inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
- INetd.PERMISSION_NONE);
+ inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
// When the agent disconnects, test that the app on the work profile falls back to the
// default network.
@@ -12358,8 +12537,8 @@
mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2);
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
- inOrder.verify(mMockNetd).networkCreatePhysical(workAgent2.getNetwork().netId,
- INetd.PERMISSION_SYSTEM);
+ inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent2.getNetwork().netId, INetd.PERMISSION_SYSTEM));
inOrder.verify(mMockNetd).networkAddUidRanges(workAgent2.getNetwork().netId,
uidRangeFor(testHandle));
@@ -12404,8 +12583,8 @@
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
r -> r.run(), listener);
listener.expectOnComplete();
- inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
- INetd.PERMISSION_NONE);
+ inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
uidRangeFor(testHandle));
@@ -12457,10 +12636,10 @@
mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
app4Cb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
- INetd.PERMISSION_NONE);
- inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
- INetd.PERMISSION_SYSTEM);
+ inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
+ inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM));
final TestOnCompleteListener listener = new TestOnCompleteListener();
mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
@@ -12512,8 +12691,8 @@
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
r -> r.run(), listener);
listener.expectOnComplete();
- inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
- INetd.PERMISSION_NONE);
+ inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
uidRangeFor(testHandle));
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
similarity index 100%
rename from tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/IpSecServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceTest.java
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt
similarity index 100%
rename from tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
rename to packages/Connectivity/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt
diff --git a/tests/net/java/com/android/server/NetIdManagerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/NetIdManagerTest.kt
similarity index 100%
rename from tests/net/java/com/android/server/NetIdManagerTest.kt
rename to packages/Connectivity/tests/unit/java/com/android/server/NetIdManagerTest.kt
diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/NetworkManagementServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
diff --git a/tests/net/java/com/android/server/NsdServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/NsdServiceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/NsdServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/NsdServiceTest.java
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/DnsManagerTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
diff --git a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/FullScoreTest.kt
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
diff --git a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MetricsTestUtil.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/MetricsTestUtil.java
diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
diff --git a/tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt
diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
new file mode 100644
index 0000000..4408958
--- /dev/null
+++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity
+
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.NetworkScore.KEEP_CONNECTED_NONE
+import android.net.NetworkScore.POLICY_EXITING
+import android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY
+import android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.server.connectivity.FullScore.POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD
+import com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+
+private fun score(vararg policies: Int) = FullScore(0,
+ policies.fold(0L) { acc, e -> acc or (1L shl e) }, KEEP_CONNECTED_NONE)
+private fun caps(transport: Int) = NetworkCapabilities.Builder().addTransportType(transport).build()
+
+@SmallTest
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+class NetworkRankerTest {
+ private val mRanker = NetworkRanker()
+
+ private class TestScore(private val sc: FullScore, private val nc: NetworkCapabilities)
+ : NetworkRanker.Scoreable {
+ override fun getScore() = sc
+ override fun getCapsNoCopy(): NetworkCapabilities = nc
+ }
+
+ @Test
+ fun testYieldToBadWiFiOneCell() {
+ // Only cell, it wins
+ val winner = TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ val scores = listOf(winner)
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiOneCellOneBadWiFi() {
+ // Bad wifi wins against yielding validated cell
+ val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD),
+ caps(TRANSPORT_WIFI))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiOneCellTwoBadWiFi() {
+ // Bad wifi wins against yielding validated cell. Prefer the one that's primary.
+ val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD),
+ caps(TRANSPORT_WIFI)),
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiOneCellTwoBadWiFiOneNotAvoided() {
+ // Bad wifi ever validated wins against bad wifi that never was validated (or was
+ // avoided when bad).
+ val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD),
+ caps(TRANSPORT_WIFI))
+ val scores = listOf(
+ winner,
+ TestScore(score(), caps(TRANSPORT_WIFI)),
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiOneCellOneBadWiFiOneGoodWiFi() {
+ // Good wifi wins
+ val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_IS_VALIDATED), caps(TRANSPORT_WIFI))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI)),
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiTwoCellsOneBadWiFi() {
+ // Cell that doesn't yield wins over cell that yields and bad wifi
+ val winner = TestScore(score(POLICY_IS_VALIDATED), caps(TRANSPORT_CELLULAR))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI)),
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiTwoCellsOneBadWiFiOneGoodWiFi() {
+ // Good wifi wins over cell that doesn't yield and cell that yields
+ val winner = TestScore(score(POLICY_IS_VALIDATED), caps(TRANSPORT_WIFI))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI)),
+ TestScore(score(POLICY_IS_VALIDATED), caps(TRANSPORT_CELLULAR)),
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiOneExitingGoodWiFi() {
+ // Yielding cell wins over good exiting wifi
+ val winner = TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_IS_VALIDATED, POLICY_EXITING), caps(TRANSPORT_WIFI))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiOneExitingBadWiFi() {
+ // Yielding cell wins over bad exiting wifi
+ val winner = TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_EXITING), caps(TRANSPORT_WIFI))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
similarity index 97%
rename from tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index d7535a9..02a5808 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -479,13 +479,14 @@
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
- buildPackageInfo(/* SYSTEM */ true, SYSTEM_UID1, MOCK_USER1),
- buildPackageInfo(/* SYSTEM */ false, MOCK_UID1, MOCK_USER1),
- buildPackageInfo(/* SYSTEM */ false, MOCK_UID2, MOCK_USER1),
- buildPackageInfo(/* SYSTEM */ false, VPN_UID, MOCK_USER1)
+ buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
}));
- when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
- buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
+ when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
+ eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring();
// Every app on user 0 except MOCK_UID2 are under VPN.
final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
@@ -530,11 +531,12 @@
public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
- buildPackageInfo(true, SYSTEM_UID1, MOCK_USER1),
- buildPackageInfo(false, VPN_UID, MOCK_USER1)
+ buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
}));
- when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
- buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
+ when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
+ eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring();
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/VpnTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/VpnTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/VpnTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsBaseTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsBaseTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
diff --git a/tests/net/jni/Android.bp b/packages/Connectivity/tests/unit/jni/Android.bp
similarity index 100%
rename from tests/net/jni/Android.bp
rename to packages/Connectivity/tests/unit/jni/Android.bp
diff --git a/tests/net/jni/test_onload.cpp b/packages/Connectivity/tests/unit/jni/test_onload.cpp
similarity index 100%
rename from tests/net/jni/test_onload.cpp
rename to packages/Connectivity/tests/unit/jni/test_onload.cpp
diff --git a/tests/net/res/raw/history_v1 b/packages/Connectivity/tests/unit/res/raw/history_v1
similarity index 100%
rename from tests/net/res/raw/history_v1
rename to packages/Connectivity/tests/unit/res/raw/history_v1
Binary files differ
diff --git a/tests/net/res/raw/net_dev_typical b/packages/Connectivity/tests/unit/res/raw/net_dev_typical
similarity index 100%
rename from tests/net/res/raw/net_dev_typical
rename to packages/Connectivity/tests/unit/res/raw/net_dev_typical
diff --git a/tests/net/res/raw/netstats_uid_v4 b/packages/Connectivity/tests/unit/res/raw/netstats_uid_v4
similarity index 100%
rename from tests/net/res/raw/netstats_uid_v4
rename to packages/Connectivity/tests/unit/res/raw/netstats_uid_v4
Binary files differ
diff --git a/tests/net/res/raw/netstats_v1 b/packages/Connectivity/tests/unit/res/raw/netstats_v1
similarity index 100%
rename from tests/net/res/raw/netstats_v1
rename to packages/Connectivity/tests/unit/res/raw/netstats_v1
Binary files differ
diff --git a/tests/net/res/raw/xt_qtaguid_iface_fmt_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_iface_fmt_typical
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical
diff --git a/tests/net/res/raw/xt_qtaguid_iface_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_typical
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_iface_typical
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_typical
diff --git a/tests/net/res/raw/xt_qtaguid_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_typical
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_typical
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_typical
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_one_underlying
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_with_clat b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_with_clat
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_with_clat
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_with_clat
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_with_clat
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_simple
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_with_clat_simple
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_simple
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index 784a747..ca69a28 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index 5b7bda4..d7cfb96 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index 784a747..ca69a28 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index 780cb8a..84c3401 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 16a946d..f8cb5d3d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -561,7 +561,20 @@
break;
}
- Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
+ StringBuilder msg = new StringBuilder();
+ msg.append("status: " + statusString + ", cause: " + causeString);
+ if (status == STATUS_IN_PROGRESS) {
+ msg.append(
+ String.format(
+ ", partition name: %s, progress: %d/%d",
+ mCurrentPartitionName,
+ mCurrentPartitionInstalledSize,
+ mCurrentPartitionSize));
+ }
+ if (detail != null) {
+ msg.append(", detail: " + detail);
+ }
+ Log.d(TAG, msg.toString());
if (notifyOnNotificationBar) {
mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 59ea9f0..f18d426 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -320,20 +320,21 @@
}
}
- private void installScratch() throws IOException {
- final long scratchSize = mDynSystem.suggestScratchSize();
+ private void installWritablePartition(final String partitionName, final long partitionSize)
+ throws IOException {
+ Log.d(TAG, "Creating writable partition: " + partitionName + ", size: " + partitionSize);
+
Thread thread = new Thread() {
@Override
public void run() {
mInstallationSession =
- mDynSystem.createPartition("scratch", scratchSize, /* readOnly= */ false);
+ mDynSystem.createPartition(
+ partitionName, partitionSize, /* readOnly= */ false);
}
};
- Log.d(TAG, "Creating partition: scratch, size = " + scratchSize);
thread.start();
-
- Progress progress = new Progress("scratch", scratchSize, mNumInstalledPartitions++);
+ Progress progress = new Progress(partitionName, partitionSize, mNumInstalledPartitions++);
while (thread.isAlive()) {
if (isCancelled()) {
@@ -356,53 +357,22 @@
if (mInstallationSession == null) {
throw new IOException(
- "Failed to start installation with requested size: " + scratchSize);
+ "Failed to start installation with requested size: " + partitionSize);
}
+
// Reset installation session and verify that installation completes successfully.
mInstallationSession = null;
if (!mDynSystem.closePartition()) {
- throw new IOException("Failed to complete partition installation: scratch");
+ throw new IOException("Failed to complete partition installation: " + partitionName);
}
}
+ private void installScratch() throws IOException {
+ installWritablePartition("scratch", mDynSystem.suggestScratchSize());
+ }
+
private void installUserdata() throws IOException {
- Thread thread = new Thread(() -> {
- mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
- });
-
- Log.d(TAG, "Creating partition: userdata, size = " + mUserdataSize);
- thread.start();
-
- Progress progress = new Progress("userdata", mUserdataSize, mNumInstalledPartitions++);
-
- while (thread.isAlive()) {
- if (isCancelled()) {
- return;
- }
-
- final long installedSize = mDynSystem.getInstallationProgress().bytes_processed;
-
- if (installedSize > progress.installedSize + MIN_PROGRESS_TO_PUBLISH) {
- progress.installedSize = installedSize;
- publishProgress(progress);
- }
-
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- // Ignore the error.
- }
- }
-
- if (mInstallationSession == null) {
- throw new IOException(
- "Failed to start installation with requested size: " + mUserdataSize);
- }
- // Reset installation session and verify that installation completes successfully.
- mInstallationSession = null;
- if (!mDynSystem.closePartition()) {
- throw new IOException("Failed to complete partition installation: userdata");
- }
+ installWritablePartition("userdata", mUserdataSize);
}
private void installImages() throws IOException, ImageValidationException {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
index 44d5a0c..1614188 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
@@ -171,15 +171,15 @@
}
key LEFT_BRACKET {
- label: '['
+ label: ']'
base, capslock: '\u062c'
- shift: '<'
+ shift: '>'
}
key RIGHT_BRACKET {
- label: ']'
+ label: '['
base, capslock: '\u062f'
- shift: '>'
+ shift: '<'
}
key BACKSLASH {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm b/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm
index cd3a4b9..283cb4e 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm
@@ -89,14 +89,14 @@
key 9 {
label: '9'
base: '9'
- shift: '('
+ shift: ')'
shift+capslock: '\u05c2'
}
key 0 {
label: '0'
base: '0'
- shift: ')'
+ shift: '('
shift+capslock: '\u05c1'
}
@@ -180,17 +180,17 @@
}
key LEFT_BRACKET {
- label: '['
- base, capslock: '['
- shift: '{'
-}
-
-key RIGHT_BRACKET {
label: ']'
base, capslock: ']'
shift: '}'
}
+key RIGHT_BRACKET {
+ label: '['
+ base, capslock: '['
+ shift: '{'
+}
+
### ROW 3
key A {
@@ -322,14 +322,14 @@
key COMMA {
label: ','
base: '\u05ea'
- shift: '<'
+ shift: '>'
capslock: ','
}
key PERIOD {
label: '.'
base: '\u05e5'
- shift: '>'
+ shift: '<'
capslock: '.'
}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
index e7dd6c6..bfe7821 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
@@ -292,14 +292,14 @@
key COMMA {
label, number: '\u0648'
base: '\u0648'
- shift: '<'
+ shift: '>'
ctrl, alt, meta: none
}
key PERIOD {
label, number: '.'
base: '.'
- shift: '>'
+ shift: '<'
ctrl, alt, meta: none
}
@@ -440,14 +440,14 @@
}
key NUMPAD_LEFT_PAREN {
- label, number: '('
- base: '('
+ label, number: ')'
+ base: ')'
ctrl, alt, meta: none
}
key NUMPAD_RIGHT_PAREN {
- label, number: ')'
- base: ')'
+ label, number: '('
+ base: '('
ctrl, alt, meta: none
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 0210079..9c6113c 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -86,7 +86,7 @@
private int mOriginatingUid = PackageInstaller.SessionParams.UID_UNKNOWN;
private String mOriginatingPackage; // The package name corresponding to #mOriginatingUid
- private boolean localLOGV = false;
+ private final boolean mLocalLOGV = false;
PackageManager mPm;
IPackageManager mIpm;
AppOpsManager mAppOpsManager;
@@ -104,7 +104,7 @@
private List<UnknownSourcesListener> mActiveUnknownSourcesListeners = new ArrayList<>(1);
// ApplicationInfo object primarily used for already existing applications
- private ApplicationInfo mAppInfo = null;
+ private ApplicationInfo mAppInfo;
// Buttons to indicate user acceptance
private Button mOk;
@@ -154,6 +154,7 @@
* @param id The dialog type to add
*/
private void showDialogInner(int id) {
+ if (mLocalLOGV) Log.i(TAG, "showDialogInner(" + id + ")");
DialogFragment currentDialog =
(DialogFragment) getFragmentManager().findFragmentByTag("dialog");
if (currentDialog != null) {
@@ -174,6 +175,7 @@
* @return The dialog
*/
private DialogFragment createDialog(int id) {
+ if (mLocalLOGV) Log.i(TAG, "createDialog(" + id + ")");
switch (id) {
case DLG_PACKAGE_ERROR:
return SimpleErrorDialog.newInstance(R.string.Parse_error_dlg_text);
@@ -294,6 +296,7 @@
@Override
protected void onCreate(Bundle icicle) {
+ if (mLocalLOGV) Log.i(TAG, "creating for user " + getUserId());
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(null);
@@ -354,6 +357,8 @@
}
boolean wasSetUp = processPackageUri(packageUri);
+ if (mLocalLOGV) Log.i(TAG, "wasSetUp: " + wasSetUp);
+
if (!wasSetUp) {
return;
}
@@ -363,6 +368,8 @@
protected void onResume() {
super.onResume();
+ if (mLocalLOGV) Log.i(TAG, "onResume(): mAppSnippet=" + mAppSnippet);
+
if (mAppSnippet != null) {
// load dummy layout with OK button disabled until we override this layout in
// startInstallConfirm
@@ -443,15 +450,21 @@
final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
+ if (mLocalLOGV) Log.i(TAG, "install not allowed: " + UserManager.DISALLOW_INSTALL_APPS);
showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
return;
} else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
+ if (mLocalLOGV) {
+ Log.i(TAG, "install not allowed by admin; showing "
+ + Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
+ }
startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
finish();
return;
}
if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
+ if (mLocalLOGV) Log.i(TAG, "install allowed");
initiateInstall();
} else {
// Check for unknown sources restrictions.
@@ -462,6 +475,7 @@
final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
& (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
if (systemRestriction != 0) {
+ if (mLocalLOGV) Log.i(TAG, "Showing DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER");
showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
} else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
@@ -475,13 +489,19 @@
}
private void startAdminSupportDetailsActivity(String restriction) {
+ if (mLocalLOGV) Log.i(TAG, "startAdminSupportDetailsActivity(): " + restriction);
+
// If the given restriction is set by an admin, display information about the
// admin enforcing the restriction for the affected user.
final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
final Intent showAdminSupportDetailsIntent = dpm.createAdminSupportIntent(restriction);
if (showAdminSupportDetailsIntent != null) {
+ if (mLocalLOGV) Log.i(TAG, "starting " + showAdminSupportDetailsIntent);
startActivity(showAdminSupportDetailsIntent);
+ } else {
+ if (mLocalLOGV) Log.w(TAG, "not intent for " + restriction);
}
+
finish();
}
@@ -497,6 +517,7 @@
final int appOpMode = mAppOpsManager.noteOpNoThrow(appOpCode, mOriginatingUid,
mOriginatingPackage, mCallingAttributionTag,
"Started package installation activity");
+ if (mLocalLOGV) Log.i(TAG, "handleUnknownSources(): appMode=" + appOpMode);
switch (appOpMode) {
case AppOpsManager.MODE_DEFAULT:
mAppOpsManager.setMode(appOpCode, mOriginatingUid,
@@ -527,6 +548,7 @@
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
+ if (mLocalLOGV) Log.i(TAG, "processPackageUri(): uri=" + packageUri + ", scheme=" + scheme);
switch (scheme) {
case SCHEME_PACKAGE: {
@@ -543,7 +565,9 @@
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return false;
}
- mAppSnippet = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
+ CharSequence label = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
+ if (mLocalLOGV) Log.i(TAG, "creating snippet for " + label);
+ mAppSnippet = new PackageUtil.AppSnippet(label,
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} break;
@@ -559,6 +583,7 @@
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return false;
}
+ if (mLocalLOGV) Log.i(TAG, "creating snippet for local file " + sourceFile);
mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
} break;
@@ -604,7 +629,7 @@
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
}
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
+ if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
startActivity(newIntent);
finish();
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index d3a9f8f..f5570df 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -107,13 +107,18 @@
icon);
}
- static public class AppSnippet {
+ static final class AppSnippet {
@NonNull public CharSequence label;
@Nullable public Drawable icon;
public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon) {
this.label = label;
this.icon = icon;
}
+
+ @Override
+ public String toString() {
+ return "AppSnippet[" + label + (icon != null ? "(has" : "(no ") + " icon)]";
+ }
}
/**
diff --git a/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml b/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml
index a19f7af..22b25a3 100644
--- a/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml
@@ -23,4 +23,15 @@
column="15"/>
</issue>
+ <issue
+ id="NewApi"
+ message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
+ errorLine1=" android:radius="?android:attr/dialogCornerRadius""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml"
+ line="23"
+ column="9"/>
+ </issue>
+
</issues>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
new file mode 100644
index 0000000..ae3834d
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners
+ android:radius="?android:attr/dialogCornerRadius"
+ />
+</shape>
\ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
new file mode 100644
index 0000000..ba612d7
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:paddingHorizontal="8dp"
+ android:orientation="horizontal"
+ android:background="@drawable/settingslib_rounded_background">
+
+ <Button
+ android:id="@+id/button1"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <View
+ android:id="@+id/divider1"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button2"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <View
+ android:id="@+id/divider2"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button3"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <View
+ android:id="@+id/divider3"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button4"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout/settingslib_action_buttons.xml
similarity index 88%
rename from packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml
rename to packages/SettingsLib/ActionButtonsPreference/res/layout/settingslib_action_buttons.xml
index 4f47113..55df34f 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout/settingslib_action_buttons.xml
@@ -24,29 +24,29 @@
<Button
android:id="@+id/button1"
- style="@style/SettingsActionButton"
+ style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:id="@+id/button2"
- style="@style/SettingsActionButton"
+ style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:id="@+id/button3"
- style="@style/SettingsActionButton"
+ style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:id="@+id/button4"
- style="@style/SettingsActionButton"
+ style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
index efa508d..42c7d76 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
@@ -16,11 +16,10 @@
-->
<resources>
- <style name="SettingsActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
+ <style name="SettingsLibActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
<item name="android:drawablePadding">4dp</item>
<item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item>
- <item name="android:layout_marginEnd">8dp</item>
<item name="android:paddingTop">20dp</item>
<item name="android:paddingBottom">20dp</item>
</style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
index 8b46cc6..aeda7ac 100644
--- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -27,16 +27,20 @@
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
+import androidx.core.os.BuildCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* This preference provides a four buttons layout with Settings style.
* It looks like below
*
- * --------------------------------------------------
- * button1 | button2 | button3 | button4 |
- * --------------------------------------------------
+ * ---------------------------------------
+ * - button1 | button2 | button3 | button4 -
+ * ---------------------------------------
*
* User can set title / icon / click listener for each button.
*
@@ -49,10 +53,16 @@
public class ActionButtonsPreference extends Preference {
private static final String TAG = "ActionButtonPreference";
+
private final ButtonInfo mButton1Info = new ButtonInfo();
private final ButtonInfo mButton2Info = new ButtonInfo();
private final ButtonInfo mButton3Info = new ButtonInfo();
private final ButtonInfo mButton4Info = new ButtonInfo();
+ private final List<ButtonInfo> mVisibleButtonInfos = new ArrayList<>(4);
+
+ private View mDivider1;
+ private View mDivider2;
+ private View mDivider3;
public ActionButtonsPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
@@ -76,25 +86,49 @@
}
private void init() {
- setLayoutResource(R.layout.settings_action_buttons);
+ setLayoutResource(R.layout.settingslib_action_buttons);
setSelectable(false);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- holder.setDividerAllowedAbove(true);
- holder.setDividerAllowedBelow(true);
+ if (!BuildCompat.isAtLeastS()) {
+ holder.setDividerAllowedAbove(true);
+ holder.setDividerAllowedBelow(true);
+ }
mButton1Info.mButton = (Button) holder.findViewById(R.id.button1);
mButton2Info.mButton = (Button) holder.findViewById(R.id.button2);
mButton3Info.mButton = (Button) holder.findViewById(R.id.button3);
mButton4Info.mButton = (Button) holder.findViewById(R.id.button4);
+ mDivider1 = holder.findViewById(R.id.divider1);
+ mDivider2 = holder.findViewById(R.id.divider2);
+ mDivider3 = holder.findViewById(R.id.divider3);
+
mButton1Info.setUpButton();
mButton2Info.setUpButton();
mButton3Info.setUpButton();
mButton4Info.setUpButton();
+
+ // Add visible button into list only
+ if (mButton1Info.isVisible()) {
+ mVisibleButtonInfos.add(mButton1Info);
+ }
+ if (mButton2Info.isVisible()) {
+ mVisibleButtonInfos.add(mButton2Info);
+ }
+ if (mButton3Info.isVisible()) {
+ mVisibleButtonInfos.add(mButton3Info);
+ }
+ if (mButton4Info.isVisible()) {
+ mVisibleButtonInfos.add(mButton4Info);
+ }
+
+ setupDivider1();
+ setupDivider2();
+ setupDivider3();
}
/**
@@ -357,6 +391,28 @@
return this;
}
+ private void setupDivider1() {
+ // Display divider1 only if button1 and button2 is visible
+ if (mDivider1 != null && mButton1Info.isVisible() && mButton2Info.isVisible()) {
+ mDivider1.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void setupDivider2() {
+ // Display divider2 only if button3 is visible and button2 or button3 is visible
+ if (mDivider2 != null && mButton3Info.isVisible()
+ && (mButton1Info.isVisible() || mButton2Info.isVisible())) {
+ mDivider2.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void setupDivider3() {
+ // Display divider3 only if button4 is visible and 2 visible buttons at least
+ if (mDivider3 != null && mVisibleButtonInfos.size() > 1 && mButton4Info.isVisible()) {
+ mDivider3.setVisibility(View.VISIBLE);
+ }
+ }
+
static class ButtonInfo {
private Button mButton;
private CharSequence mText;
@@ -379,6 +435,10 @@
}
}
+ boolean isVisible() {
+ return mButton.getVisibility() == View.VISIBLE;
+ }
+
/**
* By default, four buttons are visible.
* However, there are two cases which button should be invisible.
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 69cee00..a65bf41 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -54,6 +54,7 @@
"SettingsLibAppPreference",
"SettingsLibSearchWidget",
"SettingsLibSettingsSpinner",
+ "SettingsLibIllustrationPreference",
"SettingsLibLayoutPreference",
"SettingsLibMainSwitchPreference",
"SettingsLibActionButtonsPreference",
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
index 0390e86..4907c6f 100644
--- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -54,7 +54,7 @@
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
- android:singleLine="true"
+ android:maxLines="2"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<TextView
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 83a838f..e6ca2e0 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -35,7 +35,6 @@
public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity {
private CollapsingToolbarLayout mCollapsingToolbarLayout;
- private Toolbar mToolbar;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -45,8 +44,8 @@
super.setContentView(R.layout.collapsing_toolbar_base_layout);
mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
- mToolbar = findViewById(R.id.action_bar);
- setActionBar(mToolbar);
+ final Toolbar toolbar = findViewById(R.id.action_bar);
+ setActionBar(toolbar);
// Enable title and home button by default
final ActionBar actionBar = getActionBar();
@@ -97,16 +96,11 @@
@Override
public boolean onNavigateUp() {
if (!super.onNavigateUp()) {
- finish();
+ finishAfterTransition();
}
return true;
}
- @Override
- public Toolbar getToolbar() {
- return mToolbar;
- }
-
/**
* Returns an instance of collapsing toolbar.
*/
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
index b4fe7ed..4c45c5e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
@@ -18,14 +18,11 @@
import android.app.ActivityOptions;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
import android.util.Log;
-import android.view.MenuItem;
import android.view.Window;
import android.widget.Toolbar;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.os.BuildCompat;
import androidx.fragment.app.FragmentActivity;
@@ -40,117 +37,85 @@
private static final String TAG = "SettingsTransitionActivity";
private static final int DEFAULT_REQUEST = -1;
+ private Toolbar mToolbar;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
- if (BuildCompat.isAtLeastS()) {
+ if (isSettingsTransitionEnabled()) {
getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
SettingsTransitionHelper.applyForwardTransition(this);
SettingsTransitionHelper.applyBackwardTransition(this);
}
+
super.onCreate(savedInstanceState);
}
@Override
- public void startActivity(Intent intent) {
- if (!BuildCompat.isAtLeastS()) {
- super.startActivity(intent);
- return;
- }
- final Toolbar toolbar = getToolbar();
- if (toolbar == null) {
- Log.w(TAG, "Toolbar is null. Cannot apply settings transition!");
- super.startActivity(intent);
- return;
- }
- super.startActivity(intent, getActivityOptionsBundle(toolbar));
+ public void setActionBar(@Nullable Toolbar toolbar) {
+ super.setActionBar(toolbar);
+ mToolbar = toolbar;
+ }
+
+ @Override
+ public void startActivity(Intent intent) {
+ if (!isSettingsTransitionEnabled()) {
+ super.startActivity(intent);
+ return;
+ }
+
+ super.startActivity(intent, createActivityOptionsBundleForTransition(null));
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
- if (!BuildCompat.isAtLeastS()) {
+ if (!isSettingsTransitionEnabled()) {
super.startActivity(intent, options);
return;
}
- final Toolbar toolbar = getToolbar();
- if (toolbar == null) {
- Log.w(TAG, "Toolbar is null. Cannot apply settings transition!");
- super.startActivity(intent, options);
- return;
- }
- if (options != null) {
- super.startActivity(intent, getMergedBundleForTransition(options));
- return;
- }
- super.startActivity(intent, getActivityOptionsBundle(toolbar));
+
+ super.startActivity(intent, createActivityOptionsBundleForTransition(options));
}
@Override
public void startActivityForResult(Intent intent, int requestCode) {
- if (!BuildCompat.isAtLeastS() || requestCode == DEFAULT_REQUEST) {
+ if (!isSettingsTransitionEnabled() || requestCode == DEFAULT_REQUEST) {
super.startActivityForResult(intent, requestCode);
return;
}
- final Toolbar toolbar = getToolbar();
- if (toolbar == null) {
- Log.w(TAG, "Toolbar is null. Cannot apply settings transition!");
- super.startActivityForResult(intent, requestCode);
- return;
- }
- super.startActivityForResult(intent, requestCode, getActivityOptionsBundle(toolbar));
+ super.startActivityForResult(intent, requestCode, createActivityOptionsBundleForTransition(
+ null));
}
@Override
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
- if (!BuildCompat.isAtLeastS() || requestCode == DEFAULT_REQUEST) {
+ if (!isSettingsTransitionEnabled() || requestCode == DEFAULT_REQUEST) {
super.startActivityForResult(intent, requestCode, options);
return;
}
- final Toolbar toolbar = getToolbar();
- if (toolbar == null) {
- Log.w(TAG, "Toolbar is null. Cannot apply settings transition!");
- super.startActivityForResult(intent, requestCode, options);
- return;
- }
- if (options != null) {
- super.startActivityForResult(intent, requestCode,
- getMergedBundleForTransition(options));
- return;
- }
- super.startActivityForResult(intent, requestCode, getActivityOptionsBundle(toolbar));
+ super.startActivityForResult(intent, requestCode, createActivityOptionsBundleForTransition(
+ options));
}
- @Override
- public boolean onOptionsItemSelected(@NonNull MenuItem item) {
- final int id = item.getItemId();
- if (id == android.R.id.home) {
- // Make the up button behave the same as the back button.
- onBackPressed();
- return true;
- }
- return super.onOptionsItemSelected(item);
+ protected boolean isSettingsTransitionEnabled() {
+ return BuildCompat.isAtLeastS();
}
- /**
- * Subclasses should implement this method and return their {@link Toolbar}.
- */
- public abstract Toolbar getToolbar();
-
- private Bundle getActivityOptionsBundle(Toolbar toolbar) {
- return ActivityOptions.makeSceneTransitionAnimation(this, toolbar,
- "shared_element_view").toBundle();
- }
-
- private Bundle getMergedBundleForTransition(@NonNull Bundle options) {
- final Toolbar toolbar = getToolbar();
- final Bundle mergedBundle = new Bundle();
- mergedBundle.putAll(options);
- final Bundle activityOptionsBundle = getActivityOptionsBundle(toolbar);
- if (activityOptionsBundle != null) {
- mergedBundle.putAll(activityOptionsBundle);
+ @Nullable
+ private Bundle createActivityOptionsBundleForTransition(@Nullable Bundle options) {
+ if (mToolbar == null) {
+ Log.w(TAG, "setActionBar(Toolbar) is not called. Cannot apply settings transition!");
+ return options;
}
- return mergedBundle;
+ final Bundle transitionOptions = ActivityOptions.makeSceneTransitionAnimation(this,
+ mToolbar, "shared_element_view").toBundle();
+ if (options == null) {
+ return transitionOptions;
+ }
+ final Bundle mergedOptions = new Bundle(options);
+ mergedOptions.putAll(transitionOptions);
+ return mergedOptions;
}
}
diff --git a/packages/SettingsLib/FooterPreference/Android.bp b/packages/SettingsLib/FooterPreference/Android.bp
index 11f39e7..0929706 100644
--- a/packages/SettingsLib/FooterPreference/Android.bp
+++ b/packages/SettingsLib/FooterPreference/Android.bp
@@ -20,4 +20,8 @@
],
sdk_version: "system_current",
min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
}
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 5ba1082e..feb3b01 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -35,6 +35,7 @@
public static final String KEY_FOOTER = "footer_preference";
static final int ORDER_FOOTER = Integer.MAX_VALUE - 1;
+ private CharSequence mContentDescription;
public FooterPreference(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.footerPreferenceStyle);
@@ -52,6 +53,7 @@
title.setMovementMethod(new LinkMovementMethod());
title.setClickable(false);
title.setLongClickable(false);
+ title.setContentDescription(mContentDescription);
}
@Override
@@ -69,6 +71,26 @@
return getTitle();
}
+ /**
+ * To set content description of the {@link FooterPreference}. This can use for talkback
+ * environment if developer wants to have a customization content.
+ *
+ * @param contentDescription The resource id of the content description.
+ */
+ public void setContentDescription(CharSequence contentDescription) {
+ if (!TextUtils.equals(mContentDescription, contentDescription)) {
+ mContentDescription = contentDescription;
+ notifyChanged();
+ }
+ }
+
+ /**
+ * Return the content description of footer preference.
+ */
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
private void init() {
setLayoutResource(R.layout.preference_footer);
if (getIcon() == null) {
@@ -87,6 +109,7 @@
private Context mContext;
private String mKey;
private CharSequence mTitle;
+ private CharSequence mContentDescription;
public Builder(@NonNull Context context) {
mContext = context;
@@ -94,6 +117,7 @@
/**
* To set the key value of the {@link FooterPreference}.
+ *
* @param key The key value.
*/
public Builder setKey(@NonNull String key) {
@@ -103,6 +127,7 @@
/**
* To set the title of the {@link FooterPreference}.
+ *
* @param title The title.
*/
public Builder setTitle(CharSequence title) {
@@ -112,6 +137,7 @@
/**
* To set the title of the {@link FooterPreference}.
+ *
* @param titleResId The resource id of the title.
*/
public Builder setTitle(@StringRes int titleResId) {
@@ -120,6 +146,28 @@
}
/**
+ * To set content description of the {@link FooterPreference}. This can use for talkback
+ * environment if developer wants to have a customization content.
+ *
+ * @param contentDescription The resource id of the content description.
+ */
+ public Builder setContentDescription(CharSequence contentDescription) {
+ mContentDescription = contentDescription;
+ return this;
+ }
+
+ /**
+ * To set content description of the {@link FooterPreference}. This can use for talkback
+ * environment if developer wants to have a customization content.
+ *
+ * @param contentDescriptionResId The resource id of the content description.
+ */
+ public Builder setContentDescription(@StringRes int contentDescriptionResId) {
+ mContentDescription = mContext.getText(contentDescriptionResId);
+ return this;
+ }
+
+ /**
* To generate the {@link FooterPreference}.
*/
public FooterPreference build() {
@@ -132,6 +180,10 @@
if (!TextUtils.isEmpty(mKey)) {
footerPreference.setKey(mKey);
}
+
+ if (!TextUtils.isEmpty(mContentDescription)) {
+ footerPreference.setContentDescription(mContentDescription);
+ }
return footerPreference;
}
}
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
new file mode 100644
index 0000000..f8dd384
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -0,0 +1,23 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SettingsLibIllustrationPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ "lottie",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml
new file mode 100644
index 0000000..120b085
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml b/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml
new file mode 100644
index 0000000..55b3115
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:fillColor="#FFFFFF" android:pathData="M24,24m-19,0a19,19 0,1 1,38 0a19,19 0,1 1,-38 0"/>
+ <path android:fillColor="#1A73E8" android:pathData="M20,33l12,-9l-12,-9z"/>
+ <path android:fillColor="#1A73E8" android:pathData="M24,4C12.96,4 4,12.96 4,24s8.96,20 20,20s20,-8.96 20,-20S35.04,4 24,4zM24,40c-8.82,0 -16,-7.18 -16,-16S15.18,8 24,8s16,7.18 16,16S32.82,40 24,40z"/>
+</vector>
diff --git a/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml
new file mode 100644
index 0000000..dd2fa5e
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="@dimen/settingslib_illustration_padding"
+ android:left="@dimen/settingslib_illustration_padding"
+ android:right="@dimen/settingslib_illustration_padding"
+ android:bottom="@dimen/settingslib_illustration_padding">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/settingslib_protection_color"/>
+ <corners android:radius="28dp"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
new file mode 100644
index 0000000..7d65aae
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:background="?android:attr/colorBackground"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/lottie_view"
+ android:layout_width="412dp"
+ android:layout_height="300dp"
+ android:layout_gravity="center"
+ android:clipToOutline="true"
+ android:background="@drawable/protection_background"
+ android:importantForAccessibility="no"/>
+
+ <ImageView
+ android:id="@+id/video_play_button"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_gravity="center"
+ android:visibility="gone"
+ android:src="@drawable/ic_gesture_play_button"/>
+
+</FrameLayout>
+
diff --git a/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml
new file mode 100644
index 0000000..71b18a8
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <color name="settingslib_protection_color">@android:color/black</color>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
new file mode 100644
index 0000000..e53a43e
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <color name="settingslib_protection_color">@android:color/white</color>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
new file mode 100644
index 0000000..79562b9
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <!-- Padding of illustration -->
+ <dimen name="settingslib_illustration_padding">16dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
new file mode 100644
index 0000000..90b8a32
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceViewHolder;
+
+import com.airbnb.lottie.LottieAnimationView;
+
+/**
+ * IllustrationPreference is a preference that can play lottie format animation
+ */
+public class IllustrationPreference extends Preference implements OnPreferenceClickListener {
+
+ static final String TAG = "IllustrationPreference";
+ private int mAnimationId;
+ private boolean mIsAnimating;
+ private ImageView mPlayButton;
+ private LottieAnimationView mIllustrationView;
+
+ public IllustrationPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs);
+ }
+
+ public IllustrationPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context, attrs);
+ }
+
+ public IllustrationPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context, attrs);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ if (mAnimationId == 0) {
+ Log.w(TAG, "Invalid illustration resource id.");
+ return;
+ }
+ mPlayButton = (ImageView) holder.findViewById(R.id.video_play_button);
+ mIllustrationView = (LottieAnimationView) holder.findViewById(R.id.lottie_view);
+ mIllustrationView.setAnimation(mAnimationId);
+ mIllustrationView.loop(true);
+ mIllustrationView.playAnimation();
+ updateAnimationStatus(mIsAnimating);
+ setOnPreferenceClickListener(this);
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ mIsAnimating = !isAnimating();
+ updateAnimationStatus(mIsAnimating);
+ return true;
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.mIsAnimating = mIsAnimating;
+ return ss;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ mIsAnimating = ss.mIsAnimating;
+ }
+
+ @VisibleForTesting
+ boolean isAnimating() {
+ return mIllustrationView.isAnimating();
+ }
+
+ private void init(Context context, AttributeSet attrs) {
+ setLayoutResource(R.layout.illustration_preference);
+
+ mIsAnimating = true;
+ if (attrs != null) {
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.LottieAnimationView, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
+ mAnimationId = a.getResourceId(R.styleable.LottieAnimationView_lottie_rawRes, 0);
+ a.recycle();
+ }
+ }
+
+ private void updateAnimationStatus(boolean playAnimation) {
+ if (playAnimation) {
+ mIllustrationView.resumeAnimation();
+ mPlayButton.setVisibility(View.INVISIBLE);
+ } else {
+ mIllustrationView.pauseAnimation();
+ mPlayButton.setVisibility(View.VISIBLE);
+ }
+ }
+
+ static class SavedState extends BaseSavedState {
+ boolean mIsAnimating;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ /**
+ * Constructor called from {@link #CREATOR}
+ */
+ private SavedState(Parcel in) {
+ super(in);
+ mIsAnimating = (Boolean) in.readValue(null);
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeValue(mIsAnimating);
+ }
+
+ @Override
+ public String toString() {
+ return "IllustrationPreference.SavedState{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " mIsAnimating=" + mIsAnimating + "}";
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_disabled.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_disabled.xml
index 900400e..b41762f 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_disabled.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_disabled.xml
@@ -26,7 +26,7 @@
android:height="@dimen/settingslib_switch_thumb_size"
android:width="@dimen/settingslib_switch_thumb_size"/>
<solid
- android:color="@color/settingslib_state_off_color"
+ android:color="@color/settingslib_thumb_off_color"
android:alpha="?android:attr/disabledAlpha"/>
</shape>
</item>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_off.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_off.xml
index e54c332..8b69ad1 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_off.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_off.xml
@@ -25,7 +25,7 @@
<size
android:height="@dimen/settingslib_switch_thumb_size"
android:width="@dimen/settingslib_switch_thumb_size"/>
- <solid android:color="@color/settingslib_state_off_color"/>
+ <solid android:color="@color/settingslib_thumb_off_color"/>
</shape>
</item>
</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index 27c30ca6..010b9ba 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -19,10 +19,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
- android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:background="?android:attr/colorBackground"
android:orientation="vertical">
@@ -31,15 +27,16 @@
android:minHeight="@dimen/settingslib_min_switch_bar_height"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:paddingLeft="@dimen/settingslib_switchbar_margin_start"
- android:paddingRight="@dimen/settingslib_switchbar_margin_end">
+ android:layout_margin="@dimen/settingslib_switchbar_margin"
+ android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+ android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
<TextView
android:id="@+id/switch_text"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
- android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
android:layout_gravity="center_vertical"
android:maxLines="2"
android:ellipsize="end"
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml
index c8d06d4..9ca3683 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml
@@ -15,10 +15,16 @@
limitations under the License.
-->
-<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+<resources>
<color name="settingslib_switchbar_switch_track_tint">#82000000</color>
<color name="settingslib_switchbar_switch_thumb_tint">@android:color/black</color>
+ <!-- Material next thumb off color-->
+ <color name="settingslib_thumb_off_color">@android:color/system_neutral2_300</color>
+
<!-- Material next track on color-->
- <color name="settingslib_track_on_color">?androidprv:attr/colorSurfaceHighlight</color>
+ <color name="settingslib_track_on_color">@android:color/system_accent2_700</color>
+
+ <!-- Material next track off color-->
+ <color name="settingslib_track_off_color">@android:color/system_neutral1_700</color>
</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
index 3fcc1dd..2c73238 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
@@ -26,9 +26,12 @@
<!-- Material next state off color-->
<color name="settingslib_state_off_color">?androidprv:attr/colorAccentSecondary</color>
+ <!-- Material next thumb off color-->
+ <color name="settingslib_thumb_off_color">@android:color/system_neutral2_100</color>
+
<!-- Material next track on color-->
<color name="settingslib_track_on_color">?androidprv:attr/colorAccentPrimaryVariant</color>
<!-- Material next track off color-->
- <color name="settingslib_track_off_color">?androidprv:attr/colorAccentSecondaryVariant</color>
+ <color name="settingslib_track_off_color">@android:color/system_neutral2_600</color>
</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
index 4c528da..a1cbcf72 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
@@ -17,11 +17,14 @@
<resources>
+ <!-- Size of layout margin -->
+ <dimen name="settingslib_switchbar_margin">16dp</dimen>
+
<!-- Size of layout margin left -->
- <dimen name="settingslib_switchbar_margin_start">24dp</dimen>
+ <dimen name="settingslib_switchbar_padding_left">24dp</dimen>
<!-- Size of layout margin right -->
- <dimen name="settingslib_switchbar_margin_end">16dp</dimen>
+ <dimen name="settingslib_switchbar_padding_right">16dp</dimen>
<!-- Minimum width of switch -->
<dimen name="settingslib_min_switch_width">52dp</dimen>
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 0748acd..123c477 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -72,9 +72,7 @@
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- LayoutInflater.from(context).inflate(
- resourceId(context, "layout", "settingslib_main_switch_bar"),
- this);
+ LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this);
if (!BuildCompat.isAtLeastS()) {
final TypedArray a = context.obtainStyledAttributes(
@@ -90,12 +88,10 @@
mFrameView = findViewById(R.id.frame);
mTextView = (TextView) findViewById(R.id.switch_text);
mSwitch = (Switch) findViewById(android.R.id.switch_widget);
- mBackgroundOn = getContext().getDrawable(
- resourceId(context, "drawable", "settingslib_switch_bar_bg_on"));
- mBackgroundOff = getContext().getDrawable(
- resourceId(context, "drawable", "settingslib_switch_bar_bg_off"));
+ mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+ mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off);
mBackgroundDisabled = getContext().getDrawable(
- resourceId(context, "drawable", "settingslib_switch_bar_bg_disabled"));
+ R.drawable.settingslib_switch_bar_bg_disabled);
addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked));
@@ -302,8 +298,4 @@
requestLayout();
}
-
- private int resourceId(Context context, String type, String name) {
- return context.getResources().getIdentifier(name, type, context.getPackageName());
- }
}
diff --git a/packages/SettingsLib/RadioButtonPreference/Android.bp b/packages/SettingsLib/RadioButtonPreference/Android.bp
index b309c01..28ff71f 100644
--- a/packages/SettingsLib/RadioButtonPreference/Android.bp
+++ b/packages/SettingsLib/RadioButtonPreference/Android.bp
@@ -20,4 +20,8 @@
sdk_version: "system_current",
min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
}
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
index 403e417..e92b6716 100644
--- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
@@ -29,9 +29,9 @@
android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:paddingHorizontal="20dp"
android:gravity="center"
android:minWidth="56dp"
- android:layout_marginEnd="16dp"
android:orientation="vertical"/>
<LinearLayout
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
index 154a0f4..304c343 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -36,6 +36,7 @@
private SettingsSpinnerAdapter mAdapter;
private AdapterView.OnItemSelectedListener mListener;
private int mPosition; //Default 0 for internal shard storage.
+ private boolean mIsClickable = true;
/**
* Perform inflation from XML and apply a class-specific base style.
@@ -50,6 +51,7 @@
public SettingsSpinnerPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setLayoutResource(R.layout.settings_spinner_preference);
+ setSelectable(false);
}
/**
@@ -62,6 +64,7 @@
public SettingsSpinnerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.settings_spinner_preference);
+ setSelectable(false);
}
/**
@@ -98,10 +101,21 @@
notifyChanged();
}
+ /** Set clickable of the spinner. */
+ public void setClickable(boolean isClickable) {
+ if (mIsClickable == isClickable) {
+ return;
+ }
+ mIsClickable = isClickable;
+ notifyChanged();
+ }
+
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.spinner);
+ spinner.setEnabled(mIsClickable);
+ spinner.setClickable(mIsClickable);
spinner.setAdapter(mAdapter);
spinner.setSelection(mPosition);
spinner.setOnItemSelectedListener(mOnSelectedListener);
diff --git a/packages/SettingsLib/TwoTargetPreference/Android.bp b/packages/SettingsLib/TwoTargetPreference/Android.bp
index 078e8c3..b32d5b4 100644
--- a/packages/SettingsLib/TwoTargetPreference/Android.bp
+++ b/packages/SettingsLib/TwoTargetPreference/Android.bp
@@ -19,4 +19,8 @@
],
sdk_version: "system_current",
min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
}
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml
index f6a64c6..21fcedc 100644
--- a/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml
@@ -23,16 +23,16 @@
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:gravity="center_vertical"
- android:background="?android:attr/selectableItemBackground"
+ android:background="@android:color/transparent"
android:clipToPadding="false">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground"
android:gravity="start|center_vertical"
android:clipToPadding="false"
- android:duplicateParentState="true"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
@@ -44,14 +44,12 @@
android:minWidth="56dp"
android:orientation="horizontal"
android:clipToPadding="false"
- android:duplicateParentState="true"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<androidx.preference.internal.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:duplicateParentState="true"
settings:maxWidth="48dp"
settings:maxHeight="48dp" />
</LinearLayout>
@@ -60,7 +58,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:duplicateParentState="true"
android:paddingTop="16dp"
android:paddingBottom="16dp">
@@ -69,7 +66,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- android:duplicateParentState="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -79,7 +75,6 @@
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
- android:duplicateParentState="true"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10" />
@@ -95,7 +90,6 @@
android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:duplicateParentState="true"
android:minWidth="64dp"
android:gravity="center"
android:orientation="vertical" />
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml
index 44732e5..bd477f8 100644
--- a/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml
@@ -22,12 +22,10 @@
android:layout_height="match_parent"
android:gravity="start|center_vertical"
android:orientation="horizontal"
- android:duplicateParentState="true"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<View
android:layout_width="1dp"
android:layout_height="match_parent"
- android:duplicateParentState="true"
android:background="?android:attr/listDivider" />
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
index 17f257d..9130662 100644
--- a/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
+++ b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
@@ -72,9 +72,9 @@
private void init(Context context) {
setLayoutResource(R.layout.preference_two_target);
mSmallIconSize = context.getResources().getDimensionPixelSize(
- resourceId(context, "dimen", "two_target_pref_small_icon_size"));
+ R.dimen.two_target_pref_small_icon_size);
mMediumIconSize = context.getResources().getDimensionPixelSize(
- resourceId(context, "dimen", "two_target_pref_medium_icon_size"));
+ R.dimen.two_target_pref_medium_icon_size);
final int secondTargetResId = getSecondTargetResId();
if (secondTargetResId != 0) {
setWidgetLayoutResource(secondTargetResId);
@@ -116,8 +116,4 @@
protected int getSecondTargetResId() {
return 0;
}
-
- private int resourceId(Context context, String type, String name) {
- return context.getResources().getIdentifier(name, type, context.getPackageName());
- }
}
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml
new file mode 100644
index 0000000..efae569
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.66,7C18.19,5.07 15.14,4 12,4C8.58,4 5.27,5.27 2.7,7.53L12,18.85l6,-7.3v3.15L12,22L0,7.39C2.97,4.08 7.25,2 12,2c4.56,0 8.69,1.92 11.64,5H20.66z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml
new file mode 100644
index 0000000..d50e734
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M23.62,7C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4L12,22l6,-7.3v-3.19l-2.13,2.59C14.74,13.4 13.39,13 12,13s-2.74,0.4 -3.87,1.1L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3H23.62z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml
new file mode 100644
index 0000000..1be297e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M23.62,7C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4L12,22l6,-7.3v-3.19l-0.27,0.33C16.12,10.67 14.09,10 12,10c-2.09,0 -4.12,0.67 -5.73,1.84L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3H23.62z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml
new file mode 100644
index 0000000..738bd5a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M0,7.4L12,22l6,-7.3V8.57C16.21,7.56 14.14,7 12,7C9.2,7 6.53,7.96 4.45,9.62L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3h2.95C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml
new file mode 100644
index 0000000..14d020b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M0,7.4C3,4.1 7.2,2 12,2c4.6,0 8.65,1.93 11.62,5H18v7.7L12,22L0,7.4z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 8639d21..8e7bbaa 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laai tans stadig"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laai tans draadloos"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Laai nie"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ingeprop; kan nie op die oomblik laai nie"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Gekoppel, laai nie"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Gelaai"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Beheer deur administrateur"</string>
<string name="disabled" msgid="8017887509554714950">"Gedeaktiveer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index a1cf08d..69e2b4d 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ኃይል በዝግታ በመሙላት ላይ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"በገመድ-አልባ ኃይል በመሙላት ላይ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ባትሪ እየሞላ አይደለም"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"ተሰክቷል፣ አሁን ኃይል መሙላት አይቻልም"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ተገናኝቷል፣ ኃይል በመሙላት ላይ አይደለም"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ባትሪ ሞልቷል"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"በአስተዳዳሪ ቁጥጥር የተደረገበት"</string>
<string name="disabled" msgid="8017887509554714950">"ቦዝኗል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index e3c452d..4e30f38 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"جارٍ الشحن ببطء"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"جارٍ الشحن لاسلكيًا"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"لا يتم الشحن"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"تم التوصيل، ولكن يتعذّر الشحن الآن"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"الجهاز متصل بالشاحن، ولا يتم الشحن."</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"تم الشحن"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"إعدادات يتحكم فيها المشرف"</string>
<string name="disabled" msgid="8017887509554714950">"غير مفعّل"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index e8d294d..cc00047 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"লাহে লাহে চাৰ্জ হৈছে"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"বেতাঁৰৰ মাধ্যমেৰে চাৰ্জ হৈ আছে"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"চ্চাৰ্জ কৰা নাই"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"প্লাগ কৰি থোৱা হৈছে, এই মুহূৰ্তত চ্চাৰ্জ কৰিব নোৱাৰি"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"সংযোগ হৈ আছে, চাৰ্জ হৈ থকা নাই"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"চাৰ্জ হ’ল"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"এডমিনৰ দ্বাৰা নিয়ন্ত্ৰিত"</string>
<string name="disabled" msgid="8017887509554714950">"নিষ্ক্ৰিয়"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 5d2c997..41d4b6e 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Asta doldurulur"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Simsiz şarj edilir"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Doldurulmur"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Cihaz hazırda batareya yığa bilmir"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Qoşulub, şarj edilmir"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Şarj edilib"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Admin tərəfindən nəzarət olunur"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktiv"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index aa722b1..b056e30 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo se puni"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Priključeno je, ali punjenje trenutno nije moguće"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontroliše administrator"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 65dcb2f..59d63c5 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Павольная зарадка"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бесправадная зарадка"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не зараджаецца"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Падключана да сеткі сілкавання, зарадзіць зараз немагчыма"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Падключана, не зараджаецца"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Зараджаны"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Кантралюецца адміністратарам"</string>
<string name="disabled" msgid="8017887509554714950">"Адключанае"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 31b0325..20b70cc 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Зарежда се бавно"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Зарежда се безжично"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не се зарежда"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Включена в захранването, в момента не се зарежда"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Свързано, не се зарежда"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Заредена"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролира се от администратор"</string>
<string name="disabled" msgid="8017887509554714950">"Деактивирано"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 8d5c99d..7db27d2 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ধীরে চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"কেবল ছাড়া চার্জ হচ্ছে"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"চার্জ হচ্ছে না"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"প্লাগ-ইন করা হয়েছে কিন্তু এখনই চার্জ করা যাবে না"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"কানেক্ট করা থাকলেও চার্জ করা হচ্ছে না"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"চার্জ হয়েছে"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"প্রশাসকের দ্বারা নিয়ন্ত্রিত"</string>
<string name="disabled" msgid="8017887509554714950">"অক্ষম হয়েছে"</string>
@@ -506,9 +506,9 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"অ্যালার্ম এবং রিমাইন্ডার"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"অ্যালার্ম এবং রিমাইন্ডার সেট করার অনুমতি দিন"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"অ্যালার্ম এবং রিমাইন্ডার"</string>
- <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"অন্যান্য অ্যাকশন শিডিউল করতে এবং অ্যালার্ম সেট করার জন্য এই অ্যাপকে অনুমতি দিন। আপনি ফোন ব্যবহার না করার সময় এই অ্যাপ ব্যবহার করা হতে পারে, কারণ এটির জন্য বেশি ব্যাটারি চার্জ লাগতে পারে। অনুমতি দেওয়া না থাকলে, এই অ্যাপ সঠিকভাবে কাজ নাও করতে পারে এবং অ্যালার্মও শিডিউল অনুযায়ী বাজবে না।"</string>
- <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"অন্যান্য অ্যাকশন শিডিউল করতে এবং অ্যালার্ম সেট করার জন্য এই অ্যাপকে অনুমতি দিন। আপনি ট্যাবলেট ব্যবহার না করার সময় এই অ্যাপ ব্যবহার করা হতে পারে, কারণ এটির জন্য বেশি ব্যাটারি চার্জ লাগতে পারে। অনুমতি দেওয়া না থাকলে, এই অ্যাপ সঠিকভাবে কাজ নাও করতে পারে এবং অ্যালার্মও শিডিউল অনুযায়ী বাজবে না।"</string>
- <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"অন্যান্য অ্যাকশন শিডিউল করতে এবং অ্যালার্ম সেট করার জন্য এই অ্যাপকে অনুমতি দিন। আপনি ডিভাইস ব্যবহার না করার সময় এই অ্যাপ ব্যবহার করা হতে পারে, কারণ এটির জন্য বেশি ব্যাটারি চার্জ খরচ হতে পারে। অনুমতি দেওয়া না থাকলে, এই অ্যাপ সঠিকভাবে কাজ নাও করতে পারে এবং অ্যালার্মও শিডিউল অনুযায়ী বাজবে না।"</string>
+ <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"অন্যান্য অ্যাকশন শিডিউল করতে এবং অ্যালার্ম সেট করার জন্য এই অ্যাপকে অনুমতি দিন। আপনি ফোন ব্যবহার না করার সময় এই অ্যাপ ব্যবহার করা হতে পারে, যার ফলে আরও ব্যাটারির চার্জ খরচ হতে পারে। অনুমতি দেওয়া না থাকলে, এই অ্যাপ সঠিকভাবে কাজ নাও করতে পারে এবং অ্যালার্মও শিডিউল অনুযায়ী বাজবে না।"</string>
+ <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"অন্যান্য অ্যাকশন শিডিউল করতে এবং অ্যালার্ম সেট করার জন্য এই অ্যাপকে অনুমতি দিন। আপনি ট্যাবলেট ব্যবহার না করার সময় এই অ্যাপ ব্যবহার করা হতে পারে, যার ফলে আরও ব্যাটারির চার্জ খরচ হতে পারে। অনুমতি দেওয়া না থাকলে, এই অ্যাপ সঠিকভাবে কাজ নাও করতে পারে এবং অ্যালার্মও শিডিউল অনুযায়ী বাজবে না।"</string>
+ <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"অন্যান্য অ্যাকশন শিডিউল করতে এবং অ্যালার্ম সেট করার জন্য এই অ্যাপকে অনুমতি দিন। আপনি ডিভাইস ব্যবহার না করার সময় এই অ্যাপ ব্যবহার করা হতে পারে, যার ফলে আরও ব্যাটারির চার্জ খরচ হতে পারে। অনুমতি দেওয়া না থাকলে, এই অ্যাপ সঠিকভাবে কাজ নাও করতে পারে এবং অ্যালার্মও শিডিউল অনুযায়ী বাজবে না।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"শিডিউল, অ্যালার্ম, রিমাইন্ডার, ঘড়ি"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"চালু করুন"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'বিরক্ত করবে না\' মোড চালু করুন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 9242a70..424d215 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo punjenje"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Priključeno, trenutno se ne može puniti"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Pod kontrolom administratora"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 7bb27ab..010fa5e 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregant lentament"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregant sense fil"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"No s\'està carregant"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"El dispositiu està endollat però en aquests moments no es pot carregar"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connectat; no s\'està carregant"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlat per l\'administrador"</string>
<string name="disabled" msgid="8017887509554714950">"Desactivat"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 1a7e83b..4e57750 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Pomalé nabíjení"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bezdrátové nabíjení"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenabíjí se"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Zapojeno, ale nelze nabíjet"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Připojeno, nenabíjí se"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Nabito"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Spravováno administrátorem"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktivováno"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 0554960..fc96cbf 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Oplader langsomt"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Trådløs opladning"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Oplader ikke"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Enheden er tilsluttet en strømkilde. Det er ikke muligt at oplade på nuværende tidspunkt."</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tilsluttet, oplader ikke"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Opladet"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolleret af administratoren"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktiveret"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 83f516f..b5b579e 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Langsames Aufladen"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kabelloses Laden"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Wird nicht geladen"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Angeschlossen, kann derzeit nicht geladen werden"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Verbunden, wird nicht geladen"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Aufgeladen"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Durch den Administrator verwaltet"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktiviert"</string>
@@ -506,9 +506,9 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Wecker und Erinnerungen"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Erlauben, Wecker und Erinnerungen einzurichten"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wecker und Erinnerungen"</string>
- <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"Dieser App erlauben, Wecker zu stellen und andere Aktionen zu planen. Du kannst diese App nutzen, wenn du dein Smartphone nicht verwendest. Dies kann den Akkuverbrauch erhöhen. Wenn diese Berechtigung deaktiviert ist, funktionieren die App und die zugehörigen Wecker möglicherweise nicht wie erwartet."</string>
- <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"Dieser App erlauben, Wecker zu stellen und andere Aktionen zu planen. Du kannst diese App nutzen, wenn du dein Tablet nicht verwendest. Dies kann den Akkuverbrauch erhöhen. Wenn diese Berechtigung deaktiviert ist, funktionieren die App und die zugehörigen Wecker möglicherweise nicht wie erwartet."</string>
- <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"Dieser App erlauben, Wecker zu stellen und andere Aktionen zu planen. Du kannst diese App nutzen, wenn du dein Gerät nicht verwendest. Dies kann den Akkuverbrauch erhöhen. Wenn diese Berechtigung deaktiviert ist, funktionieren die App und die zugehörigen Wecker möglicherweise nicht wie erwartet."</string>
+ <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"Dieser App erlauben, Wecker zu stellen und andere Aktionen zu planen. So kann die App gestartet und ausgeführt werden, auch wenn du das Gerät nicht verwendest. Dies kann den Akkuverbrauch erhöhen. Wenn diese Berechtigung deaktiviert ist, funktionieren die App und die zugehörigen Wecker möglicherweise nicht wie erwartet."</string>
+ <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"Dieser App erlauben, Wecker zu stellen und andere Aktionen zu planen. So kann die App gestartet und ausgeführt werden, auch wenn du das Gerät nicht verwendest. Dies kann den Akkuverbrauch erhöhen. Wenn diese Berechtigung deaktiviert ist, funktionieren die App und die zugehörigen Wecker möglicherweise nicht wie erwartet."</string>
+ <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"Dieser App erlauben, Wecker zu stellen und andere Aktionen zu planen. So kann die App gestartet und ausgeführt werden, auch wenn du das Gerät nicht verwendest. Dies kann den Akkuverbrauch erhöhen. Wenn diese Berechtigung deaktiviert ist, funktionieren die App und die zugehörigen Wecker möglicherweise nicht wie erwartet."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planen, Wecker, Erinnerung, Uhr"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivieren"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"„Bitte nicht stören“ aktivieren"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 585d54e..cb76522 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Αργή φόρτιση"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ασύρματη φόρτιση"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Δεν φορτίζει"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Συνδέθηκε, δεν είναι δυνατή η φόρτιση αυτήν τη στιγμή"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Συνδεδεμένη, δεν φορτίζει"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Φορτισμένη"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ελέγχονται από το διαχειριστή"</string>
<string name="disabled" msgid="8017887509554714950">"Απενεργοποιημένο"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 46e4b401..2fb0078 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Plugged in, can\'t charge at the moment"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 705e903..450865b 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Plugged in, can\'t charge at the moment"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 46e4b401..2fb0078 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Plugged in, can\'t charge at the moment"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 46e4b401..2fb0078 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Plugged in, can\'t charge at the moment"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 8e944ba..525d4d2 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Plugged in, can\'t charge right now"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 0a6e98b..38af77a 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carga lenta"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carga inalámbrica"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando."</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectado. No se puede cargar en este momento"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Está conectado, pero no se está cargando"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada por el administrador"</string>
<string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 99b297f..8634f18 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Cargando lentamente"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Cargando sin cables"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Enchufado, pero no se puede cargar en este momento"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado, no se carga"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada por el administrador"</string>
<string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 75e0128..d46a99b 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Aeglaselt laadimine"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Juhtmevaba laadimine"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei lae"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Vooluvõrgus, praegu ei saa laadida"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ühendatud, ei laeta"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Laetud"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Juhib administraator"</string>
<string name="disabled" msgid="8017887509554714950">"Keelatud"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 9e7fa29..7a81e5f 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mantso kargatzen"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hari gabe kargatzen"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ez da kargatzen ari"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Konektatuta dago. Ezin da kargatu une honetan."</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Konektatuta dago, baina ez da kargatzen ari"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Kargatuta"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Administratzaileak kontrolatzen du"</string>
<string name="disabled" msgid="8017887509554714950">"Desgaituta"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 1d03bf5..7eae947 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"درحال شارژ شدن آهسته"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"درحال شارژ بیسیم"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"شارژ نمیشود"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"به برق وصل شده است، درحالحاضر شارژ نمیشود"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"متصل، شارژ نمیشود"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"شارژ کامل شد"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"توسط سرپرست سیستم کنترل میشود"</string>
<string name="disabled" msgid="8017887509554714950">"غیر فعال شد"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 68b4204..af97b8d 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Hidas lataus"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Langaton lataus"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei laturissa"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Kytketty virtalähteeseen, lataaminen ei onnistu"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Yhdistetty, ei ladata"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Ladattu"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Järjestelmänvalvoja hallinnoi tätä asetusta."</string>
<string name="disabled" msgid="8017887509554714950">"Pois päältä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 98f346c..36bdf47 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -197,10 +197,10 @@
<string name="choose_profile" msgid="343803890897657450">"Sélectionnez un profil"</string>
<string name="category_personal" msgid="6236798763159385225">"Personnel"</string>
<string name="category_work" msgid="4014193632325996115">"Professionnel"</string>
- <string name="development_settings_title" msgid="140296922921597393">"Options pour les concepteurs"</string>
- <string name="development_settings_enable" msgid="4285094651288242183">"Activer les options pour les concepteurs"</string>
+ <string name="development_settings_title" msgid="140296922921597393">"Options pour les développeurs"</string>
+ <string name="development_settings_enable" msgid="4285094651288242183">"Activer les options pour les développeurs"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Définir les options pour le développement de l\'application"</string>
- <string name="development_settings_not_available" msgid="355070198089140951">"Les options proposées aux concepteurs ne sont pas disponibles pour cet utilisateur."</string>
+ <string name="development_settings_not_available" msgid="355070198089140951">"Les options proposées aux développeurs ne sont pas disponibles pour cet utilisateur."</string>
<string name="vpn_settings_not_available" msgid="2894137119965668920">"Les paramètres de RPV ne sont pas disponibles pour cet utilisateur"</string>
<string name="tethering_settings_not_available" msgid="266821736434699780">"Les paramètres de partage de connexion ne sont pas disponibles pour cet utilisateur"</string>
<string name="apn_settings_not_available" msgid="1147111671403342300">"Les paramètres de point d\'accès ne sont pas disponibles pour cet utilisateur"</string>
@@ -234,7 +234,7 @@
<string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Numériser le code QR"</string>
<string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Associer l\'appareil par Wi-Fi en numérisant un code QR"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Veuillez vous connecter à un réseau Wi-Fi"</string>
- <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, débogage, concepteur"</string>
+ <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, débogage, développeur"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Raccourci de rapport de bogue"</string>
<string name="bugreport_in_power_summary" msgid="1885529649381831775">"Afficher un bouton permettant d\'établir un rapport de bogue dans le menu de démarrage"</string>
<string name="keep_screen_on" msgid="1187161672348797558">"Rester activé"</string>
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Recharge lente"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En recharge sans fil"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"N\'est pas en charge"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"L\'appareil est branché, mais il ne peut pas être chargé pour le moment"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connecté, pas en charge"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Contrôlé par l\'administrateur"</string>
<string name="disabled" msgid="8017887509554714950">"Désactivée"</string>
@@ -562,7 +562,7 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Créer un utilisateur…"</string>
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Création d\'un utilisateur en cours…"</string>
<string name="add_user_failed" msgid="4809887794313944872">"Impossible de créer un utilisateur"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 11f7db7..0645b9e 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charge lente"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En charge sans fil"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Pas en charge"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Appareil branché, mais impossible de le charger pour le moment"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connecté, pas en charge"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Contrôlé par l\'administrateur"</string>
<string name="disabled" msgid="8017887509554714950">"Désactivée"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index edf653f..8425e62 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Cargando lentamente"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Cargando sen fíos"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Non se está cargando"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectouse, pero non se pode cargar neste momento"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado, sen cargar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Opción controlada polo administrador"</string>
<string name="disabled" msgid="8017887509554714950">"Desactivada"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 32efcda..7fa52f5 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ધીમેથી ચાર્જ થાય છે"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"વાયરલેસથી ચાર્જિંગ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ચાર્જ થઈ રહ્યું નથી"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"પ્લગ ઇન કરેલ, હમણાં ચાર્જ કરી શકતા નથી"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"કનેક્ટ કરેલું છે, પણ ચાર્જ થઈ રહ્યું નથી"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ચાર્જ થયું"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"વ્યવસ્થાપક દ્વારા નિયંત્રિત"</string>
<string name="disabled" msgid="8017887509554714950">"અક્ષમ કર્યો"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index c312456..88ab593 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"धीरे चार्ज हो रही है"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस चार्जिंग"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज नहीं हो रही है"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"प्लग इन है, अभी चार्ज नहीं हो सकती"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"कनेक्ट किया गया, चार्ज नहीं हो रहा है"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"बैटरी चार्ज हो गई"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"इसका नियंत्रण एडमिन के पास है"</string>
<string name="disabled" msgid="8017887509554714950">"बंद किया गया"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 40e0a3a..0c048f0 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo punjenje"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Uključen, trenutačno se ne može puniti"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolira administrator"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 8f08997..4adef4d 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lassú töltés"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Vezeték nélküli töltés"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nem tölt"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Csatlakoztatva, jelenleg nem tölt"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Csatlakoztatva, nem töltődik"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Feltöltve"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Rendszergazda által irányítva"</string>
<string name="disabled" msgid="8017887509554714950">"Letiltva"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index ea1c59a..8abfdec 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Դանդաղ լիցքավորում"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Անլար լիցքավորում"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Չի լիցքավորվում"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Միացված է հոսանքին, այս պահին չի կարող լիցքավորվել"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Միացված է, չի լիցքավորվում"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Լիցքավորված է"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Վերահսկվում է ադմինիստրատորի կողմից"</string>
<string name="disabled" msgid="8017887509554714950">"Կասեցված է"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 20e6133..58b4daf 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mengisi daya lambat"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Mengisi daya nirkabel"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Tidak mengisi daya"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Tercolok, tidak dapat mengisi baterai sekarang"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Terhubung, tidak mengisi daya"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Terisi"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Dikontrol oleh admin"</string>
<string name="disabled" msgid="8017887509554714950">"Dinonaktifkan"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 7d36dc9..d6f9be8 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Hæg hleðsla"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hleður þráðlaust"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ekki í hleðslu"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Í sambandi, ekki hægt að hlaða eins og er"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tengt, ekki í hleðslu"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Fullhlaðin"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Stjórnað af kerfisstjóra"</string>
<string name="disabled" msgid="8017887509554714950">"Óvirkt"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index b0e660e..85f6999 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Ricarica lenta"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"In carica, wireless"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Non in carica"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Collegato alla corrente. Impossibile caricare al momento"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Dispositivo connesso, non in carica"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Carica"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Gestita dall\'amministratore"</string>
<string name="disabled" msgid="8017887509554714950">"Disattivato"</string>
@@ -506,7 +506,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Sveglie e promemoria"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Consenti l\'impostazione di sveglie e promemoria"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Sveglie e promemoria"</string>
- <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"Consenti a questa app di impostare sveglie e programmare altre azioni. Utilizzare quest\'app quando non stai usando il tuo telefono potrebbe consumare più batteria. Se questa autorizzazione è disattivata, l\'app potrebbe non funzionare normalmente e le relative sveglie potrebbero non avviarsi come programmato."</string>
+ <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"Consenti a questa app di impostare sveglie e programmare altre azioni. L\'app potrebbe essere in uso quando non utilizzi il telefono, comportando un consumo maggiore della batteria. Se questa autorizzazione è disattivata, l\'app potrebbe non funzionare normalmente e le relative sveglie potrebbero non avviarsi come programmato."</string>
<string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"Consenti a questa app di impostare sveglie e programmare altre azioni. Utilizzare quest\'app quando non stai usando il tuo tablet potrebbe consumare più batteria. Se questa autorizzazione è disattivata, l\'app potrebbe non funzionare normalmente e le relative sveglie potrebbero non avviarsi come programmato."</string>
<string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"Consenti a questa app di impostare sveglie e programmare altre azioni. Utilizzare quest\'app quando non stai usando il tuo dispositivo potrebbe consumare più batteria. Se questa autorizzazione è disattivata, l\'app potrebbe non funzionare normalmente e le relative sveglie potrebbero non avviarsi come programmato."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programmare, sveglia, promemoria, orologio"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 9e0c4a63..b98ad00 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"הסוללה נטענת לאט"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"בטעינה אלחוטית"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"לא בטעינה"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"המכשיר מחובר, אבל לא ניתן לטעון עכשיו"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"מחובר, לא בטעינה"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"הסוללה טעונה"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"נמצא בשליטת מנהל מערכת"</string>
<string name="disabled" msgid="8017887509554714950">"מושבת"</string>
@@ -505,7 +505,7 @@
<string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"פחות זמן."</string>
<string name="cancel" msgid="5665114069455378395">"ביטול"</string>
<string name="okay" msgid="949938843324579502">"אישור"</string>
- <string name="alarms_and_reminders_label" msgid="6918395649731424294">"השכמות ותזכורות"</string>
+ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"שעונים מעוררים ותזכורות"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"הרשאה להגדרה של שעונים מעוררים ותזכורות"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"שעונים מעוררים ותזכורות"</string>
<string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"הגדרה זו מתירה לאפליקציה להגדיר שעון מעורר ולתזמן אירועים אחרים. ייתכן שהאפליקציה תפעל גם כשלא נעשה שימוש בטלפון שלך, ולכן תגביר את צריכת הסוללה. אם ההרשאה הזו תושבת, ייתכן שהאפליקציה לא תפעל כראוי ושהשעונים המעוררים לא יפעלו כפי שתוזמנו."</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 4b484e5..3975006 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"低速充電中"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ワイヤレス充電中"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"充電していません"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"接続されていますが、現在、充電できません"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"接続済み、充電していません"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"充電が完了しました"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"管理者により管理されています"</string>
<string name="disabled" msgid="8017887509554714950">"無効"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index cc30cce..6874824 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ნელა იტენება"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"უსადენოდ დატენა"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"არ იტენება"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"მიერთებულია, დატენვა ამჟამად ვერ ხერხდება"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"დაკავშირებულია, არ იტენება"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"დატენილია"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"იმართება ადმინისტრატორის მიერ"</string>
<string name="disabled" msgid="8017887509554714950">"გამორთული"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 97101f7..f3a533f 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Баяу зарядталуда"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Сымсыз зарядталуда"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Зарядталу орындалып жатқан жоқ"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Қосылған, зарядталмайды"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Жалғанған, зарядталып жатқан жоқ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Зарядталды"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Әкімші басқарады"</string>
<string name="disabled" msgid="8017887509554714950">"Өшірілген"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 086a829..3a5199f 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"កំពុងសាកថ្មយឺត"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"កំពុងសាកថ្មឥតខ្សែ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"មិនកំពុងបញ្ចូលថ្ម"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"ដោតសាកថ្មរួចហើយ ប៉ុន្តែសាកថ្មមិនចូលទេឥឡូវនេះ"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"បានភ្ជាប់ មិនកំពុងសាកថ្ម"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"បានសាកថ្ម"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"គ្រប់គ្រងដោយអ្នកគ្រប់គ្រង"</string>
<string name="disabled" msgid="8017887509554714950">"បិទ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 54d31cb..e886de5 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ನಿಧಾನ ಗತಿಯ ಚಾರ್ಜಿಂಗ್"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ವೈರ್ಲೆಸ್ ಚಾರ್ಜಿಂಗ್"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"ಪ್ಲಗ್ ಇನ್ ಮಾಡಲಾಗಿದೆ, ಇದೀಗ ಚಾರ್ಜ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ಕನೆಕ್ಟ್ ಆಗಿದೆ, ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ನಿರ್ವಾಹಕರ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗಿದೆ"</string>
<string name="disabled" msgid="8017887509554714950">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 465467b..918250c 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"저속 충전 중"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"무선 충전 중"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"충전 안함"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"전원이 연결되었지만 현재 충전할 수 없음"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"연결됨, 충전 중 아님"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"청구됨"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"관리자가 제어"</string>
<string name="disabled" msgid="8017887509554714950">"사용 안함"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 538fe68..8efc1e4 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Жай кубатталууда"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Зымсыз кубатталууда"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Кубат алган жок"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"сайылып турат, бирок кубатталган жок"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Туташты, кубатталган жок"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Кубатталды"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Администратор тарабынан көзөмөлдөнөт"</string>
<string name="disabled" msgid="8017887509554714950">"Өчүрүлгөн"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 59fdbc3..ab829ab 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ກຳລັງສາກໄຟຊ້າໆ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ກຳລັງສາກໄຟໄຮ້ສາຍ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ບໍ່ໄດ້ສາກໄຟ"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"ສຽບສາຍແລ້ວ, ບໍ່ສາມາດສາກໄດ້ໃນຕອນນີ້"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ເຊື່ອມຕໍ່ແລ້ວ, ບໍ່ໄດ້ສາກໄຟ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ສາກເຕັມແລ້ວ"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ຄວບຄຸມໂດຍຜູ້ເບິ່ງແຍງ"</string>
<string name="disabled" msgid="8017887509554714950">"ປິດການນຳໃຊ້"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 021ecce..6aa200f 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lėtai įkraunama"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kraunama be laidų"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nekraunama"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Įjungta į maitinimo lizdą, bet šiuo metu įkrauti neįmanoma"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Prijungta, neįkraunama"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Įkrauta"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Valdo administratorius"</string>
<string name="disabled" msgid="8017887509554714950">"Neleidžiama"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index ecf915b..77a5d4b 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Notiek lēnā uzlāde"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bezvadu uzlāde"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenotiek uzlāde"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Pievienots, taču pašlaik nevar veikt uzlādi"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ierīce pievienota, uzlāde nenotiek"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Uzlādēts"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolē administrators"</string>
<string name="disabled" msgid="8017887509554714950">"Atspējots"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 9dbe24c..71a0c5c 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Бавно полнење"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Се полни безжично"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не се полни"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Приклучен е, но батеријата не може да се полни во моментов"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Поврзана, не се полни"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Полна"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролирано од администраторот"</string>
<string name="disabled" msgid="8017887509554714950">"Оневозможено"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index a99d864..11c160d 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"പതുക്കെയുള്ള ചാർജിംഗ്"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"വയർലെസായി ചാർജുചെയ്യുന്നു"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"പ്ലഗ് ഇൻ ചെയ്തു, ഇപ്പോൾ ചാർജ് ചെയ്യാനാവില്ല"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"കണക്റ്റ് ചെയ്തിരിക്കുന്നു, ചാർജ് ചെയ്യുന്നില്ല"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ചാർജായി"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"അഡ്മിൻ നിയന്ത്രിക്കുന്നത്"</string>
<string name="disabled" msgid="8017887509554714950">"പ്രവർത്തനരഹിതമാക്കി"</string>
@@ -506,7 +506,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"അലാറങ്ങളും റിമെെൻഡറുകളും"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"അലാറവും റിമെെൻഡറും സജ്ജീകരിക്കാൻ അനുവദിക്കുക"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"അലാറങ്ങളും റിമെെൻഡറുകളും"</string>
- <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും മറ്റ് പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. കൂടുതൽ ബാറ്ററി ഉപയോഗിച്ചേക്കാവുന്ന നിങ്ങളുടെ ഫോൺ ഉപയോഗിക്കാത്തപ്പോൾ ഈ ആപ്പ് ഉപയോഗിച്ചേക്കാം. ഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് സ്വാഭാവികമായി പ്രവർത്തിച്ചേക്കില്ല, ഷെഡ്യൂൾ ചെയ്ത പോലെ അതിന്റെ അലാറങ്ങളും പ്രവർത്തിക്കില്ല."</string>
+ <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും മറ്റ് പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. നിങ്ങൾ ഫോൺ ഉപയോഗിക്കാത്തപ്പോൾ ഈ ആപ്പ് ഉപയോഗിച്ചേക്കാം, ഇത് കൂടുതൽ ബാറ്ററി ഉപയോഗിക്കും. ഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് സാധാരണ നിലയിൽ പ്രവർത്തിച്ചേക്കില്ല, ഷെഡ്യൂൾ ചെയ്ത പോലെ അതിന്റെ അലാറങ്ങളും പ്രവർത്തിക്കില്ല."</string>
<string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും മറ്റ് പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. കൂടുതൽ ബാറ്ററി ഉപയോഗിച്ചേക്കാവുന്ന നിങ്ങളുടെ ടാബ്ലെറ്റ് ഉപയോഗിക്കാത്തപ്പോൾ ഈ ആപ്പ് ഉപയോഗിച്ചേക്കാം. ഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് സ്വാഭാവികമായി പ്രവർത്തിച്ചേക്കില്ല, ഷെഡ്യൂൾ ചെയ്ത പോലെ അതിന്റെ അലാറങ്ങളും പ്രവർത്തിക്കില്ല."</string>
<string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും മറ്റ് പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. കൂടുതൽ ബാറ്ററി ഉപയോഗിച്ചേക്കാവുന്ന നിങ്ങളുടെ ഉപകരണം ഉപയോഗിക്കാത്തപ്പോൾ ഈ ആപ്പ് ഉപയോഗിച്ചേക്കാം. ഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് സ്വാഭാവികമായി പ്രവർത്തിച്ചേക്കില്ല, ഷെഡ്യൂൾ ചെയ്ത പോലെ അതിന്റെ അലാറങ്ങളും പ്രവർത്തിക്കില്ല."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ഷെഡ്യൂൾ, അലാറം, റിമെെൻഡർ, ക്ലോക്ക്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index e8da28f..2e9fa6a 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Удаан цэнэглэж байна"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Утасгүй цэнэглэж байна"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Цэнэглэхгүй байна"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Залгаастай тул одоо цэнэглэх боломжгүй"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Холбогдсон, цэнэглээгүй байна"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Цэнэглэсэн"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Админ удирдсан"</string>
<string name="disabled" msgid="8017887509554714950">"Идэвхгүйжүүлсэн"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 332d168..16c7f39 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"हळूहळू चार्ज होत आहे"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेसने चार्ज होत आहे"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज होत नाही"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"प्लग इन केलेले आहे, आता चार्ज करू शकत नाही"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"कनेक्ट केले, चार्ज होत नाही"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"चार्ज झाली"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"प्रशासकाने नियंत्रित केलेले"</string>
<string name="disabled" msgid="8017887509554714950">"अक्षम"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 25c44cf..9707c5e 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mengecas dgn prlahan"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Mengecas tanpa wayar"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Tidak mengecas"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Dipalamkan, tidak boleh mengecas sekarang"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Bersambung, tidak mengecas"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Sudah dicas"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Dikawal oleh pentadbir"</string>
<string name="disabled" msgid="8017887509554714950">"Dilumpuhkan"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index f247c37..0cf3a0f 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"နှေးကွေးစွာ အားသွင်း"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ကြိုးမဲ့ အားသွင်းနေသည်"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"အားသွင်းမနေပါ"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"ပလပ်ထိုးထားသောကြောင့် ယခုအားသွင်း၍ မရသေးပါ"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ချိတ်ဆက်ထားသည်၊ အားသွင်းမနေပါ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"အားသွင်းပြီးပါပြီ"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"စီမံခန့်ခွဲသူမှ ထိန်းချုပ်ပါသည်"</string>
<string name="disabled" msgid="8017887509554714950">"ပိတ်ထားပြီး"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index c28bf27..fa1837f 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lader sakte"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Lader trådløst"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Lader ikke"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Laderen er koblet til – kan ikke lade akkurat nå"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tilkoblet, lader ikke"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Ladet"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrollert av administratoren"</string>
<string name="disabled" msgid="8017887509554714950">"Slått av"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 645248c..e5e9a96 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"बिस्तारै चार्ज गरिँदै"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस तरिकाले चार्ज गरिँदै छ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज भइरहेको छैन"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"प्लगइन गरिएको छ, अहिले नै चार्ज गर्न सकिँदैन"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"कनेक्ट गरिएको छ, चार्ज भइरहेको छैन"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"चार्ज भयो"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"प्रशासकद्वारा नियन्त्रित"</string>
<string name="disabled" msgid="8017887509554714950">"असक्षम पारियो"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 9889b22..5472785 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Langzaam opladen"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Draadloos opladen"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Wordt niet opgeladen"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Aangesloten, kan nu niet opladen"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Verbonden, wordt niet opgeladen"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Opgeladen"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ingesteld door beheerder"</string>
<string name="disabled" msgid="8017887509554714950">"Uitgezet"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 66b1d61..2fd00cb 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ଧୀରେ ଚାର୍ଜ ହେଉଛି"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ୱେୟରଲେସ ଭାବେ ଚାର୍ଜିଂ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ଚାର୍ଜ ହେଉନାହିଁ"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"ପ୍ଲଗ୍ରେ ଲାଗିଛି, ହେଲେ ଏବେ ଚାର୍ଜ କରିପାରିବ ନାହିଁ"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ସଂଯୋଗ କରାଯାଇଛି, ଚାର୍ଜ ହେଉନାହିଁ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ଚାର୍ଜ ହୋଇଯାଇଛି"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ଆଡ୍ମିନ୍ ଦ୍ୱାରା ନିୟନ୍ତ୍ରିତ"</string>
<string name="disabled" msgid="8017887509554714950">"ଅକ୍ଷମ ହୋଇଛି"</string>
@@ -506,9 +506,9 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"ଆଲାରାମ୍ ଏବଂ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ଆଲାରାମ ଓ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ ସେଟ କରିବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ଆଲାରାମ୍ ଏବଂ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ"</string>
- <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଫୋନ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ପରି କାମ କରିବ ନାହିଁ।"</string>
- <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ପରି କାମ କରିବ ନାହିଁ।"</string>
- <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଡିଭାଇସ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ପରି କାମ କରିବ ନାହିଁ।"</string>
+ <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ଅନୁସାରେ କାମ କରିବ ନାହିଁ।"</string>
+ <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ଅନୁସାରେ କାମ କରିବ ନାହିଁ।"</string>
+ <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ଅନୁସାରେ କାମ କରିବ ନାହିଁ।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ସିଡୁଲ୍, ଆଲାରାମ୍, ରିମାଇଣ୍ଡର୍, ଘଣ୍ଟା"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ଚାଲୁ କରନ୍ତୁ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍ କରନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index d314ad9..53855a8 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ਬਿਨਾਂ ਤਾਰ ਤੋਂ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"ਪਲੱਗ ਲੱਗਾ ਹੋਇਆ ਹੈ, ਇਸ ਸਮੇਂ ਚਾਰਜ ਨਹੀਂ ਹੋ ਸਕਦੀ"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ਕਨੈਕਟ ਹੈ, ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ਚਾਰਜ ਹੋ ਗਈ"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕੀਤੀ ਗਈ"</string>
<string name="disabled" msgid="8017887509554714950">"ਅਯੋਗ ਬਣਾਇਆ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 446fca3..cac4a8e 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Wolne ładowanie"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ładowanie bezprzewodowe"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nie podłączony"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Podłączony. Nie można teraz ładować"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Podłączono, brak ładowania"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Naładowana"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolowane przez administratora"</string>
<string name="disabled" msgid="8017887509554714950">"Wyłączone"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 117722c..ff8b5e1 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregando devagar"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregando sem fio"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está carregando"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectado. Não é possível carregar no momento"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado sem carregar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada pelo admin"</string>
<string name="disabled" msgid="8017887509554714950">"Desativado"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index ce3a748..f5e2240 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregamento lento"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"A carregar sem fios"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está a carregar"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ligada à corrente, não é possível carregar neste momento"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ligado, não está a carregar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlado pelo gestor"</string>
<string name="disabled" msgid="8017887509554714950">"Desativada"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 117722c..ff8b5e1 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregando devagar"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregando sem fio"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está carregando"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectado. Não é possível carregar no momento"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado sem carregar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada pelo admin"</string>
<string name="disabled" msgid="8017887509554714950">"Desativado"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index e0789eb..ed0b470 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Se încarcă lent"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Se încarcă wireless"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nu se încarcă"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectat, nu se poate încărca chiar acum"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectat, nu se încarcă"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Încărcată"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlată de administrator"</string>
<string name="disabled" msgid="8017887509554714950">"Dezactivată"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 375c0ca..5bbb9f4 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Медленная зарядка"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Беспроводная зарядка"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не заряжается"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Подключено, не заряжается"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Подключено, не заряжается"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Батарея заряжена"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролируется администратором"</string>
<string name="disabled" msgid="8017887509554714950">"Отключено"</string>
@@ -508,9 +508,9 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Будильники и напоминания"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Разрешить установку будильников и напоминаний"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будильники и напоминания"</string>
- <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"Разрешить этому приложению устанавливать будильники и планировать другие действия. Оно также сможет работать в то время, когда вы не пользуетесь телефоном. Не исключено, что в результате заряд батареи будет расходоваться быстрее. Если этот параметр отключен, в работе приложения могут возникнуть сбои, а будильники перестанут запускаться по расписанию."</string>
- <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"Разрешить этому приложению устанавливать будильники и планировать другие действия. Оно также сможет работать в то время, когда вы не пользуетесь планшетом. Не исключено, что в результате заряд батареи будет расходоваться быстрее. Если этот параметр отключен, в работе приложения могут возникнуть сбои, а будильники перестанут запускаться по расписанию."</string>
- <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"Разрешить этому приложению устанавливать будильники и планировать другие действия. Оно также сможет работать в то время, когда вы не пользуетесь устройством. Не исключено, что в результате заряд батареи будет расходоваться быстрее. Если этот параметр отключен, в работе приложения могут возникнуть сбои, а будильники перестанут запускаться по расписанию."</string>
+ <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"Если вы разрешите этому приложению устанавливать будильники и создавать напоминания, оно сможет работать, даже когда вы не пользуетесь телефоном. Правда, в результате заряд батареи будет расходоваться быстрее. Если отключить эту настройку, в работе приложения могут возникать сбои, а будильники не будут запускаться вовремя."</string>
+ <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"Если вы разрешите этому приложению устанавливать будильники и создавать напоминания, оно сможет работать, даже когда вы не пользуетесь планшетом. Правда, в результате заряд батареи будет расходоваться быстрее. Если отключить эту настройку, в работе приложения могут возникать сбои, а будильники не будут запускаться вовремя."</string>
+ <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"Если вы разрешите этому приложению устанавливать будильники и создавать напоминания, оно сможет работать, даже когда вы не пользуетесь устройством. Правда, в результате заряд батареи будет расходоваться быстрее. Если отключить эту настройку, в работе приложения могут возникать сбои, а будильники не будут запускаться вовремя."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"установить, будильник, напоминание, часы"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Включить"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Включите режим \"Не беспокоить\""</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 8465229..16cb85a 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"සෙමින් ආරෝපණය"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"නොරැහැන්ව ආරෝපණය වේ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ආරෝපණය නොවේ"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"පේනුගත කර ඇත, මේ අවස්ථාවේදී ආරෝපණය කළ නොහැකිය"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"සම්බන්ධයි, ආරෝපණය නොවේ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"අරෝපිතයි"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"පරිපාලක විසින් පාලනය කරන ලදී"</string>
<string name="disabled" msgid="8017887509554714950">"අබල කර ඇත"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 09589cd9..018ee86 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Pomalé nabíjanie"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Nabíja sa bezdrôtovo"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenabíja sa"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Pripojené, ale nie je možné nabíjať"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Pripojené, nenabíja sa"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Nabité"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ovládané správcom"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktivované"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 7edbeca..0ad339e 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Počasno polnjenje"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Brezžično polnjenje"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Se ne polni"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Priključeno, trenutno ni mogoče polniti"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, se ne polni"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Baterija napolnjena"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Nadzira skrbnik"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogočeno"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index aa0c118..5868d78 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -460,7 +460,8 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Po karikohet ngadalë"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Po karikohet pa tel"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nuk po karikohet"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Në prizë, por nuk mund të karikohet për momentin"</string>
+ <!-- no translation found for battery_info_status_not_charging (3371084153747234837) -->
+ <skip />
<string name="battery_info_status_full" msgid="1339002294876531312">"Karikuar"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolluar nga administratori"</string>
<string name="disabled" msgid="8017887509554714950">"Çaktivizuar"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 0d03460a..5e7022d 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Споро се пуни"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бежично пуњење"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не пуни се"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Прикључено је, али пуњење тренутно није могуће"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Повезано, не пуни се"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Напуњено"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролише администратор"</string>
<string name="disabled" msgid="8017887509554714950">"Онемогућено"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 49ee6f5..ced30cf 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laddas långsamt"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laddas trådlöst"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Laddar inte"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ansluten, kan inte laddas just nu"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ansluten, laddas inte"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Laddat"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Strys av administratören"</string>
<string name="disabled" msgid="8017887509554714950">"Inaktiverad"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index b4a79eb..29c6a4f 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Inachaji pole pole"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Inachaji bila kutumia waya"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Haichaji"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Haiwezi kuchaji kwa sasa"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Imeunganishwa, haichaji"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Imechajiwa"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Imedhibitiwa na msimamizi"</string>
<string name="disabled" msgid="8017887509554714950">"Imezimwa"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index bdefb85..2ac3830 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"மெதுவாக சார்ஜாகிறது"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"வயரின்றி சார்ஜாகிறது"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"சார்ஜ் செய்யப்படவில்லை"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"செருகப்பட்டது, ஆனால் இப்போது சார்ஜ் செய்ய முடியவில்லை"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"இணைக்கப்பட்டுள்ளது, சார்ஜாகவில்லை"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"சார்ஜாகிவிட்டது"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"நிர்வாகி கட்டுப்படுத்துகிறார்"</string>
<string name="disabled" msgid="8017887509554714950">"முடக்கப்பட்டது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index f919296..2cd1bec 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"నెమ్మదిగా ఛార్జింగ్"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"వైర్లెస్ ఛార్జింగ్"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ఛార్జ్ కావడం లేదు"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"ప్లగ్ ఇన్ చేయబడింది, ప్రస్తుతం ఛార్జ్ చేయడం సాధ్యం కాదు"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"కనెక్ట్ చేయబడింది, ఛార్జ్ చేయబడలేదు"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ఛార్జ్ చేయబడింది"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"నిర్వాహకుని ద్వారా నియంత్రించబడింది"</string>
<string name="disabled" msgid="8017887509554714950">"డిజేబుల్ చేయబడింది"</string>
@@ -506,7 +506,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"అలారాలు, రిమైండర్లు"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"అలారాలు, రిమైండర్లను సెట్ చేయడానికి అనుమతించండి"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"అలారాలు & రిమైండర్లు"</string>
- <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"అలారాలను సెట్ చేయడానికి, ఇతర చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. మీరు మీ ఫోన్ను ఉపయోగించనప్పుడు ఈ యాప్ ఉపయోగించబడవచ్చు, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు. ఈ అనుమతిని ఆఫ్ చేసినట్లైతే, ఈ యాప్ సాధారణంగా పనిచేయకపోవచ్చు అలాగే దాని అలారాలు షెడ్యూల్ ప్రకారం పనిచేయవు."</string>
+ <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"అలారాలను సెట్ చేయడానికి, ఇతర చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. మీ ఫోన్ను మీరు ఉపయోగించనప్పుడు కూడా ఈ యాప్ ఉపయోగించబడవచ్చు, తద్వారా ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు. ఈ అనుమతిని ఆఫ్ చేస్తే, ఈ యాప్ సాధారణ రీతిలో పనిచేయకపోవచ్చు, దాని అలారాలు షెడ్యూల్ ప్రకారం పనిచేయవు."</string>
<string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"అలారాలను సెట్ చేయడానికి, ఇతర చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. మీరు మీ టాబ్లెట్ను ఉపయోగించనప్పుడు ఈ యాప్ ఉపయోగించబడవచ్చు, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు. ఈ అనుమతిని ఆఫ్ చేసినట్లైతే, ఈ యాప్ సాధారణంగా పనిచేయకపోవచ్చు అలాగే దాని అలారాలు షెడ్యూల్ ప్రకారం పనిచేయవు."</string>
<string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"అలారాలను సెట్ చేయడానికి, ఇతర చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. మీరు మీ పరికరాన్ని ఉపయోగించనప్పుడు ఈ యాప్ ఉపయోగించబడవచ్చు, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు. ఈ అనుమతిని ఆఫ్ చేసినట్లైతే, ఈ యాప్ సాధారణంగా పనిచేయకపోవచ్చు అలాగే దాని అలారాలు షెడ్యూల్ ప్రకారం పనిచేయవు."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"షెడ్యూల్, అలారం, రిమైండర్, గడియారం"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 798531a..7f26fc5 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"กำลังชาร์จอย่างช้าๆ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"กำลังชาร์จแบบไร้สาย"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ไม่ได้ชาร์จ"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"เสียบอยู่ ไม่สามารถชาร์จได้ในขณะนี้"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"เชื่อมต่ออยู่ ไม่ได้ชาร์จ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ชาร์จแล้ว"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ผู้ดูแลระบบเป็นผู้ควบคุม"</string>
<string name="disabled" msgid="8017887509554714950">"ปิดอยู่"</string>
@@ -506,9 +506,9 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"การปลุกและการช่วยเตือน"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"อนุญาตให้ตั้งปลุกและการช่วยเตือน"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"การปลุกและการช่วยเตือน"</string>
- <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้เมื่อคุณไม่ได้ใช้โทรศัพท์ ซึ่งอาจใช้แบตเตอรี่มากขึ้น หากสิทธิ์ดังกล่าวปิดอยู่ แอปอาจทำงานไม่ปกติ และการปลุกจะไม่ทำงานตามกำหนดเวลา"</string>
- <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้เมื่อคุณไม่ได้ใช้แท็บเล็ต ซึ่งอาจใช้แบตเตอรี่มากขึ้น หากสิทธิ์ดังกล่าวปิดอยู่ แอปอาจทำงานไม่ปกติ และการปลุกจะไม่ทำงานตามกำหนดเวลา"</string>
- <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้เมื่อคุณไม่ได้ใช้อุปกรณ์ ซึ่งอาจใช้แบตเตอรี่มากขึ้น หากสิทธิ์ดังกล่าวปิดอยู่ แอปอาจทำงานไม่ปกติ และการปลุกจะไม่ทำงานตามกำหนดเวลา"</string>
+ <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้แม้คุณจะไม่ได้ใช้โทรศัพท์อยู่ จึงอาจทำให้เปลืองแบตเตอรี่มากขึ้น การปิดสิทธิ์นี้จะทำให้แอปทำงานไม่ปกติและการปลุกไม่ทำงานตามกำหนดเวลา"</string>
+ <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้แม้คุณจะไม่ได้ใช้แท็บเล็ตอยู่ จึงอาจทำให้เปลืองแบตเตอรี่มากขึ้น การปิดสิทธิ์นี้จะทำให้แอปทำงานไม่ปกติและการปลุกไม่ทำงานตามกำหนดเวลา"</string>
+ <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้แม้คุณจะไม่ได้ใช้อุปกรณ์อยู่ จึงอาจทำให้เปลืองแบตเตอรี่มากขึ้น การปิดสิทธิ์นี้จะทำให้แอปทำงานไม่ปกติและการปลุกไม่ทำงานตามกำหนดเวลา"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"กำหนดเวลา การปลุก การช่วยเตือน นาฬิกา"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"เปิด"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"เปิด \"ห้ามรบกวน\""</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index ad7e557..0962f93 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mabagal na charge"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Wireless na nagcha-charge"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Hindi nagcha-charge"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Nakasaksak, hindi makapag-charge sa ngayon"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Nakakonekta, hindi nagcha-charge"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Nasingil"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Pinapamahalaan ng admin"</string>
<string name="disabled" msgid="8017887509554714950">"Naka-disable"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index f80a32e..727dee8 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Yavaş şarj oluyor"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kablosuz şarj oluyor"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Şarj olmuyor"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Prize takıldı, şu anda şarj olamıyor"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Bağlandı, şarj olmuyor"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Şarj oldu"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Yönetici tarafından denetleniyor"</string>
<string name="disabled" msgid="8017887509554714950">"Devre dışı"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 292ec62..dffc3d8 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Повільне заряджання"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бездротове заряджання"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не заряджається"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Підключено, не заряджається"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Підключено, не заряджається"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Заряджено"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Керується адміністратором"</string>
<string name="disabled" msgid="8017887509554714950">"Вимкнено"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 4a3800d..90b3e7d 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"آہستہ چارج ہو رہا ہے"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"وائرلیس طریقے سے چارج ہو رہی ہے"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"چارج نہیں ہو رہا ہے"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"پلگ ان ہے، ابھی چارج نہیں کر سکتے"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"منسلک ہے، چارج نہیں ہو رہی ہے"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"چارج ہو گئی"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"کنٹرول کردہ بذریعہ منتظم"</string>
<string name="disabled" msgid="8017887509554714950">"غیر فعال"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 228ba77..dc005a4 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sekin quvvat olmoqda"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Simsiz quvvat olmoqda"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Quvvat olmayapti"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ulangan, lekin quvvat olmayapti"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ulangan, quvvat olmayapti"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Quvvat oldi"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Administrator tomonidan boshqariladi"</string>
<string name="disabled" msgid="8017887509554714950">"Yoqilmagan"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 36df4cd..d0c0fa2 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Đang sạc chậm"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Đang sạc không dây"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Hiện không sạc"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Đã cắm nhưng không thể sạc ngay"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Đã kết nối nhưng chưa sạc"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Đã sạc"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Do quản trị viên kiểm soát"</string>
<string name="disabled" msgid="8017887509554714950">"Đã tắt"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 4934e44..da62ded 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"正在慢速充电"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"正在无线充电"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"未在充电"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"已插入电源,但是现在无法充电"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"已连接,尚未充电"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"已充满电"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"由管理员控制"</string>
<string name="disabled" msgid="8017887509554714950">"已停用"</string>
@@ -506,9 +506,9 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"闹钟和提醒"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"允许设置闹钟和提醒"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"闹钟和提醒"</string>
- <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"允许该应用设置闹钟或安排其他操作的执行时间。该应用可能会在您未使用手机时运行,手机或许会比较耗电。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟将无法在排定的时间响起。"</string>
- <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"允许该应用设置闹钟或安排其他操作的执行时间。该应用可能会在您未使用平板电脑时运行,平板电脑或许会比较耗电。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟将无法在排定的时间响起。"</string>
- <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"允许该应用设置闹钟或安排其他操作的执行时间。该应用可能会在您未使用设备时运行,设备或许会比较耗电。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟将无法在排定的时间响起。"</string>
+ <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"允许该应用设置闹钟及安排其他操作的执行时间。该应用可能会在您未使用手机时运行,以致手机的电源消耗更快。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟也无法在排定的时间响起。"</string>
+ <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"允许该应用设置闹钟及安排其他操作的执行时间。该应用可能会在您未使用平板电脑时运行,以致平板电脑的电源消耗更快。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟也无法在排定的时间响起。"</string>
+ <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"允许该应用设置闹钟及安排其他操作的执行时间。该应用可能会在您未使用设备时运行,以致设备电源消耗更快。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟也无法在排定的时间响起。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"设置, 闹钟, 提醒, 时钟, schedule, alarm, reminder, clock"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"开启"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"开启勿扰模式"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index f46710e3..5624905 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"正在慢速充電"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"無線充電中"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"非充電中"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"已連接電源插頭,但目前無法充電"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"已連接,非充電中"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"已充滿電"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"已由管理員停用"</string>
<string name="disabled" msgid="8017887509554714950">"已停用"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 2e77d6d..1215eb1 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"慢速充電中"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"正在進行無線充電"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"非充電中"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"已接上電源,但現在無法充電"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"已連接,尚未充電"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"充電完成"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"已由管理員停用"</string>
<string name="disabled" msgid="8017887509554714950">"已停用"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 30e2316..825f173 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -460,7 +460,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Ishaja kancane"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Iyashaja ngaphandle kwentambo"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ayishaji"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Kuxhunyiwe, ayikwazi ukushaja khona manje"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ixhunyiwe, ayishaji"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Kushajiwe"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kulawulwa umqondisi"</string>
<string name="disabled" msgid="8017887509554714950">"Akusebenzi"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
index 35499c9..877dd2d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -25,6 +25,7 @@
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.PermissionManager;
import android.text.format.DateUtils;
import android.util.IconDrawableFactory;
import android.util.Log;
@@ -132,7 +133,8 @@
}
}
}
- if (showApp) {
+ if (showApp && PermissionManager.shouldShowPackageForIndicatorCached(mContext,
+ packageName)) {
Access access = getAccessFromOps(now, ops);
if (access != null) {
accesses.add(access);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 9889419..4dd3ff1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -187,8 +187,9 @@
}
protected int getIconColorAttr() {
- return (mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED)
- ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
+ final boolean accent = (mWifiEntry.hasInternetAccess()
+ && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED);
+ return accent ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
}
private void updateIcon(boolean showX, int level) {
@@ -267,7 +268,7 @@
}
public Drawable getIcon(boolean showX, int level) {
- return mContext.getDrawable(Utils.getWifiIconResource(showX, level));
+ return mContext.getDrawable(WifiUtils.getInternetIconResource(level, showX));
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 2c2ca3b..0696916 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -80,36 +80,41 @@
@Override
public void onCapabilitiesChanged(
Network network, NetworkCapabilities networkCapabilities) {
- if (!mNetworks.contains(network.getNetId())) {
- // New network
- boolean isVcnOverWifi =
- networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
- && (Utils.tryGetWifiInfoForVcn(networkCapabilities) != null);
- boolean isWifi =
- networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
- if (isVcnOverWifi || isWifi) {
- mNetworks.add(network.getNetId());
- }
- }
-
+ boolean isVcnOverWifi = false;
+ boolean isWifi = false;
WifiInfo wifiInfo = null;
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
wifiInfo = Utils.tryGetWifiInfoForVcn(networkCapabilities);
+ isVcnOverWifi = (wifiInfo != null);
} else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
+ isWifi = true;
}
- String log = new StringBuilder()
- .append(SSDF.format(System.currentTimeMillis())).append(",")
- .append("onCapabilitiesChanged: ")
- .append("network=").append(network).append(",")
- .append("networkCapabilities=").append(networkCapabilities)
- .toString();
- recordLastWifiNetwork(log);
- if (wifiInfo != null) {
- updateWifiInfo(wifiInfo);
- updateStatusLabel();
- mCallback.run();
+ // As long as it is a WiFi network, we will log it in the dumpsys for debugging.
+ if (isVcnOverWifi || isWifi) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("onCapabilitiesChanged: ")
+ .append("network=").append(network).append(",")
+ .append("networkCapabilities=").append(networkCapabilities)
+ .toString();
+ recordLastWifiNetwork(log);
}
+ // Ignore the WiFi network if it doesn't contain any valid WifiInfo, or it is not the
+ // primary WiFi.
+ if (wifiInfo == null || !wifiInfo.isPrimary()) {
+ // Remove the network from the tracking list once it becomes non-primary.
+ if (mNetworks.contains(network.getNetId())) {
+ mNetworks.remove(network.getNetId());
+ }
+ return;
+ }
+ if (!mNetworks.contains(network.getNetId())) {
+ mNetworks.add(network.getNetId());
+ }
+ updateWifiInfo(wifiInfo);
+ updateStatusLabel();
+ mCallback.run();
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 15b146d..6100615 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -36,6 +36,22 @@
private static final int INVALID_RSSI = -127;
+ static final int[] WIFI_PIE = {
+ com.android.internal.R.drawable.ic_wifi_signal_0,
+ com.android.internal.R.drawable.ic_wifi_signal_1,
+ com.android.internal.R.drawable.ic_wifi_signal_2,
+ com.android.internal.R.drawable.ic_wifi_signal_3,
+ com.android.internal.R.drawable.ic_wifi_signal_4
+ };
+
+ static final int[] NO_INTERNET_WIFI_PIE = {
+ R.drawable.ic_no_internet_wifi_signal_0,
+ R.drawable.ic_no_internet_wifi_signal_1,
+ R.drawable.ic_no_internet_wifi_signal_2,
+ R.drawable.ic_no_internet_wifi_signal_3,
+ R.drawable.ic_no_internet_wifi_signal_4
+ };
+
public static String buildLoggingSummary(AccessPoint accessPoint, WifiConfiguration config) {
final StringBuilder summary = new StringBuilder();
final WifiInfo info = accessPoint.getInfo();
@@ -245,6 +261,20 @@
return context.getString(R.string.wifi_unmetered_label);
}
+ /**
+ * Returns the Internet icon resource for a given RSSI level.
+ *
+ * @param level The number of bars to show (0-4)
+ * @param noInternet True if a connected Wi-Fi network cannot access the Internet
+ * @throws IllegalArgumentException if an invalid RSSI level is given.
+ */
+ public static int getInternetIconResource(int level, boolean noInternet) {
+ if (level < 0 || level >= WIFI_PIE.length) {
+ throw new IllegalArgumentException("No Wifi icon found for level: " + level);
+ }
+ return noInternet ? NO_INTERNET_WIFI_PIE[level] : WIFI_PIE[level];
+ }
+
public static boolean isMeteredOverridden(WifiConfiguration config) {
return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE;
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
index 53a382a..b0c5314 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
@@ -89,4 +89,24 @@
assertThat(mSpinnerPreference.getSelectedItem())
.isEqualTo(mSpinner.getAdapter().getItem(1));
}
+
+ @Test
+ public void onBindViewHolder_setClickableTrue_isClickableTrue() {
+ mSpinnerPreference.setClickable(true);
+
+ mSpinnerPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mSpinner.isClickable()).isTrue();
+ assertThat(mSpinner.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void onBindViewHolder_setClickableFalse_isClickableFalse() {
+ mSpinnerPreference.setClickable(false);
+
+ mSpinnerPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mSpinner.isClickable()).isFalse();
+ assertThat(mSpinner.isEnabled()).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
index e24b525..9f31cef 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
@@ -48,7 +48,7 @@
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
- mRootView = View.inflate(mContext, R.layout.settings_action_buttons, null /* parent */);
+ mRootView = View.inflate(mContext, R.layout.settingslib_action_buttons, null /* parent */);
mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
mPref = new ActionButtonsPreference(mContext);
}
@@ -251,6 +251,114 @@
assertThat(drawablesAroundText[1 /* top */]).isNull();
}
+ @Test
+ public void onBindViewHolder_setAllButton_shouldShowAllDivider() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton2Text(R.string.install_other_apps);
+ mPref.setButton3Text(R.string.install_other_apps);
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_setAllButtonWithoutButton2_shouldHideDivider1() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton3Text(R.string.install_other_apps);
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_setAllButtonWithoutButton3_shouldHideDivider2() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton2Text(R.string.install_other_apps);
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_setButton1And4_shouldShowDivider3Only() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_setOneButtonOnly_noDivider() {
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setButton1And2_shouldShowDivider1Only() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton2Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setButton1And3_shouldShowDivider2Only() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton3Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
public static ActionButtonsPreference createMock() {
final ActionButtonsPreference pref = mock(ActionButtonsPreference.class);
when(pref.setButton1Text(anyInt())).thenReturn(pref);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index 1a4f0ef..4503669 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -62,4 +62,11 @@
assertThat(mFooterPreference.getTitle()).isEqualTo("summary");
}
+
+ @Test
+ public void setContentDescription_contentSet_shouldGetSameContentDescription() {
+ mFooterPreference.setContentDescription("test");
+
+ assertThat(mFooterPreference.getContentDescription()).isEqualTo("test");
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
new file mode 100644
index 0000000..4a14403
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.airbnb.lottie.LottieAnimationView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+public class IllustrationPreferenceTest {
+
+ @Mock
+ LottieAnimationView mAnimationView;
+
+ private IllustrationPreference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ final Context context = RuntimeEnvironment.application;
+ final AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
+ mPreference = new IllustrationPreference(context, attributeSet);
+ ReflectionHelpers.setField(mPreference, "mIllustrationView", mAnimationView);
+ }
+
+ @Test
+ public void isAnimating_lottieAnimationViewIsNotAnimating_shouldReturnFalse() {
+ when(mAnimationView.isAnimating()).thenReturn(false);
+
+ assertThat(mPreference.isAnimating()).isFalse();
+ }
+
+ @Test
+ public void isAnimating_lottieAnimationViewIsAnimating_shouldReturnTrue() {
+ when(mAnimationView.isAnimating()).thenReturn(true);
+
+ assertThat(mPreference.isAnimating()).isTrue();
+ }
+}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index ccbcb89..8e6e251f 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -251,7 +251,7 @@
<integer name="def_accessibility_magnification_capabilities">3</integer>
<!-- Default for Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW -->
- <bool name="def_enable_non_resizable_multi_window">true</bool>
+ <bool name="def_enable_non_resizable_multi_window">false</bool>
<!-- Default for Settings.Secure.ACCESSIBILITY_BUTTON_MODE -->
<integer name="def_accessibility_button_mode">1</integer>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index b7560d2..c9c3db8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -73,6 +73,7 @@
Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER,
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
Settings.Global.USER_DISABLED_HDR_FORMATS,
- Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED
+ Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED,
+ Settings.Global.DEVICE_CONFIG_SYNC_DISABLED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 1cfdff8..cf54083 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -190,5 +190,7 @@
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
Settings.Secure.NOTIFICATION_BUBBLES,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
+ Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
+ Settings.Secure.LOCKSCREEN_SHOW_WALLET,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 8f7f1fa..5220a04 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -139,6 +139,7 @@
/* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT,
/* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
VALIDATORS.put(Global.DISABLE_WINDOW_BLURS, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.DEVICE_CONFIG_SYNC_DISABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 36f5dba..50fab4f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -146,6 +146,8 @@
VALIDATORS.put(Secure.QS_TILES, TILE_LIST_VALIDATOR);
VALIDATORS.put(Secure.CONTROLS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.POWER_MENU_LOCKED_SHOW_CONTENT, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.LOCKSCREEN_SHOW_CONTROLS, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.LOCKSCREEN_SHOW_WALLET, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_ALWAYS_ON, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_PICK_UP_GESTURE, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index df6ff73..00fd19c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -16,7 +16,11 @@
package com.android.providers.settings;
-import android.annotation.SystemApi;
+import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE;
+import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT;
+import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT;
+
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.AttributionSource;
import android.content.IContentProvider;
@@ -41,10 +45,7 @@
/**
* Receives shell commands from the command line related to device config flags, and dispatches them
* to the SettingsProvider.
- *
- * @hide
*/
-@SystemApi
public final class DeviceConfigService extends Binder {
final SettingsProvider mProvider;
@@ -62,18 +63,20 @@
final SettingsProvider mProvider;
enum CommandVerb {
- UNSPECIFIED,
GET,
PUT,
DELETE,
LIST,
RESET,
+ SET_SYNC_DISABLED_FOR_TESTS,
+ IS_SYNC_DISABLED_FOR_TESTS,
}
MyShellCommand(SettingsProvider provider) {
mProvider = provider;
}
+ @SuppressLint("AndroidFrameworkRequiresPermission")
@Override
public int onCommand(String cmd) {
if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) {
@@ -83,6 +86,7 @@
final PrintWriter perr = getErrPrintWriter();
boolean isValid = false;
+
CommandVerb verb;
if ("get".equalsIgnoreCase(cmd)) {
verb = CommandVerb.GET;
@@ -97,21 +101,33 @@
}
} else if ("reset".equalsIgnoreCase(cmd)) {
verb = CommandVerb.RESET;
+ } else if ("set_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
+ verb = CommandVerb.SET_SYNC_DISABLED_FOR_TESTS;
+ } else if ("is_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
+ verb = CommandVerb.IS_SYNC_DISABLED_FOR_TESTS;
+ if (peekNextArg() != null) {
+ perr.println("Bad arguments");
+ return -1;
+ }
+ isValid = true;
} else {
// invalid
perr.println("Invalid command: " + cmd);
return -1;
}
+ // Parse args for those commands that have them.
+ int disableSyncMode = -1;
int resetMode = -1;
boolean makeDefault = false;
String namespace = null;
String key = null;
String value = null;
- String arg = null;
+ String arg;
while ((arg = getNextArg()) != null) {
if (verb == CommandVerb.RESET) {
if (resetMode == -1) {
+ // RESET 1st arg (required)
if ("untrusted_defaults".equalsIgnoreCase(arg)) {
resetMode = Settings.RESET_MODE_UNTRUSTED_DEFAULTS;
} else if ("untrusted_clear".equalsIgnoreCase(arg)) {
@@ -127,6 +143,7 @@
isValid = true;
}
} else {
+ // RESET 2nd arg (optional)
namespace = arg;
if (peekNextArg() == null) {
isValid = true;
@@ -136,7 +153,26 @@
return -1;
}
}
+ } else if (verb == CommandVerb.SET_SYNC_DISABLED_FOR_TESTS) {
+ if (disableSyncMode == -1) {
+ // DISABLE_SYNC_FOR_TESTS 1st arg (required)
+ if ("none".equalsIgnoreCase(arg)) {
+ disableSyncMode = SYNC_DISABLED_MODE_NONE;
+ } else if ("persistent".equalsIgnoreCase(arg)) {
+ disableSyncMode = SYNC_DISABLED_MODE_PERSISTENT;
+ } else if ("until_reboot".equalsIgnoreCase(arg)) {
+ disableSyncMode = SYNC_DISABLED_MODE_UNTIL_REBOOT;
+ } else {
+ // invalid
+ perr.println("Invalid sync disabled mode: " + arg);
+ return -1;
+ }
+ if (peekNextArg() == null) {
+ isValid = true;
+ }
+ }
} else if (namespace == null) {
+ // GET, PUT, DELETE, LIST 1st arg
namespace = arg;
if (verb == CommandVerb.LIST) {
if (peekNextArg() == null) {
@@ -148,8 +184,10 @@
}
}
} else if (key == null) {
+ // GET, PUT, DELETE 2nd arg
key = arg;
if ((verb == CommandVerb.GET || verb == CommandVerb.DELETE)) {
+ // GET, DELETE only have 2 args
if (peekNextArg() == null) {
isValid = true;
} else {
@@ -159,11 +197,13 @@
}
}
} else if (value == null) {
+ // PUT 3rd arg (required)
value = arg;
if (verb == CommandVerb.PUT && peekNextArg() == null) {
isValid = true;
}
} else if ("default".equalsIgnoreCase(arg)) {
+ // PUT 4th arg (optional)
makeDefault = true;
if (verb == CommandVerb.PUT && peekNextArg() == null) {
isValid = true;
@@ -211,6 +251,12 @@
case RESET:
DeviceConfig.resetToDefaults(resetMode, namespace);
break;
+ case SET_SYNC_DISABLED_FOR_TESTS:
+ DeviceConfig.setSyncDisabled(disableSyncMode);
+ break;
+ case IS_SYNC_DISABLED_FOR_TESTS:
+ pout.println(DeviceConfig.isSyncDisabled());
+ break;
default:
perr.println("Unspecified command");
return -1;
@@ -241,6 +287,16 @@
+ "trusted_defaults}");
pw.println(" NAMESPACE limits which flags are reset if provided, otherwise all "
+ "flags are reset");
+ pw.println(" set_sync_disabled_for_tests SYNC_DISABLED_MODE");
+ pw.println(" Modifies bulk property setting behavior for tests. When in one of the"
+ + " disabled modes this ensures that config isn't overwritten.");
+ pw.println(" SYNC_DISABLED_MODE is one of:");
+ pw.println(" none: Sync is not disabled. A reboot may be required to restart"
+ + " syncing.");
+ pw.println(" persistent: Sync is disabled, this state will survive a reboot.");
+ pw.println(" until_reboot: Sync is disabled until the next reboot.");
+ pw.println(" is_sync_disabled_for_tests");
+ pw.println(" Prints 'true' if sync is disabled, 'false' otherwise.");
}
private boolean delete(IContentProvider provider, String namespace, String key) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 941f47f..bce576d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -19,6 +19,12 @@
import static android.os.Process.ROOT_UID;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
+import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE;
+import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT;
+import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT;
+import static android.provider.Settings.SET_ALL_RESULT_DISABLED;
+import static android.provider.Settings.SET_ALL_RESULT_FAILURE;
+import static android.provider.Settings.SET_ALL_RESULT_SUCCESS;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
@@ -34,8 +40,11 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.backup.BackupManager;
+import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -80,8 +89,10 @@
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.provider.Settings.Config.SyncDisabledMode;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
+import android.provider.Settings.SetAllResult;
import android.provider.settings.validators.SystemSettingsValidators;
import android.provider.settings.validators.Validator;
import android.text.TextUtils;
@@ -335,6 +346,9 @@
// We have to call in the package manager with no lock held,
private volatile IPackageManager mPackageManager;
+ @GuardedBy("mLock")
+ private boolean mSyncConfigDisabledUntilReboot;
+
public static int makeKey(int type, int userId) {
return SettingsState.makeKey(type, userId);
}
@@ -350,6 +364,9 @@
public static String keyToString(int key) {
return SettingsState.keyToString(key);
}
+ @ChangeId
+ @EnabledSince(targetSdkVersion=android.os.Build.VERSION_CODES.S)
+ private static final long ENFORCE_READ_PERMISSION_FOR_MULTI_SIM_DATA_CALL = 172670679L;
@Override
public boolean onCreate() {
@@ -440,11 +457,24 @@
String prefix = getSettingPrefix(args);
Map<String, String> flags = getSettingFlags(args);
Bundle result = new Bundle();
- result.putBoolean(Settings.KEY_CONFIG_SET_RETURN,
+ result.putInt(Settings.KEY_CONFIG_SET_ALL_RETURN,
setAllConfigSettings(prefix, flags));
return result;
}
+ case Settings.CALL_METHOD_SET_SYNC_DISABLED_CONFIG: {
+ final int mode = getSyncDisabledMode(args);
+ setSyncDisabledConfig(mode);
+ break;
+ }
+
+ case Settings.CALL_METHOD_IS_SYNC_DISABLED_CONFIG: {
+ Bundle result = new Bundle();
+ result.putBoolean(Settings.KEY_CONFIG_IS_SYNC_DISABLED_RETURN,
+ isSyncDisabledConfig());
+ return result;
+ }
+
case Settings.CALL_METHOD_RESET_CONFIG: {
final int mode = getResetModeEnforcingPermission(args);
String prefix = getSettingPrefix(args);
@@ -1099,7 +1129,8 @@
MUTATION_OPERATION_INSERT, 0);
}
- private boolean setAllConfigSettings(String prefix, Map<String, String> keyValues) {
+
+ private @SetAllResult int setAllConfigSettings(String prefix, Map<String, String> keyValues) {
if (DEBUG) {
Slog.v(LOG_TAG, "setAllConfigSettings for prefix: " + prefix);
}
@@ -1107,9 +1138,95 @@
enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
synchronized (mLock) {
+ if (isSyncDisabledConfigLocked()) {
+ return SET_ALL_RESULT_DISABLED;
+ }
final int key = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
- return mSettingsRegistry.setConfigSettingsLocked(key, prefix, keyValues,
+ boolean success = mSettingsRegistry.setConfigSettingsLocked(key, prefix, keyValues,
resolveCallingPackage());
+ return success ? SET_ALL_RESULT_SUCCESS : SET_ALL_RESULT_FAILURE;
+ }
+ }
+
+ private void setSyncDisabledConfig(@SyncDisabledMode int syncDisabledMode) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "setSyncDisabledConfig(" + syncDisabledMode + ")");
+ }
+
+ enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+
+ synchronized (mLock) {
+ setSyncDisabledConfigLocked(syncDisabledMode);
+ }
+ }
+
+ private boolean isSyncDisabledConfig() {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "isSyncDisabledConfig");
+ }
+
+ enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+
+ synchronized (mLock) {
+ return isSyncDisabledConfigLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void setSyncDisabledConfigLocked(@SyncDisabledMode int syncDisabledMode) {
+ boolean persistentValue;
+ boolean inMemoryValue;
+ if (syncDisabledMode == SYNC_DISABLED_MODE_NONE) {
+ persistentValue = false;
+ inMemoryValue = false;
+ } else if (syncDisabledMode == SYNC_DISABLED_MODE_PERSISTENT) {
+ persistentValue = true;
+ inMemoryValue = false;
+ } else if (syncDisabledMode == SYNC_DISABLED_MODE_UNTIL_REBOOT) {
+ persistentValue = false;
+ inMemoryValue = true;
+ } else {
+ throw new IllegalArgumentException(Integer.toString(syncDisabledMode));
+ }
+
+ mSyncConfigDisabledUntilReboot = inMemoryValue;
+
+ CallingIdentity callingIdentity = clearCallingIdentity();
+ try {
+ String globalSettingValue = persistentValue ? "1" : "0";
+ mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
+ UserHandle.USER_SYSTEM, Settings.Global.DEVICE_CONFIG_SYNC_DISABLED,
+ globalSettingValue, /*tag=*/null, /*makeDefault=*/false,
+ SettingsState.SYSTEM_PACKAGE_NAME, /*forceNotify=*/false,
+ /*criticalSettings=*/null, Settings.DEFAULT_OVERRIDEABLE_BY_RESTORE);
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean isSyncDisabledConfigLocked() {
+ // Check the values used for both SYNC_DISABLED_MODE_PERSISTENT and
+ // SYNC_DISABLED_MODE_UNTIL_REBOOT.
+
+ // The SYNC_DISABLED_MODE_UNTIL_REBOOT value is cheap to check first.
+ if (mSyncConfigDisabledUntilReboot) {
+ return true;
+ }
+
+ // Now check the global setting used to implement SYNC_DISABLED_MODE_PERSISTENT.
+ CallingIdentity callingIdentity = clearCallingIdentity();
+ try {
+ Setting settingLocked = mSettingsRegistry.getSettingLocked(
+ SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,
+ Global.DEVICE_CONFIG_SYNC_DISABLED);
+ if (settingLocked == null) {
+ return false;
+ }
+ String settingValue = settingLocked.getValue();
+ return settingValue != null && !"0".equals(settingValue);
+ } finally {
+ restoreCallingIdentity(callingIdentity);
}
}
@@ -1950,6 +2067,25 @@
// Skip checking readable annotations for test_only apps
checkReadableAnnotation(settingsType, settingName);
}
+ /**
+ * some settings need additional permission check, this is to have a matching security
+ * control from other API alternatives returning the same settings values.
+ * note, the permission enforcement should be based on app's targetSDKlevel to better handle
+ * app-compat.
+ */
+ switch (settingName) {
+ // missing READ_PRIVILEGED_PHONE_STATE permission protection
+ // see alternative API {@link SubscriptionManager#getPreferredDataSubscriptionId()
+ case Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION:
+ // app-compat handling, not break apps targeting on previous SDKs.
+ if (CompatChanges.isChangeEnabled(
+ ENFORCE_READ_PERMISSION_FOR_MULTI_SIM_DATA_CALL)) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "access global settings MULTI_SIM_DATA_CALL_SUBSCRIPTION");
+ }
+ break;
+ }
if (!ai.isInstantApp()) {
return;
}
@@ -2202,6 +2338,16 @@
return (args != null) && args.getBoolean(Settings.CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY);
}
+ private static int getSyncDisabledMode(Bundle args) {
+ final int mode = (args != null)
+ ? args.getInt(Settings.CALL_METHOD_SYNC_DISABLED_MODE_KEY) : -1;
+ if (mode == SYNC_DISABLED_MODE_NONE || mode == SYNC_DISABLED_MODE_UNTIL_REBOOT
+ || mode == SYNC_DISABLED_MODE_PERSISTENT) {
+ return mode;
+ }
+ throw new IllegalArgumentException("Invalid sync disabled mode: " + mode);
+ }
+
private static int getResetModeEnforcingPermission(Bundle args) {
final int mode = (args != null) ? args.getInt(Settings.CALL_METHOD_RESET_MODE_KEY) : 0;
switch (mode) {
@@ -3401,7 +3547,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 202;
+ private static final int SETTINGS_VERSION = 203;
private final int mUserId;
@@ -4919,6 +5065,15 @@
String.valueOf(defAccessibilityButtonMode), /* tag= */
null, /* makeDefault= */ true,
SettingsState.SYSTEM_PACKAGE_NAME);
+
+ if (hasValueInA11yButtonTargets(secureSettings)) {
+ secureSettings.insertSettingLocked(
+ Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
+ /* enabled */ "1",
+ /* tag= */ null,
+ /* makeDefault= */ false,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
}
}
@@ -4975,6 +5130,27 @@
currentVersion = 202;
}
+ if (currentVersion == 202) {
+ // Version 202: Power menu has been removed, and the privacy setting
+ // has been split into two for wallet and controls
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting showLockedContent = secureSettings.getSettingLocked(
+ Secure.POWER_MENU_LOCKED_SHOW_CONTENT);
+ if (!showLockedContent.isNull()) {
+ String currentValue = showLockedContent.getValue();
+
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Secure.LOCKSCREEN_SHOW_CONTROLS,
+ currentValue, null /* tag */, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Secure.LOCKSCREEN_SHOW_WALLET,
+ currentValue, null /* tag */, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 203;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
@@ -5145,13 +5321,21 @@
}
private boolean isAccessibilityButtonInNavigationBarOn(SettingsState secureSettings) {
- final boolean hasValueInA11yBtnTargets = !TextUtils.isEmpty(
- secureSettings.getSettingLocked(
- Secure.ACCESSIBILITY_BUTTON_TARGETS).getValue());
+ return hasValueInA11yButtonTargets(secureSettings) && !isGestureNavigateEnabled();
+ }
+
+ private boolean isGestureNavigateEnabled() {
final int navigationMode = getContext().getResources().getInteger(
com.android.internal.R.integer.config_navBarInteractionMode);
+ return navigationMode == NAV_BAR_MODE_GESTURAL;
+ }
- return hasValueInA11yBtnTargets && (navigationMode != NAV_BAR_MODE_GESTURAL);
+ private boolean hasValueInA11yButtonTargets(SettingsState secureSettings) {
+ final Setting a11yButtonTargetsSettings =
+ secureSettings.getSettingLocked(Secure.ACCESSIBILITY_BUTTON_TARGETS);
+
+ return !a11yButtonTargetsSettings.isNull()
+ && !TextUtils.isEmpty(a11yButtonTargetsSettings.getValue());
}
}
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 22e38f4..3a82434 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -752,6 +752,7 @@
Settings.Secure.SUPPRESS_DOZE,
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
Settings.Secure.UI_TRANSLATION_ENABLED);
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1d321f2..4bf4094 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -555,6 +555,9 @@
<!-- Permission required for CTS test - CtsAlarmManagerTestCases -->
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+ <!-- Permission required for CTS test - GlobalSearchSessionPlatformCtsTests -->
+ <uses-permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/res/values-te/strings.xml b/packages/Shell/res/values-te/strings.xml
index 6050c1f..989b53e 100644
--- a/packages/Shell/res/values-te/strings.xml
+++ b/packages/Shell/res/values-te/strings.xml
@@ -43,5 +43,5 @@
<string name="bugreport_info_title" msgid="2306030793918239804">"బగ్ శీర్షిక"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"బగ్ సారాంశం"</string>
<string name="save" msgid="4781509040564835759">"సేవ్ చేయి"</string>
- <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"బగ్ నివేదిక భాగస్వామ్యం చేయండి"</string>
+ <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"బగ్ నివేదిక షేర్ చేయండి"</string>
</resources>
diff --git a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
index 2191d93..ecd1369 100644
--- a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
+++ b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
@@ -29,6 +29,7 @@
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
+import android.view.WindowManager;
import android.widget.CheckBox;
import com.android.internal.app.AlertActivity;
@@ -47,6 +48,10 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ // Don't allow overlay windows.
+ getWindow().addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
mSendIntent = getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
// We need to touch the extras to unpack them so they get migrated to
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
index 0a052df..044b5ed 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
@@ -16,6 +16,7 @@
package com.android.systemui.animation;
+import android.util.MathUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.BounceInterpolator;
@@ -72,6 +73,27 @@
new PathInterpolator(0.9f, 0f, 0.7f, 1f);
/**
+ * Calculate the amount of overshoot using an exponential falloff function with desired
+ * properties, where the overshoot smoothly transitions at the 1.0f boundary into the
+ * overshoot, retaining its acceleration.
+ *
+ * @param progress a progress value going from 0 to 1
+ * @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max
+ * value of the overall progress will be at 1.1.
+ * @param overshootStart the point in (0,1] where the result should reach 1
+ * @return the interpolated overshoot
+ */
+ public static float getOvershootInterpolation(float progress, float overshootAmount,
+ float overshootStart) {
+ if (overshootAmount == 0.0f || overshootStart == 0.0f) {
+ throw new IllegalArgumentException("Invalid values for overshoot");
+ }
+ float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart;
+ return MathUtils.max(0.0f,
+ (float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f));
+ }
+
+ /**
* Interpolate alpha for notifications background scrim during shade expansion.
* @param fraction Shade expansion fraction
*/
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 32def03..b3c4374 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -19,6 +19,7 @@
import android.app.PendingIntent;
import android.app.smartspace.SmartspaceAction;
import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
@@ -45,6 +46,18 @@
/** Unregister a listener. */
void unregisterListener(SmartspaceTargetListener listener);
+ /** Register a SmartspaceEventNotifier. */
+ default void registerSmartspaceEventNotifier(SmartspaceEventNotifier notifier) {}
+
+ /** Push a SmartspaceTargetEvent to the SmartspaceEventNotifier. */
+ default void notifySmartspaceEvent(SmartspaceTargetEvent event) {}
+
+ /** Allows for notifying the SmartspaceSession of SmartspaceTargetEvents. */
+ interface SmartspaceEventNotifier {
+ /** Pushes a given SmartspaceTargetEvent to the SmartspaceSession. */
+ void notifySmartspaceEvent(SmartspaceTargetEvent event);
+ }
+
/**
* Create a view to be shown within the parent. Do not add the view, as the parent
* will be responsible for correctly setting the LayoutParams
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 4d4c909..98ef9e2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -33,7 +33,7 @@
String ACTION = "com.android.systemui.action.PLUGIN_QS";
- int VERSION = 8;
+ int VERSION = 9;
String TAG = "QS";
@@ -50,8 +50,13 @@
void setListening(boolean listening);
boolean isShowingDetail();
void closeDetail();
- default void setShowCollapsedOnKeyguard(boolean showCollapsedOnKeyguard) {}
- void animateHeaderSlidingIn(long delay);
+
+ /**
+ * Set that we're currently pulse expanding
+ *
+ * @param pulseExpanding if we're currently expanding during pulsing
+ */
+ default void setPulseExpanding(boolean pulseExpanding) {}
void animateHeaderSlidingOut();
void setQsExpansion(float qsExpansionFraction, float headerTranslation);
void setHeaderListening(boolean listening);
@@ -79,10 +84,23 @@
void setTranslateWhileExpanding(boolean shouldTranslate);
/**
+ * Set the amount of pixels we have currently dragged down if we're transitioning to the full
+ * shade. 0.0f means we're not transitioning yet.
+ */
+ default void setTransitionToFullShadeAmount(float pxAmount, boolean animated) {}
+
+ /**
* A rounded corner clipping that makes QS feel as if it were behind everything.
*/
void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible);
+ /**
+ * @return if quick settings is fully collapsed currently
+ */
+ default boolean isFullyCollapsed() {
+ return true;
+ }
+
@ProvidesInterface(version = HeightListener.VERSION)
interface HeightListener {
int VERSION = 1;
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 7c5459c1..35eebac 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -81,5 +81,6 @@
android:layout_height="@dimen/notification_shelf_height"
android:layout_below="@id/keyguard_status_area"
android:paddingStart="@dimen/below_clock_padding_start_extra"
+ android:visibility="invisible"
/>
</com.android.keyguard.KeyguardClockSwitch>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
index 0ee1b69..8bb7877 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
@@ -26,6 +26,7 @@
android:orientation="vertical"
android:gravity="center"
android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="12dp"
android:layout_alignParentBottom="true">
<com.android.keyguard.CarrierText
@@ -44,7 +45,6 @@
android:id="@+id/emergency_call_button"
android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_marginBottom="12dp"
android:text="@*android:string/lockscreen_emergency_call"
style="@style/Keyguard.TextView.EmergencyButton" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index c64afd3..a957df6 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -192,6 +192,7 @@
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+ android:layout_marginBottom="12dp"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardPINView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index 297c20e..3e34e4b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -57,7 +57,7 @@
android:id="@+id/row0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="16dp"
+ android:paddingBottom="4dp"
>
<com.android.keyguard.PasswordTextView
android:id="@+id/simPinEntry"
@@ -202,6 +202,7 @@
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+ android:layout_marginBottom="2dp"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardSimPinView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 388919e..d5510e9 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -58,7 +58,7 @@
android:id="@+id/row0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="16dp"
+ android:paddingBottom="4dp"
>
<com.android.keyguard.PasswordTextView
android:id="@+id/pukEntry"
@@ -204,5 +204,6 @@
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+ android:layout_marginBottom="2dp"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardSimPukView>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 9f00e668..83a0a32 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -24,7 +24,7 @@
<string name="keyguard_password_enter_pin_code" msgid="8582296866585566671">"Idatzi PIN kodea"</string>
<string name="keyguard_password_enter_puk_code" msgid="3813154965969758868">"Idatzi SIM txartelaren PUK kodea eta PIN kode berria"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="3529260761374385243">"SIM txartelaren PUK kodea"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="2304037870481240781">"SIM txartelaren PIN kode berria"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="2304037870481240781">"SIM txartelaren PIN berria"</string>
<string name="keyguard_password_entry_touch_hint" msgid="6180028658339706333"><font size="17">"Pasahitza idazteko, sakatu hau"</font></string>
<string name="keyguard_password_enter_password_code" msgid="7393393239623946777">"Idatzi desblokeatzeko pasahitza"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="3692259677395250509">"Idatzi desblokeatzeko PIN kodea"</string>
@@ -63,7 +63,7 @@
<string name="kg_forgot_pattern_button_text" msgid="3304688032024541260">"Eredua ahaztu zaizu"</string>
<string name="kg_wrong_pattern" msgid="5907301342430102842">"Eredua ez da zuzena"</string>
<string name="kg_wrong_password" msgid="4143127991071670512">"Pasahitza ez da zuzena"</string>
- <string name="kg_wrong_pin" msgid="4160978845968732624">"PIN kode hori ez da zuzena"</string>
+ <string name="kg_wrong_pin" msgid="4160978845968732624">"PIN hori ez da zuzena"</string>
<plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="991400408675793914">
<item quantity="other">Saiatu berriro <xliff:g id="NUMBER">%d</xliff:g> segundo igarotakoan.</item>
<item quantity="one">Saiatu berriro segundo bat igarotakoan.</item>
diff --git a/packages/SystemUI/res/color/media_player_solid_button_bg.xml b/packages/SystemUI/res/color/media_player_solid_button_bg.xml
new file mode 100644
index 0000000..96685ab
--- /dev/null
+++ b/packages/SystemUI/res/color/media_player_solid_button_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentTertiary"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.xml b/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.xml
new file mode 100644
index 0000000..46e7dcc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorAccentPrimary" />
+ <corners android:radius="@dimen/accessibility_floating_tooltip_text_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
index ceef9f9..4d9188c 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
@@ -34,7 +34,7 @@
android:right="@dimen/rounded_slider_icon_inset">
<com.android.systemui.util.AlphaTintDrawableWrapper
android:drawable="@drawable/ic_brightness"
- android:tint="?android:attr/colorBackground"
- android:alpha="0.8"/>
+ android:tint="?android:attr/textColorPrimaryInverse"
+ />
</item>
</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_conversation_icon.xml b/packages/SystemUI/res/drawable/ic_conversation_icon.xml
new file mode 100644
index 0000000..0e3533b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_conversation_icon.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:pathData="M24,24m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
+ android:fillColor="#81C995"/>
+ <path
+ android:pathData="M27,34C23.134,34 20,30.866 20,27C20,23.134 23.134,20 27,20C30.866,20 34,23.134 34,27C34,28.4872 33.5362,29.8662 32.7453,31"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#3C4043"/>
+ <path
+ android:pathData="M35,33l-8,0l-0,2l8,0z"
+ android:fillColor="#3C4043"/>
+ <path
+ android:pathData="M21,21m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"
+ android:fillColor="#81C995"/>
+ <path
+ android:pathData="M16,25h5v2h-5z"
+ android:fillColor="#81C995"/>
+ <path
+ android:pathData="M21,28C24.866,28 28,24.866 28,21C28,17.134 24.866,14 21,14C17.134,14 14,17.134 14,21C14,22.4872 14.4638,23.8662 15.2547,25"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#ffffff"/>
+ <path
+ android:pathData="M13,27h8v2h-8z"
+ android:fillColor="#ffffff"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_corp_badge_off.xml b/packages/SystemUI/res/drawable/ic_corp_badge_off.xml
new file mode 100644
index 0000000..a441fb2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_corp_badge_off.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20.0"
+ android:viewportHeight="20.0">
+ <path
+ android:pathData="M10,10m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
+ android:fillColor="#607D8B"/>
+ <path
+ android:pathData="M16.42,15.68l-0.85,-0.85L7.21,6.47L4.9,4.16L4.16,4.9l1.57,1.57H5.36c-0.65,0 -1.16,0.52 -1.16,1.17L4.2,14.05c0,0.65 0.52,1.17 1.17,1.17h9.12l1.2,1.2L16.42,15.68zM15.83,7.64c0.03,-0.65 -0.49,-1.17 -1.14,-1.14h-2.33V5.3c0,-0.65 -0.52,-1.17 -1.17,-1.14H8.86C8.22,4.14 7.7,4.66 7.7,5.3v0.19l8.14,8.17V7.64zM11.2,6.5H8.83V5.3h2.36V6.5z"
+ android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_move_magnification.xml b/packages/SystemUI/res/drawable/ic_move_magnification.xml
index 642fc31..96db365 100644
--- a/packages/SystemUI/res/drawable/ic_move_magnification.xml
+++ b/packages/SystemUI/res/drawable/ic_move_magnification.xml
@@ -19,7 +19,7 @@
<item>
<shape android:shape="rectangle">
<solid
- android:color="@color/magnification_switch_button_color" />
+ android:color="@color/magnification_drag_handle_color" />
<size
android:height="@dimen/magnification_drag_view_size"
android:width="@dimen/magnification_drag_view_size"/>
@@ -28,8 +28,8 @@
<item
android:gravity="center">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="30dp"
+ android:height="30dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
index 5bb7390..36a1224 100644
--- a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
+++ b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
@@ -24,10 +24,11 @@
</shape>
</item>
- <item>
+ <item
+ android:gravity="center">
<vector
- android:width="24dp"
- android:height="24dp"
+ android:width="36dp"
+ android:height="36dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group>
diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
index 2d8a5d9..f41f784 100644
--- a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
+++ b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
@@ -24,10 +24,11 @@
</shape>
</item>
- <item>
+ <item
+ android:gravity="center">
<vector
- android:width="24dp"
- android:height="24dp"
+ android:width="36dp"
+ android:height="36dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
diff --git a/packages/SystemUI/res/drawable/people_tile_empty_background.xml b/packages/SystemUI/res/drawable/people_tile_empty_background.xml
new file mode 100644
index 0000000..2dac740
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_empty_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:radius="@dimen/people_space_widget_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_status_scrim.xml b/packages/SystemUI/res/drawable/people_tile_status_scrim.xml
new file mode 100644
index 0000000..cf16f1c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_status_scrim.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <gradient
+ android:type="linear"
+ android:angle="90"
+ android:endColor="@android:color/transparent"
+ android:startColor="?androidprv:attr/colorSurfaceHeader" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml b/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml
new file mode 100644
index 0000000..e0c111d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <solid android:color="?androidprv:attr/colorSurfaceVariant" />
+ <corners android:radius="@dimen/people_space_widget_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
index 77b9871..a45769f 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
@@ -34,7 +34,7 @@
</item>
<item>
<shape android:shape="rectangle">
- <stroke android:width="1dp" android:color="@color/qs_footer_action_border"/>
+ <stroke android:width="1dp" android:color="?android:attr/colorBackground"/>
<corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/wallet_action_button_bg.xml b/packages/SystemUI/res/drawable/wallet_action_button_bg.xml
index bf7625f..925ab67 100644
--- a/packages/SystemUI/res/drawable/wallet_action_button_bg.xml
+++ b/packages/SystemUI/res/drawable/wallet_action_button_bg.xml
@@ -14,13 +14,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item>
<shape android:shape="rectangle">
<stroke
android:width="1dp"
- android:color="@color/GM2_blue_600"/>
- <solid android:color="@color/GM2_blue_600"/>
+ android:color="?androidprv:attr/colorAccentPrimary"/>
+ <solid android:color="?androidprv:attr/colorAccentPrimary"/>
<corners android:radius="24dp"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml b/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml
index f52889a..b203228 100644
--- a/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml
+++ b/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?android:attr/colorControlHighlight">
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="#DADCE0" />
@@ -26,7 +26,7 @@
<shape>
<stroke
android:width="1dp"
- android:color="@color/GM2_grey_900" />
+ android:color="?androidprv:attr/colorAccentPrimary" />
<corners android:radius="@dimen/wallet_empty_state_corner_radius" />
</shape>
</item>
diff --git a/packages/SystemUI/res/layout/accessibility_floating_menu_tooltip.xml b/packages/SystemUI/res/layout/accessibility_floating_menu_tooltip.xml
new file mode 100644
index 0000000..5e7b7e1
--- /dev/null
+++ b/packages/SystemUI/res/layout/accessibility_floating_menu_tooltip.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tooltip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <View
+ android:id="@+id/arrow_left"
+ android:layout_width="@dimen/accessibility_floating_tooltip_arrow_width"
+ android:layout_height="@dimen/accessibility_floating_tooltip_arrow_height"
+ android:layout_marginRight="@dimen/accessibility_floating_tooltip_arrow_margin"
+ android:visibility="gone"
+ android:layout_gravity="center_vertical"/>
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:padding="@dimen/accessibility_floating_tooltip_padding"
+ android:background="@drawable/accessibility_floating_tooltip_background"
+ android:textColor="@android:color/black"
+ android:textColorLink="@android:color/black"
+ android:text="@string/accessibility_floating_button_migration_tooltip"
+ android:textSize="@dimen/accessibility_floating_tooltip_font_size"/>
+
+ <View
+ android:id="@+id/arrow_right"
+ android:layout_width="@dimen/accessibility_floating_tooltip_arrow_width"
+ android:layout_height="@dimen/accessibility_floating_tooltip_arrow_height"
+ android:layout_marginLeft="@dimen/accessibility_floating_tooltip_arrow_margin"
+ android:visibility="gone"
+ android:layout_gravity="center_vertical"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/controls_onboarding.xml b/packages/SystemUI/res/layout/controls_onboarding.xml
index 577a3b4..d234c32 100644
--- a/packages/SystemUI/res/layout/controls_onboarding.xml
+++ b/packages/SystemUI/res/layout/controls_onboarding.xml
@@ -46,7 +46,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
- android:textColor="?attr/wallpaperTextColor"
+ android:textColor="?android:attr/colorPrimary"
android:textSize="16sp"/>
<ImageView
android:id="@+id/dismiss"
@@ -58,7 +58,7 @@
android:layout_marginEnd="2dp"
android:alpha="0.7"
android:src="@drawable/ic_close_white"
- android:tint="?attr/wallpaperTextColor"
+ android:tint="?android:attr/colorPrimary"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/accessibility_desc_close"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 69d73c1..0dc1473 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -21,8 +21,7 @@
android:id="@+id/keyguard_bottom_area"
android:layout_height="match_parent"
android:layout_width="match_parent"
- android:outlineProvider="none"
- android:elevation="5dp" > <!-- Put it above the status bar header -->
+ android:outlineProvider="none" > <!-- Put it above the status bar header -->
<LinearLayout
android:id="@+id/keyguard_indication_area"
@@ -58,12 +57,6 @@
</LinearLayout>
- <FrameLayout
- android:id="@+id/preview_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- </FrameLayout>
-
<com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/camera_button"
android:layout_height="@dimen/keyguard_affordance_height"
@@ -95,6 +88,7 @@
android:background="@drawable/wallet_lockscreen_bg"
android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset"
android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
+ android:contentDescription="@string/accessibility_wallet_button"
android:visibility="gone" />
<FrameLayout
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index ceba4e3..b99c86f 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -126,8 +126,8 @@
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/save"
app:layout_constraintStart_toStartOf="parent"
- android:scaleType="matrix"
- android:visibility="gone"
+ android:scaleType="centerCrop"
+ android:visibility="invisible"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index 8dbbd4a..e999872 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -145,6 +145,7 @@
android:layout_width="@dimen/qs_seamless_icon_size"
android:layout_height="@dimen/qs_seamless_icon_size"
android:layout_gravity="center"
+ android:tint="?android:attr/textColorPrimary"
android:src="@*android:drawable/ic_media_seamless" />
<TextView
android:id="@+id/media_seamless_text"
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 2ec4e73..9b2f0ac 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -24,7 +24,7 @@
android:clipChildren="true"
android:clipToPadding="true"
android:orientation="vertical"
- android:paddingStart="12dp">
+ android:paddingStart="@dimen/notification_shade_content_margin_horizontal">
<!-- Package Info -->
<LinearLayout
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml
index a146547..f8175d4 100644
--- a/packages/SystemUI/res/layout/ongoing_call_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml
@@ -13,36 +13,45 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<LinearLayout
+<!-- Have the wrapper frame layout match the parent height so that we get a larger touch area for
+ the chip. -->
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ongoing_call_chip"
android:layout_width="wrap_content"
- android:layout_height="@dimen/ongoing_appops_chip_height"
+ android:layout_height="match_parent"
android:layout_gravity="center_vertical|start"
- android:gravity="center_vertical"
- android:background="@drawable/ongoing_call_chip_bg"
- android:paddingStart="@dimen/ongoing_call_chip_side_padding"
- android:paddingEnd="@dimen/ongoing_call_chip_side_padding"
>
-
- <ImageView
- android:src="@*android:drawable/ic_phone"
- android:layout_width="@dimen/ongoing_call_chip_icon_size"
- android:layout_height="@dimen/ongoing_call_chip_icon_size"
- android:tint="?android:attr/colorPrimary"
- />
-
- <!-- TODO(b/183229367): The text in this view isn't quite centered within the chip. -->
- <com.android.systemui.statusbar.phone.ongoingcall.OngoingCallChronometer
- android:id="@+id/ongoing_call_chip_time"
+ <LinearLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:gravity="center|start"
- android:paddingStart="@dimen/ongoing_call_chip_icon_text_padding"
- android:textAppearance="@android:style/TextAppearance.Material.Small"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textColor="?android:attr/colorPrimary"
- />
+ android:layout_height="@dimen/ongoing_appops_chip_height"
+ android:layout_gravity="center_vertical"
+ android:gravity="center"
+ android:background="@drawable/ongoing_call_chip_bg"
+ android:paddingStart="@dimen/ongoing_call_chip_side_padding"
+ android:paddingEnd="@dimen/ongoing_call_chip_side_padding"
+ android:contentDescription="@string/ongoing_phone_call_content_description"
+ android:minWidth="@dimen/min_clickable_item_size"
+ >
-</LinearLayout>
+ <ImageView
+ android:src="@*android:drawable/ic_phone"
+ android:layout_width="@dimen/ongoing_call_chip_icon_size"
+ android:layout_height="@dimen/ongoing_call_chip_icon_size"
+ android:tint="?android:attr/colorPrimary"
+ />
+
+ <com.android.systemui.statusbar.phone.ongoingcall.OngoingCallChronometer
+ android:id="@+id/ongoing_call_chip_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:gravity="center|start"
+ android:paddingStart="@dimen/ongoing_call_chip_icon_text_padding"
+ android:textAppearance="@android:style/TextAppearance.Material.Small"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:textColor="?android:attr/colorPrimary"
+ />
+
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/people_status_scrim_layout.xml b/packages/SystemUI/res/layout/people_status_scrim_layout.xml
new file mode 100644
index 0000000..9808f74
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_status_scrim_layout.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+~ Copyright (C) 2021 The Android Open Source Project
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scrim_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/status_icon"
+ android:scaleType="centerCrop"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:weightSum="1"
+ android:orientation="vertical">
+ <ImageView
+ android:layout_weight=".2"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ <ImageView
+ android:background="@drawable/people_tile_status_scrim"
+ android:layout_weight=".8"
+ android:scaleType="centerCrop"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:weightSum="1"
+ android:orientation="vertical">
+ <ImageView
+ android:layout_weight=".66"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ <ImageView
+ android:background="@drawable/people_tile_status_scrim"
+ android:layout_weight=".33"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ </LinearLayout>
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/people_tile_empty_layout.xml b/packages/SystemUI/res/layout/people_tile_empty_layout.xml
index 7d3b919..8e9ebc6 100644
--- a/packages/SystemUI/res/layout/people_tile_empty_layout.xml
+++ b/packages/SystemUI/res/layout/people_tile_empty_layout.xml
@@ -14,16 +14,17 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
- android:background="@drawable/people_space_tile_view_card"
+ android:background="@drawable/people_tile_empty_background"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
- android:id="@+id/item"
+ android:id="@+id/icon"
+ android:src="@drawable/ic_conversation_icon"
android:gravity="center"
android:layout_gravity="center"
- android:padding="8dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
index 6f8de3b..b77670e 100644
--- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
@@ -1,145 +1,159 @@
<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@android:style/Theme.DeviceDefault.DayNight"
- android:id="@+id/item"
+~ Copyright (C) 2021 The Android Open Source Project
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/people_space_tile_view_card"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:id="@+id/item"
+ android:clipToOutline="true"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:layout_gravity="center"
- android:padding="16dp"
- android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
- <RelativeLayout
+ <include layout="@layout/people_status_scrim_layout" />
+
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="start|top">
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:padding="16dp"
+ android:orientation="vertical">
- <LinearLayout
- android:layout_width="wrap_content"
+ <RelativeLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:gravity="start|top"
- android:orientation="horizontal">
+ android:gravity="start|top">
- <ImageView
- android:id="@+id/person_icon"
- android:layout_marginStart="-2dp"
- android:layout_marginTop="-2dp"
+ <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1" />
+ android:layout_alignParentStart="true"
+ android:gravity="start|top"
+ android:orientation="horizontal">
- <ImageView
- android:id="@+id/availability"
- android:layout_marginStart="-2dp"
- android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="@drawable/circle_green_10dp" />
- </LinearLayout>
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_marginStart="-2dp"
+ android:layout_marginTop="-2dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
- <TextView
- android:id="@+id/messages_count"
- android:layout_alignParentEnd="true"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:background="@drawable/people_space_messages_count_background"
- android:textSize="14sp"
- android:maxLines="1"
- android:ellipsize="end"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- />
- </RelativeLayout>
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <include layout="@layout/people_tile_punctuation_background_large" />
- <include layout="@layout/people_tile_emoji_background_large" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView
- android:layout_gravity="center"
- android:id="@+id/name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="12dp"
- android:gravity="start"
- android:singleLine="true"
- android:ellipsize="end"
- android:text="@string/empty_user_name"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="4dp"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/predefined_icon"
- android:tint="?android:attr/colorAccent"
- android:gravity="start|center_vertical"
- android:paddingEnd="6dp"
- android:layout_width="24dp"
- android:layout_height="18dp" />
+ <ImageView
+ android:id="@+id/availability"
+ android:layout_marginStart="-2dp"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="@drawable/circle_green_10dp" />
+ </LinearLayout>
<TextView
- android:layout_gravity="center"
- android:id="@+id/subtext"
- android:gravity="center_vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:singleLine="true"
- android:text="@string/empty_user_name"
+ android:id="@+id/messages_count"
+ android:layout_alignParentEnd="true"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="12sp" />
- </LinearLayout>
+ android:textColor="?android:attr/textColorPrimary"
+ android:background="@drawable/people_space_messages_count_background"
+ android:textSize="14sp"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+ </RelativeLayout>
- <ImageView
- android:id="@+id/image"
+ <RelativeLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/people_space_content_background"
- android:gravity="center"
- android:scaleType="centerCrop" />
+ android:layout_height="match_parent">
- <TextView
- android:layout_gravity="center"
- android:id="@+id/text_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="2"
- android:singleLine="false"
- android:text="@string/empty_status"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="12sp" />
- </LinearLayout>
- </RelativeLayout>
-</LinearLayout>
+ <include layout="@layout/people_tile_punctuation_background_large" />
+
+ <include layout="@layout/people_tile_emoji_background_large" />
+
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="12dp"
+ android:gravity="start"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp" />
+
+ <LinearLayout
+ android:id="@+id/status_icon_and_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="4dp"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/predefined_icon"
+ android:tint="?android:attr/colorAccent"
+ android:gravity="start|center_vertical"
+ android:paddingEnd="6dp"
+ android:layout_width="24dp"
+ android:layout_height="18dp" />
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/subtext"
+ android:gravity="center_vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="12sp" />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/people_space_content_background"
+ android:gravity="center"
+ android:scaleType="centerCrop" />
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/text_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:singleLine="false"
+ android:text="@string/empty_status"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/content_text_size_for_large" />
+ </LinearLayout>
+ </RelativeLayout>
+ </LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index a8c15ab..8df0bf8 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -17,18 +17,20 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:id="@+id/item"
+ android:background="@drawable/people_space_tile_view_card"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
- android:background="@drawable/people_space_tile_view_card"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/people_tile_punctuation_background_medium" />
- <include layout="@layout/people_tile_emoji_background_medium" />
+ <include layout="@layout/people_tile_punctuation_background_medium" />
+ <include layout="@layout/people_status_scrim_layout" />
<LinearLayout
- android:id="@+id/item"
+ android:id="@+id/content"
android:orientation="vertical"
android:layout_gravity="center"
android:padding="8dp"
@@ -89,7 +91,7 @@
android:text="@string/empty_status"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="12sp"
+ android:textSize="@dimen/content_text_size_for_medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
diff --git a/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml b/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
new file mode 100644
index 0000000..b151c60
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:background="@drawable/people_tile_suppressed_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml b/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
new file mode 100644
index 0000000..25ab5a6
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
@@ -0,0 +1,39 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/item"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:background="@drawable/people_tile_suppressed_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
+
+ <ImageView android:id="@+id/work_widget_badge_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:layout_marginBottom="12dp"
+ android:layout_marginRight="12dp"
+ android:src="@drawable/ic_corp_badge_off"
+ android:clickable="false" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_carrier.xml b/packages/SystemUI/res/layout/qs_carrier.xml
index c521dc2..d4594d1 100644
--- a/packages/SystemUI/res/layout/qs_carrier.xml
+++ b/packages/SystemUI/res/layout/qs_carrier.xml
@@ -33,7 +33,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:textAppearance="@style/TextAppearance.QS.Status"
+ android:textAppearance="@style/TextAppearance.QS.Status.Carriers"
android:textDirection="locale"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/qs_carrier_group.xml
index 810c959..6e13ab9 100644
--- a/packages/SystemUI/res/layout/qs_carrier_group.xml
+++ b/packages/SystemUI/res/layout/qs_carrier_group.xml
@@ -32,7 +32,7 @@
android:minWidth="48dp"
android:minHeight="48dp"
android:gravity="center_vertical"
- android:textAppearance="@style/TextAppearance.QS.Status.NoCarrierText"
+ android:textAppearance="@style/TextAppearance.QS.Status.Carriers.NoCarrierText"
android:textDirection="locale"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
diff --git a/packages/SystemUI/res/layout/qs_customize_divider.xml b/packages/SystemUI/res/layout/qs_customize_divider.xml
index 035f493..b4e3010 100644
--- a/packages/SystemUI/res/layout/qs_customize_divider.xml
+++ b/packages/SystemUI/res/layout/qs_customize_divider.xml
@@ -24,5 +24,5 @@
android:gravity="center"
android:paddingTop="24dp"
android:paddingBottom="24dp"
- android:textAppearance="@style/TextAppearance.QSEdit.Headers"
+ android:textAppearance="@style/TextAppearance.QSEdit"
android:text="@string/drag_to_add_tiles" />
diff --git a/packages/SystemUI/res/layout/qs_customize_header.xml b/packages/SystemUI/res/layout/qs_customize_header.xml
index 481561d..626d53a 100644
--- a/packages/SystemUI/res/layout/qs_customize_header.xml
+++ b/packages/SystemUI/res/layout/qs_customize_header.xml
@@ -22,5 +22,5 @@
android:layout_height="wrap_content"
android:gravity="center"
android:minHeight="@dimen/qs_customize_header_min_height"
- android:textAppearance="@style/TextAppearance.QSEdit.Headers"
+ android:textAppearance="@style/TextAppearance.QSEdit"
android:text="@string/drag_to_rearrange_tiles" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index f056402..59e1a75 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -23,7 +23,6 @@
android:clickable="true"
android:orientation="vertical"
android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
- android:layout_marginBottom="@dimen/qs_container_bottom_padding"
android:paddingBottom="8dp"
android:visibility="invisible"
android:elevation="4dp"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 266ecef..0a9feb8 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -51,7 +51,7 @@
android:focusable="true"
android:gravity="center_vertical"
android:singleLine="true"
- android:textAppearance="@style/TextAppearance.QS.Build"
+ android:textAppearance="@style/TextAppearance.QS.Status.Build"
android:visibility="gone" />
<com.android.systemui.qs.PageIndicator
@@ -87,7 +87,7 @@
android:focusable="true"
android:padding="@dimen/qs_footer_icon_padding"
android:src="@*android:drawable/ic_mode_edit"
- android:tint="?android:attr/colorForeground" />
+ android:tint="?android:attr/textColorPrimary" />
<com.android.systemui.statusbar.phone.MultiUserSwitch
android:id="@+id/multi_user_switch"
@@ -126,7 +126,7 @@
android:padding="@dimen/qs_footer_icon_padding"
android:scaleType="centerInside"
android:src="@drawable/ic_settings"
- android:tint="?android:attr/colorForeground" />
+ android:tint="?android:attr/textColorPrimary" />
<com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/tuner_icon"
@@ -152,7 +152,7 @@
android:padding="@dimen/qs_footer_icon_padding"
android:src="@*android:drawable/ic_lock_power_off"
android:contentDescription="@string/accessibility_quick_settings_power_menu"
- android:tint="?android:attr/colorForeground" />
+ android:tint="?android:attr/textColorPrimary" />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 4607e5f..4c6418a 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -17,7 +17,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:clipToPadding="false"
android:clipChildren="false" >
@@ -25,7 +25,6 @@
android:id="@+id/expanded_qs_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="@dimen/qs_container_bottom_padding"
android:elevation="4dp"
android:importantForAccessibility="no"
android:scrollbars="none"
diff --git a/packages/SystemUI/res/layout/qs_tile_side_icon.xml b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
index 9f9af9d..1ae0a1c 100644
--- a/packages/SystemUI/res/layout/qs_tile_side_icon.xml
+++ b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
@@ -35,6 +35,7 @@
android:layout_width="@dimen/qs_icon_size"
android:layout_height="@dimen/qs_icon_size"
android:src="@*android:drawable/ic_chevron_end"
+ android:autoMirrored="true"
android:visibility="gone"
android:importantForAccessibility="no"
/>
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index f0229a6..c88703d 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -49,17 +49,35 @@
android:layout_gravity="end|center_vertical"
android:focusable="false"/>
- <com.android.systemui.statusbar.phone.StatusIconContainer
- android:id="@+id/statusIcons"
+ <View
+ android:id="@+id/separator"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:visibility="gone"
+ />
+
+ <LinearLayout
+ android:id="@+id/rightLayout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:paddingEnd="@dimen/signal_cluster_battery_padding" />
+ android:gravity="center_vertical|end"
+ >
+ <com.android.systemui.statusbar.phone.StatusIconContainer
+ android:id="@+id/statusIcons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingEnd="@dimen/signal_cluster_battery_padding" />
- <com.android.systemui.BatteryMeterView
- android:id="@+id/batteryRemainingIcon"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- systemui:textAppearance="@style/TextAppearance.QS.Status"
- android:paddingEnd="2dp" />
+ <com.android.systemui.BatteryMeterView
+ android:id="@+id/batteryRemainingIcon"
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ systemui:textAppearance="@style/TextAppearance.QS.Status"
+ android:paddingEnd="2dp" />
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_security_footer.xml b/packages/SystemUI/res/layout/quick_settings_security_footer.xml
index ce7f827..08bd71c 100644
--- a/packages/SystemUI/res/layout/quick_settings_security_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_security_footer.xml
@@ -55,6 +55,7 @@
android:layout_marginStart="8dp"
android:contentDescription="@null"
android:src="@*android:drawable/ic_chevron_end"
+ android:autoMirrored="true"
android:tint="?android:attr/textColorSecondary" />
</com.android.systemui.util.DualHeightHorizontalLinearLayout>
diff --git a/packages/SystemUI/res/layout/smart_action_button.xml b/packages/SystemUI/res/layout/smart_action_button.xml
index 2716034..488be3a 100644
--- a/packages/SystemUI/res/layout/smart_action_button.xml
+++ b/packages/SystemUI/res/layout/smart_action_button.xml
@@ -29,6 +29,8 @@
android:textSize="@dimen/smart_reply_button_font_size"
android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
android:textColor="@color/smart_reply_button_text"
+ android:paddingLeft="@dimen/smart_reply_button_action_padding_left"
+ android:paddingRight="@dimen/smart_reply_button_padding_horizontal"
android:drawablePadding="@dimen/smart_action_button_icon_padding"
android:textStyle="normal"
android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml
index 9faed18..ddf16e0 100644
--- a/packages/SystemUI/res/layout/smart_reply_button.xml
+++ b/packages/SystemUI/res/layout/smart_reply_button.xml
@@ -31,5 +31,7 @@
android:textSize="@dimen/smart_reply_button_font_size"
android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
android:textColor="@color/smart_reply_button_text"
+ android:paddingLeft="@dimen/smart_reply_button_padding_horizontal"
+ android:paddingRight="@dimen/smart_reply_button_padding_horizontal"
android:textStyle="normal"
android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/layout/smart_reply_view.xml b/packages/SystemUI/res/layout/smart_reply_view.xml
index 9fffc72..9d4d1db 100644
--- a/packages/SystemUI/res/layout/smart_reply_view.xml
+++ b/packages/SystemUI/res/layout/smart_reply_view.xml
@@ -24,8 +24,6 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
systemui:spacing="@dimen/smart_reply_button_spacing"
- systemui:singleLineButtonPaddingHorizontal="@dimen/smart_reply_button_padding_horizontal_single_line"
- systemui:doubleLineButtonPaddingHorizontal="@dimen/smart_reply_button_padding_horizontal_double_line"
systemui:buttonStrokeWidth="@dimen/smart_reply_button_stroke_width">
<!-- smart_reply_button(s) will be added here. -->
</com.android.systemui.statusbar.policy.SmartReplyView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index f4cb3b1..3543fd1 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -37,6 +37,25 @@
android:layout_height="match_parent"
android:layout_width="match_parent" />
+ <include
+ layout="@layout/keyguard_bottom_area"
+ android:visibility="gone" />
+
+ <ViewStub
+ android:id="@+id/keyguard_user_switcher_stub"
+ android:layout="@layout/keyguard_user_switcher"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent" />
+
+ <include layout="@layout/status_bar_expanded_plugin_frame"/>
+
+ <include layout="@layout/dock_info_bottom_area_overlay" />
+
+ <com.android.keyguard.LockIconView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/lock_icon_view" />
+
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -49,6 +68,18 @@
layout="@layout/keyguard_status_view"
android:visibility="gone"/>
+ <com.android.systemui.scrim.ScrimView
+ android:id="@+id/scrim_notifications"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:importantForAccessibility="no"
+ systemui:ignoreRightInset="true"
+ systemui:layout_constraintStart_toStartOf="parent"
+ systemui:layout_constraintEnd_toEndOf="parent"
+ systemui:layout_constraintTop_toTopOf="parent"
+ systemui:layout_constraintBottom_toBottomOf="parent"
+ />
+
<include layout="@layout/dock_info_overlay" />
<FrameLayout
@@ -101,22 +132,9 @@
</com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
- <include layout="@layout/dock_info_bottom_area_overlay" />
-
- <include
- layout="@layout/keyguard_bottom_area"
- android:visibility="gone" />
-
- <ViewStub
- android:id="@+id/keyguard_user_switcher_stub"
- android:layout="@layout/keyguard_user_switcher"
- android:layout_height="match_parent"
- android:layout_width="match_parent" />
-
- <include layout="@layout/status_bar_expanded_plugin_frame"/>
-
- <com.android.keyguard.LockIconView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/lock_icon_view" />
+ <FrameLayout
+ android:id="@+id/preview_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ </FrameLayout>
</com.android.systemui.statusbar.phone.NotificationPanelView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index c3c291b..412276d 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -19,8 +19,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
android:visibility="gone">
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/content"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index bea50e8..08284a0 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -51,14 +51,6 @@
sysui:ignoreRightInset="true"
/>
- <com.android.systemui.scrim.ScrimView
- android:id="@+id/scrim_notifications"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- sysui:ignoreRightInset="true"
- />
-
<com.android.systemui.statusbar.LightRevealScrim
android:id="@+id/light_reveal_scrim"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/wallet_empty_state.xml b/packages/SystemUI/res/layout/wallet_empty_state.xml
index 3ca0f73..cc2e781 100644
--- a/packages/SystemUI/res/layout/wallet_empty_state.xml
+++ b/packages/SystemUI/res/layout/wallet_empty_state.xml
@@ -16,7 +16,8 @@
** limitations under the License.
*/
-->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<LinearLayout
android:id="@+id/wallet_empty_state"
android:layout_width="match_parent"
@@ -35,6 +36,7 @@
android:layout_height="28dp"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
+ android:tint="?androidprv:attr/colorAccentPrimary"
android:contentDescription="@null"
android:scaleType="fitCenter"/>
<TextView
diff --git a/packages/SystemUI/res/layout/wallet_fullscreen.xml b/packages/SystemUI/res/layout/wallet_fullscreen.xml
index bbb180f..aceefee 100644
--- a/packages/SystemUI/res/layout/wallet_fullscreen.xml
+++ b/packages/SystemUI/res/layout/wallet_fullscreen.xml
@@ -16,64 +16,73 @@
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
<Toolbar
- android:id="@+id/action_bar"
- style="?android:attr/actionBarStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@android:color/transparent"
- android:navigationContentDescription="@null" />
+ android:id="@+id/action_bar"
+ style="?android:attr/actionBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:navigationContentDescription="@null" />
<LinearLayout
android:id="@+id/card_carousel_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="48dp"
android:orientation="vertical">
- <ImageView
- android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginVertical="10dp"
- android:scaleType="center"
- android:contentDescription="@null"/>
- <TextView
- android:id="@+id/label"
+ <androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/Wallet.TextAppearance"
- android:textAlignment="center"/>
+ android:layout_height="0dp"
+ android:layout_weight="1">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="vertical">
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/wallet_screen_header_view_size"
+ android:layout_height="@dimen/wallet_screen_header_view_size"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginVertical="10dp"
+ android:scaleType="center"
+ android:contentDescription="@null"/>
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Wallet.TextAppearance"
+ android:textAlignment="center"/>
- <com.android.systemui.wallet.ui.WalletCardCarousel
- android:id="@+id/card_carousel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:transitionName="dotIndicator"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_marginVertical="24dp"/>
-
- <Button
- android:id="@+id/wallet_action_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginVertical="16dp"
- android:paddingVertical="@dimen/wallet_button_vertical_padding"
- android:paddingHorizontal="@dimen/wallet_button_horizontal_padding"
- android:background="@drawable/wallet_action_button_bg"
- android:textColor="@color/wallet_white"
- android:textAlignment="center"
- android:visibility="gone"/>
-
+ <com.android.systemui.wallet.ui.WalletCardCarousel
+ android:id="@+id/card_carousel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:transitionName="dotIndicator"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_marginVertical="24dp"/>
+ <Button
+ android:id="@+id/wallet_action_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginVertical="16dp"
+ android:paddingVertical="@dimen/wallet_button_vertical_padding"
+ android:paddingHorizontal="@dimen/wallet_button_horizontal_padding"
+ android:background="@drawable/wallet_action_button_bg"
+ android:textColor="?androidprv:attr/textColorPrimaryInverse"
+ android:textAlignment="center"
+ android:visibility="gone"/>
+ </LinearLayout>
+ </androidx.core.widget.NestedScrollView>
<View
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="1"/>
-
+ android:layout_weight="0.1"/>
<Button
android:id="@+id/wallet_app_button"
android:layout_width="wrap_content"
@@ -83,10 +92,9 @@
android:paddingHorizontal="@dimen/wallet_button_horizontal_padding"
android:background="@drawable/wallet_app_button_bg"
android:text="@string/wallet_app_button_label"
- android:textColor="@color/GM2_blue_600"
+ android:textColor="?androidprv:attr/colorAccentPrimary"
android:textAlignment="center"
android:layout_marginVertical="24dp"/>
-
</LinearLayout>
<include layout="@layout/wallet_empty_state"/>
diff --git a/packages/SystemUI/res/layout/window_magnifier_view.xml b/packages/SystemUI/res/layout/window_magnifier_view.xml
index 96de5a9..ae16e5c 100644
--- a/packages/SystemUI/res/layout/window_magnifier_view.xml
+++ b/packages/SystemUI/res/layout/window_magnifier_view.xml
@@ -75,7 +75,7 @@
android:id="@+id/drag_handle"
android:layout_width="@dimen/magnification_drag_view_size"
android:layout_height="@dimen/magnification_drag_view_size"
- android:layout_margin="@dimen/magnification_outer_border_margin"
+ android:layout_margin="@dimen/magnification_inner_border_margin"
android:layout_gravity="right|bottom"
android:paddingEnd="@dimen/magnifier_drag_handle_padding"
android:paddingBottom="@dimen/magnifier_drag_handle_padding"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index d3ab5b32..3c936a0f 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Foon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Stembystand"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Beursie"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Ontsluit"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Toestel is gesluit"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Wat tans vir vingerafdruk"</string>
@@ -289,11 +290,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiele warmkol afgeskakel."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiele warmkol aangeskakel."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Uitsaai van skerm gestaak."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Werkmodus is onderbreek."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Werkmodus is aan."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Werkmodus is onderbreek."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Werkmodus is aangeskakel."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Databespaarder is afgeskakel."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Databespaarder is aangeskakel."</string>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g>-limiet"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> waarskuwing"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Werkprofiel"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Onderbreek"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Aandbeligting"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aan by sonsondergang"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Tot sonsopkoms"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Volkome\nstilte"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Net\nprioriteit"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Net\nwekkers"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans draadloos • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans vinnig • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans stadig • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Wissel gebruiker, huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Beursie"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Wys alles"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Ontsluit om te betaal"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nie opgestel nie"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontsluit om te gebruik"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kon nie jou kaarte kry nie; probeer later weer"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Sluitskerminstellings"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
<string name="add_tile" msgid="6239678623873086686">"Voeg teël by"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Aan"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Af"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Onbeskikbaar"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Gedeaktiveer"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigasiebalk"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Uitleg"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra linksknoppie-tipe"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"staaf"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"gaan by toestel in"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gebruik vingerafdruk om oop te maak"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Stawing word vereis. Raak die vingerafdruksensor om te staaf."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Oproep aan die gang"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 4dee085..c77dd57 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"ካሜራ"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ስልክ"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"የድምጽ እርዳታ"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"የኪስ ቦርሳ"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ክፈት"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"መሣሪያ ተቆልፏል"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"የጣት አሻራን በመጠባበቅ ላይ"</string>
@@ -289,11 +290,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"የሥራ ሁነታ ባለበት ቆሟል።"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"የሥራ ሁነታ በርቷል።"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"የሥራ ሁነታ ባለበት ቆሟል።"</string>
<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>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ገደብ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"የ<xliff:g id="DATA_LIMIT">%s</xliff:g> ማስጠንቀቂያ"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"የሥራ መገለጫ"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"ባለበት ቆሟል"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"የምሽት ብርሃን"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ጸሐይ ስትጠልቅ ይበራል"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ጸሐይ እስክትወጣ ድረስ"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ሙሉ ለሙሉ\nጸጥታ"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ቅድሚያ ተሰጪ\nብቻ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"ማንቂያዎች\nብቻ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በገመድ-አልባ ኃይል በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ኃይል በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ተጠቃሚ ይለውጡ፣ የአሁን ተጠቃሚ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"የአሁን ተጠቃሚ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"ሁሉንም አሳይ"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ለመክፈል ይክፈቱ"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"አልተዋቀረም"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ለማየት ይክፈቱ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"የእርስዎን ካርዶች ማግኘት ላይ ችግር ነበር፣ እባክዎ ቆይተው እንደገና ይሞክሩ"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"የገጽ መቆለፊያ ቅንብሮች"</string>
<string name="status_bar_work" msgid="5238641949837091056">"የስራ መገለጫ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"የአውሮፕላን ሁነታ"</string>
<string name="add_tile" msgid="6239678623873086686">"ሰቅ ያክሉ"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"በርቷል"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ጠፍቷል"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"አይገኝም"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"ተሰናክሏል"</string>
<string name="nav_bar" msgid="4642708685386136807">"የአሰሳ አሞሌ"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"አቀማመጥ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ተጨማሪ የግራ አዝራር ዓይነት"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ያረጋግጡ"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"መሣሪያን ያስገቡ"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ለመክፈት የጣት አሻራ ይጠቀሙ"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ማረጋገጥ ያስፈልጋል። ለማረጋገጥ የጣት አሻራ ዳሳሹን ይንኩ።"</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"በመካሄድ ላይ የስልክ ጥሪ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index cfbab4b..a3a00ed 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"الكاميرا"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"الهاتف"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"المساعد الصوتي"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"فتح القفل"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"الجهاز مُقفل."</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"في انتظار بصمة الإصبع"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"تم إيقاف وضع العمل مؤقتًا."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"وضع العمل قيد التشغيل."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"تم إيقاف وضع العمل مؤقتًا."</string>
<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>
@@ -420,8 +420,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"قيد <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"تحذير <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"الملف الشخصي للعمل"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"متوقف مؤقتًا"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"الإضاءة الليلية"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"تفعيل عند غروب الشمس"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"حتى شروق الشمس"</string>
@@ -481,14 +480,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"كتم الصوت\nتمامًا"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"الأولوية \nفقط"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"التنبيهات\nفقط"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن لاسلكيًا • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن سريعًا • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن ببطء • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تبديل المستخدم"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"تبديل المستخدم، المستخدم الحالي <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"المستخدم الحالي <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -534,7 +529,7 @@
<string name="manage_notifications_text" msgid="6885645344647733116">"إدارة"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"السجلّ"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"الإشعارات الجديدة"</string>
- <string name="notification_section_header_gentle" msgid="6804099527336337197">"صامت"</string>
+ <string name="notification_section_header_gentle" msgid="6804099527336337197">"صامتة"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"الإشعارات"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"المحادثات"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"محو جميع الإشعارات الصامتة"</string>
@@ -688,12 +683,10 @@
<string name="wallet_title" msgid="5369767670735827105">"المحفظة"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"عرض الكل"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"فتح القفل للدفع"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"لم يتم الإعداد."</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"فتح القفل للاستخدام"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"حدثت مشكلة أثناء الحصول على البطاقات، يُرجى إعادة المحاولة لاحقًا."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"إعدادات شاشة القفل"</string>
<string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string>
<string name="add_tile" msgid="6239678623873086686">"إضافة فئة"</string>
@@ -743,7 +736,7 @@
<string name="inline_block_button" msgid="479892866568378793">"حظر"</string>
<string name="inline_keep_button" msgid="299631874103662170">"الاستمرار في تلقّي الإشعارات"</string>
<string name="inline_minimize_button" msgid="1474436209299333445">"تصغير"</string>
- <string name="inline_silent_button_silent" msgid="525243786649275816">"صامت"</string>
+ <string name="inline_silent_button_silent" msgid="525243786649275816">"صامتة"</string>
<string name="inline_silent_button_stay_silent" msgid="2129254868305468743">"متابعة عرض الإشعارات بدون صوت"</string>
<string name="inline_silent_button_alert" msgid="5705343216858250354">"تنبيه"</string>
<string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"متابعة إرسال التنبيهات"</string>
@@ -891,8 +884,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"مفعّل"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"متوقف"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"غير متوفّر"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"غير مفعّل"</string>
<string name="nav_bar" msgid="4642708685386136807">"شريط التنقل"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"التنسيق"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"نوع زر اليسار الإضافي"</string>
@@ -1149,11 +1141,11 @@
<string name="select_conversation_text" msgid="3376048251434956013">"انقر على محادثة لإضافتها إلى \"الشاشة الرئيسية\"."</string>
<string name="no_conversations_text" msgid="7362374212649891057">"يمكنك الرجوع إلى هذه الأداة عندما تتلقّى بعض الرسائل."</string>
<string name="priority_conversations" msgid="3967482288896653039">"المحادثات ذات الأولوية"</string>
- <string name="recent_conversations" msgid="8531874684782574622">"المحادثات الأخيرة"</string>
+ <string name="recent_conversations" msgid="8531874684782574622">"المحادثات الحديثة"</string>
<string name="okay" msgid="6490552955618608554">"حسنًا"</string>
<string name="timestamp" msgid="6577851592534538533">"قبل <xliff:g id="DURATION">%1$s</xliff:g>"</string>
<string name="less_than_timestamp" msgid="6598972791137724517">"قبل أقل من <xliff:g id="DURATION">%1$s</xliff:g>"</string>
- <string name="over_timestamp" msgid="4765793502859358634">"قبل أكثر <xliff:g id="DURATION">%1$s</xliff:g>"</string>
+ <string name="over_timestamp" msgid="4765793502859358634">"قبل أكثر من <xliff:g id="DURATION">%1$s</xliff:g>"</string>
<string name="birthday_status" msgid="2596961629465396761">"تاريخ الميلاد"</string>
<string name="birthday_status_content_description" msgid="682836371128282925">"إنه يوم ميلاد <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="upcoming_birthday_status" msgid="2005452239256870351">"تاريخ ميلاد قريب"</string>
@@ -1172,7 +1164,7 @@
<string name="status_before_loading" msgid="1500477307859631381">"سيظهر المحتوى قريبًا."</string>
<string name="missed_call" msgid="4228016077700161689">"مكالمة فائتة"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"+<xliff:g id="NUMBER">%d</xliff:g>"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"عرض أحدث الرسائل والمكلمات الفائتة وآخر أخبار الحالة"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"عرض أحدث الرسائل والمكالمات الفائتة والتغييرات في الحالة"</string>
<string name="people_tile_title" msgid="6589377493334871272">"محادثة"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"تم إرسال رسالة من <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"تم إرسال صورة من <xliff:g id="NAME">%1$s</xliff:g>."</string>
@@ -1184,4 +1176,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"المصادقة"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"الدخول إلى الجهاز"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"يمكنك استخدام بصمة الإصبع للفتح"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings_tv.xml b/packages/SystemUI/res/values-ar/strings_tv.xml
index 7297c89..769999f 100644
--- a/packages/SystemUI/res/values-ar/strings_tv.xml
+++ b/packages/SystemUI/res/values-ar/strings_tv.xml
@@ -21,8 +21,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mic_active" msgid="5766614241012047024">"الميكروفون نشط"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"تمكن %1$s من الوصول إلى الميكروفون الخاص بك."</string>
- <string name="notification_vpn_connected" msgid="3891023882833274730">"الشبكة الافتراضية الخاصة (VPN) متصلة."</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"الشبكة الافتراضية الخاصة (VPN) غير متصلة."</string>
+ <string name="notification_vpn_connected" msgid="3891023882833274730">"شبكة VPN متصلة."</string>
+ <string name="notification_vpn_disconnected" msgid="7150747626448044843">"شبكة VPN غير متصلة."</string>
<string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"عبر <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="tv_notification_panel_title" msgid="5311050946506276154">"الإشعارات"</string>
<string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ما من إشعارات"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index d20b67d..0fba949 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"কেমেৰা"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ফ\'ন"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"কণ্ঠধ্বনিৰে সহায়"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"আনলক কৰক"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ডিভাইচটো লক হৈ আছে"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"ফিংগাৰপ্ৰিণ্টৰ বাবে ৰৈ থকা হৈছে"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"কৰ্মস্থান ম’ড পজ হৈ আছে।"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"কৰ্মস্থান ম\'ড অন হৈ আছে।"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"কৰ্মস্থান ম’ড পজ হৈ আছে।"</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সীমা"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সকীয়নি"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"পজ হৈ আছে"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"ৰাতিৰ পোহৰ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"সূৰ্যাস্তত অন কৰক"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"সূৰ্যোদয়ৰ লৈকে"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"সম্পূর্ণ \n নিৰৱতা"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"কেৱল\nগুৰুত্বপূৰ্ণ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"কেৱল\nএলাৰ্মসমূহ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • বেতাঁৰৰ দ্বাৰা চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • দ্ৰুতগতিৰে চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • লাহে লাহে চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যৱহাৰকাৰী সলনি কৰক"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ব্যৱহাৰকাৰী সলনি কৰক, বৰ্তমানৰ ব্যৱহাৰকাৰী <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"বর্তমানৰ ব্যৱহাৰকাৰী <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"ৱালেট"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"আটাইবোৰ দেখুৱাওক"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"পৰিশোধ কৰিবলৈ আনলক কৰক"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"ছেট আপ কৰা হোৱা নাই"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ব্যৱহাৰ কৰিবলৈ আনলক কৰক"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"আপোনাৰ কাৰ্ড লাভ কৰোঁতে এটা সমস্যা হৈছে, অনুগ্ৰহ কৰি পাছত পুনৰ চেষ্টা কৰক"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"লক স্ক্ৰীনৰ ছেটিং"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"এয়াৰপ্লেইন ম\'ড"</string>
<string name="add_tile" msgid="6239678623873086686">"টাইল যোগ দিয়ক"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"অন"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"অফ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"উপলব্ধ নহয়"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"অক্ষম কৰা আছে"</string>
<string name="nav_bar" msgid="4642708685386136807">"নেভিগেশ্বন দণ্ড"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"লেআউট"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"বাওঁ বুটামৰ অতিৰিক্ত প্ৰকাৰ"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"বিশ্বাসযোগ্যতা প্ৰমাণ কৰক"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইচ আনলক কৰক"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলিবলৈ ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index d30d8ab..7babf41 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Səs Yardımçısı"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Kiliddən çıxarın"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Cihaz kilidlənib"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Barmaq izi gözlənilir"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobil hotspot deaktivdir."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobil hotspot aktivdir."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekran yayımı dayandırıldı."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"İş rejiminə pauza verilib."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"İş rejimi aktivdir."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"İş rejiminə pauza verilib."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"İş rejimi yanılıdır."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Trafikə qənaət edilmir."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Trafikə qənaət edilir."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> xəbərdarlığı"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"İş profili"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pauza verilib"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Gecə işığı"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Qürubda aktiv ediləcək"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Şəfəq vaxtına qədər"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Tam\nsakitlik"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Yalnız\nprioritet"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Yalnız\nalarmlar"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Simsiz şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sürətlə şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Asta şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"İstifadəçiləri dəyişin, indiki istifadəçi: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Cari istifadəçi <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Pulqabı"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Hamısını göstər"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Ödəmək üçün kiliddən çıxarın"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Quraşdırılmayıb"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"İstifadə etmək üçün kiliddən çıxarın"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kartların əldə edilməsində problem oldu, sonra yenidən cəhd edin"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Kilid ekranı ayarları"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Təyyarə rejimi"</string>
<string name="add_tile" msgid="6239678623873086686">"Xana əlavə edin"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Aktiv"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Deaktiv"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Əlçatan deyil"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Deaktiv edilib"</string>
<string name="nav_bar" msgid="4642708685386136807">"Naviqasiya paneli"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Tərtibat"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Əlavə sol düymə növü"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"doğrulayın"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz daxil edin"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmaq üçün barmaq izindən istifadə edin"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index dd342ff..4451304 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovna pomoć"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Otključajte"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Čeka se otisak prsta"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilni hotspot je isključen."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilni hotspot je uključen."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Prebacivanje ekrana je zaustavljeno."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Poslovni režim je pauziran."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Režim rada je uključen."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Poslovni režim je pauziran."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Režim rada je uključen."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Ušteda podataka je isključena."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Ušteda podataka je uključena."</string>
@@ -414,8 +414,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ograničenje od <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozorenje za <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Poslovni profil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pauzirano"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Noćno svetlo"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Uključuje se po zalasku sunca"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do izlaska sunca"</string>
@@ -475,14 +474,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Potpuna\ntišina"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Samo\npriorit. prekidi"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Samo\nalarmi"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bežično se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Puni se • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Brzo se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Promenite korisnika, aktuelni korisnik je <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuelni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -679,12 +674,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Novčanik"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Prikaži sve"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Otključaj radi plaćanja"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nije podešeno"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključaj radi korišćenja"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Došlo je do problema pri preuzimanju kartica. Probajte ponovo kasnije"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Podešavanja zaključanog ekrana"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim rada u avionu"</string>
<string name="add_tile" msgid="6239678623873086686">"Dodaj pločicu"</string>
@@ -876,8 +869,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Uključeno"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Isključeno"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nedostupno"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Onemogućeno"</string>
<string name="nav_bar" msgid="4642708685386136807">"Traka za navigaciju"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Raspored"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Dodatni tip levog dugmeta"</string>
@@ -1150,7 +1142,7 @@
<string name="audio_status" msgid="4237055636967709208">"Sluša se"</string>
<string name="game_status" msgid="1340694320630973259">"Igra se"</string>
<string name="empty_user_name" msgid="3389155775773578300">"Prijatelji"</string>
- <string name="empty_status" msgid="5938893404951307749">"Ćaskamo večeras!"</string>
+ <string name="empty_status" msgid="5938893404951307749">"Ćaskamo večeras?"</string>
<string name="status_before_loading" msgid="1500477307859631381">"Sadržaj će se uskoro pojaviti"</string>
<string name="missed_call" msgid="4228016077700161689">"Propušten poziv"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
@@ -1166,4 +1158,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"potvrdite identitet"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"unesite uređaj"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index eed1535..26d4dff6e 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Камера"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Тэлефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Галасавая дапамога"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Разблакiраваць"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Прылада заблакіравана"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Чаканне ўводу даных адбітка пальца"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Працоўны рэжым прыпынены."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Рэжым працы ўкл."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Працоўны рэжым прыпынены."</string>
<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>
@@ -416,8 +416,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ліміт <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Папярэджанне: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Працоўны профіль"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Прыпынена"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Начная падсветка"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Уключаць увечары"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Да ўсходу сонца"</string>
@@ -477,14 +476,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Поўная\nцішыня"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Толькі\nпрыярытэтныя"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Толькі\nбудзільнікі"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе бесправадная зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе хуткая зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе павольная зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Перайсці да іншага карыстальніка"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Перайсці да іншага карыстальніка, бягучы карыстальнік <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Бягучы карыстальнік <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +677,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Кашалёк"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Паказаць усе"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Разблакіраваць для аплаты"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Не наладжана"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Разблакіраваць для выкарыстання"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Узнікла праблема з загрузкай вашых карт. Паўтарыце спробу пазней"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Налады экрана блакіроўкі"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Працоўны профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Рэжым палёту"</string>
<string name="add_tile" msgid="6239678623873086686">"Дадаць плітку"</string>
@@ -881,8 +874,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Уключана"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Выключана"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Недаступна"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Выключана"</string>
<string name="nav_bar" msgid="4642708685386136807">"Панэль навігацыі"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Раскладка"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Дадатковы тып кнопкі \"ўлева\""</string>
@@ -1172,4 +1164,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"правесці аўтэнтыфікацыю"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"вызначыць прыладу"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Каб адкрыць, скарыстайце адбітак пальца"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 17480bf..9f4c899 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Камера"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Гласова помощ"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Отключване"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Устройството е заключено"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Изчаква се отпечатък"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Работният режим е на пауза."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Работният режим е включен."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Работният режим е на пауза."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ограничение от <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Предупреждение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Служебен потребителски профил"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"На пауза"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Нощно осветление"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ще се вкл. по залез"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"До изгрев"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Пълна\nтишина"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Само\nс приоритет"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Само\nбудилници"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се безжично • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се бързо • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се бавно • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Превключване между потребителите"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Превключване на потребителя – текущият е <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Текущ потребител – <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Портфейл"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Показване на всички"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Отключване с цел плащане"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Не е настроено"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Отключване с цел използване"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"При извличането на картите ви възникна проблем. Моля, опитайте отново по-късно"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Настройки за заключения екран"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Потребителски профил в Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Самолетен режим"</string>
<string name="add_tile" msgid="6239678623873086686">"Добавяне на плочка"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Вкл."</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Изкл."</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Не е налице"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Деактивирано"</string>
<string name="nav_bar" msgid="4642708685386136807">"Лента за навигация"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Оформление"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Тип на допълнителния ляв бутон"</string>
@@ -1144,11 +1136,11 @@
<string name="audio_status" msgid="4237055636967709208">"Слуша се"</string>
<string name="game_status" msgid="1340694320630973259">"Играете"</string>
<string name="empty_user_name" msgid="3389155775773578300">"Приятели"</string>
- <string name="empty_status" msgid="5938893404951307749">"Да разговаряме!"</string>
+ <string name="empty_status" msgid="5938893404951307749">"Да поговорим!"</string>
<string name="status_before_loading" msgid="1500477307859631381">"Съдържанието ще се покаже скоро"</string>
<string name="missed_call" msgid="4228016077700161689">"Пропуснато обаждане"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"Над <xliff:g id="NUMBER">%d</xliff:g>"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"Преглеждайте скорошните съобщения, пропуснатите обаждания и актуална информация за състоянието"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"Преглеждайте скорошни съобщения, пропуснати обаждания и информация за състоянието"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Разговор"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> изпрати съобщение"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> изпрати изображение"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"удостоверяване"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"вход в устройството"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Използвайте отпечатък за отваряне"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index adbef20..66c03b2 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"ক্যামেরা"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ফোন"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ভয়েস সহায়তা"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"আনলক করুন"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ডিভাইস লক করা আছে"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"আঙ্গুলের ছাপের জন্য অপেক্ষা করা হচ্ছে"</string>
@@ -289,10 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"\'অফিস\' মোড পজ করা আছে।"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"কাজের মোড চালু আছে"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
+ <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (6422896967647049692) -->
<skip />
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"কাজের মোড চালু আছে"</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ডেটা সেভার বন্ধ আছে।"</string>
@@ -412,8 +413,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"সীমা <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সতর্কতা"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"কাজের প্রোফাইল"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"পজ করা আছে"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"নাইট লাইট"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"সূর্যাস্তে চালু হবে"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"সূর্যোদয় পর্যন্ত"</string>
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"একদম\nনিরব"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"শুধুমাত্র\nঅগ্রাধিকার"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"শুধুমাত্র\nঅ্যালার্মগুলি"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ওয়্যারলেস পদ্ধতিতে চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চার্জিং • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • দ্রুত চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ধীরে চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ব্যবহারকারী পাল্টান, বর্তমান ব্যবহারকারী <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"<xliff:g id="CURRENT_USER_NAME">%s</xliff:g> হল বর্তমান ব্যবহারকারী"</string>
@@ -676,12 +672,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"সবকটি দেখুন"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"পেমেন্ট করতে ডিভাইস আনলক করুন"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"সেট আপ করা নেই"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ব্যবহার করতে আনলক করুন"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"আপনার কার্ড সংক্রান্ত তথ্য পেতে সমস্যা হয়েছে, পরে আবার চেষ্টা করুন"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"লক স্ক্রিন সেটিংস"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কাজের প্রোফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"বিমান মোড"</string>
<string name="add_tile" msgid="6239678623873086686">"টাইল যোগ করুন"</string>
@@ -750,14 +744,10 @@
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>স্ট্যাটাস:</b> লেভেল কমিয়ে সাইলেন্ করা হয়েছে"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>স্ট্যাটাস:</b> র্যাঙ্ক বেড়ে গেছে"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>স্ট্যাটাস:</b> র্যাঙ্ক কমে গেছে"</string>
- <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
- <skip />
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়, বাবল হিসেবেও এটি দেখা যায়"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয় এবং এর ফলে \'বিরক্ত করবে না\' মোডে কাজ করতে অসুবিধা হয়"</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়, বাবল হিসেবেও এটি দেখা যায় এবং এর ফলে \'বিরক্ত করবে না\' মোডে কাজ করতে অসুবিধা হয়"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"সেটিংস"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"অগ্রাধিকার"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এ কথোপকথন ফিচার কাজ করে না"</string>
@@ -875,8 +865,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"চালু আছে"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"বন্ধ আছে"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"উপলভ্য নয়"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"বন্ধ করা আছে"</string>
<string name="nav_bar" msgid="4642708685386136807">"নেভিগেশন বার"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"লেআউট"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"অতিরিক্ত বাঁদিকের বোতামের ধরণ"</string>
@@ -1131,9 +1120,9 @@
<string name="priority_conversations" msgid="3967482288896653039">"গুরুত্বপূর্ণ কথোপকথন"</string>
<string name="recent_conversations" msgid="8531874684782574622">"সাম্প্রতিক কথোপকথন"</string>
<string name="okay" msgid="6490552955618608554">"ঠিক আছে"</string>
- <string name="timestamp" msgid="6577851592534538533">"<xliff:g id="DURATION">%1$s</xliff:g> ঘণ্টা আগে"</string>
+ <string name="timestamp" msgid="6577851592534538533">"<xliff:g id="DURATION">%1$s</xliff:g> আগে"</string>
<string name="less_than_timestamp" msgid="6598972791137724517">"<xliff:g id="DURATION">%1$s</xliff:g> থেকে কিছু কম সময় আগে"</string>
- <string name="over_timestamp" msgid="4765793502859358634">"<xliff:g id="DURATION">%1$s</xliff:g> সপ্তাহেরও আগে"</string>
+ <string name="over_timestamp" msgid="4765793502859358634">"এই সময়েরও বেশি আগে: <xliff:g id="DURATION">%1$s</xliff:g>"</string>
<string name="birthday_status" msgid="2596961629465396761">"জন্মদিন"</string>
<string name="birthday_status_content_description" msgid="682836371128282925">"এটি হল <xliff:g id="NAME">%1$s</xliff:g>-এর জন্মদিন"</string>
<string name="upcoming_birthday_status" msgid="2005452239256870351">"জন্মদিন শীঘ্রই আসছে"</string>
@@ -1164,4 +1153,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"যাচাই করিয়ে নিন"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইস আনলক করুন"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলতে ফিঙ্গারপ্রিন্ট ব্যবহার করুন"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index a54dffe..e536e67 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovna pomoć"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Otključaj"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Čeka se otisak prsta"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilna pristupna tačka je isključena."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilna pristupna tačka je uključena."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Prebacivanje ekrana je zaustavljeno."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Poslovni način rada je pauziran."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Poslovni režim uključen."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Poslovni način rada je pauziran."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Poslovni režim je uključen."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Ušteda podataka je isključena."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Ušteda podataka je uključena."</string>
@@ -414,8 +414,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ograničenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozorenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Radni profil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pauzirano"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Noćno svjetlo"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Uključuje se u sumrak"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do svitanja"</string>
@@ -475,14 +474,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Potpuna\ntišina"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Samo\nprioritetni prekidi"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Samo\nalarmi"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bežično punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Brzo punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Zamijeni korisnika. Trenutni korisnik je <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Trenutni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -679,12 +674,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Novčanik"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Prikaži sve"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Otključaj za plaćanje"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nije postavljeno"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključajte da koristite"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Došlo je do problema prilikom preuzimanja vaših kartica. Pokušajte ponovo kasnije"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Postavke zaključavanja ekrana"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil za posao"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u avionu"</string>
<string name="add_tile" msgid="6239678623873086686">"Dodaj pločicu"</string>
@@ -876,8 +869,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Uključeno"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Isključeno"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nedostupno"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Onemogućeno"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigaciona traka"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Raspored"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Vrsta dodatnog dugmeta lijevo"</string>
@@ -1166,4 +1158,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentificiranje"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"pristup uređaju"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 803bbd1d..ec2a0a0 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Càmera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telèfon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistència per veu"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloqueja"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositiu bloquejat"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"S\'està esperant l\'empremta digital"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"El punt d\'accés mòbil està desactivat."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"El punt d\'accés mòbil està activat."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"S\'ha aturat l\'emissió de la pantalla."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Mode de feina en pausa."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"El mode de feina està activat."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Mode de feina pausat."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"S\'ha activat el mode de feina."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"S\'ha desactivat l\'Economitzador de dades."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"S\'ha activat l\'Economitzador de dades."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Límit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advertiment: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de treball"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"En pausa"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Llum nocturna"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Al vespre"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Fins a l\'alba"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silenci\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Només\ninterr. prior."</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Només\nalarmes"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant sense fil • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • S\'està carregant • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant ràpidament • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant lentament • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Canvia d\'usuari. Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Mostra-ho tot"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloqueja per pagar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"No s\'ha configurat"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloqueja per utilitzar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Hi ha hagut un problema en obtenir les teves targetes; torna-ho a provar més tard"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuració de la pantalla de bloqueig"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de treball"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode d\'avió"</string>
<string name="add_tile" msgid="6239678623873086686">"Afegeix un mosaic"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activat"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desactivat"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"No disponible"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Desactivat"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegació"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Disposició"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipus de botó addicional de l\'esquerra"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accedir al dispositiu"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilitza l\'empremta digital per obrir"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 8ed760a..db334f2 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Fotoaparát"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Hlasová asistence"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Odemknout"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Zařízení uzamčeno"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Čeká se na použití otisku"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobile hotspot je vypnutý."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobile hotspot je zapnutý."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Odesílání obrazovky zastaveno."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Pracovní režim byl pozastaven."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Pracovní režim zapnutý"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Pracovní režim byl pozastaven."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Pracovní režim je zapnutý."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Spořič dat byl vypnut."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Spořič dat byl zapnut."</string>
@@ -416,8 +416,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozornění při <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Pracovní profil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pozastaveno"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Noční režim"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Při soumraku"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do svítání"</string>
@@ -477,14 +476,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Úplné\nticho"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Pouze\nprioritní"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Pouze\nbudíky"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bezdrátové nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Rychlé nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Pomalé nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Přepnout uživatele, aktuální uživatel: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuální uživatel <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +677,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Peněženka"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Zobrazit vše"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Odemknout a zaplatit"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Není nastaveno"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odemknout a použít"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Při načítání karet došlo k problému, zkuste to později"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavení obrazovky uzamčení"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovní profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim Letadlo"</string>
<string name="add_tile" msgid="6239678623873086686">"Přidat dlaždici"</string>
@@ -881,8 +874,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Zapnuto"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Vypnuto"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nedostupné"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Vypnuto"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigační panel"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Rozvržení"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Zvláštní typ tlačítka vlevo"</string>
@@ -1172,4 +1164,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ověříte"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"zadáte zařízení"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"K otevření použijte otisk prstu"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index c76ebc3..de4deff 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Taleassistent"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Lås op"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheden er låst"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Venter på fingeraftryk"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilhotspot er slået fra."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilhotspot er slået til."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Casting af din skærm er stoppet."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Arbejdstilstand er på pause."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Arbejdstilstand er slået til."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Arbejdstilstand er sat på pause."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Arbejdstilstand er slået til."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Datasparefunktionen er slået fra."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Datasparefunktionen er aktiveret."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Grænse: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advarsel ved <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Arbejdsprofil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Sat på pause"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nattelys"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Tænd ved solnedgang"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Indtil solopgang"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Total\nstilhed"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Kun\nprioritet"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Kun\nalarmer"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Trådløs opladning • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader hurtigt • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader langsomt • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Skift bruger. Nuværende bruger er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Nuværende bruger: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Vis alle"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Lås op for at betale"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Ikke konfigureret"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås op for at bruge"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Dine kort kunne ikke hentes. Prøv igen senere."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lås skærmindstillinger"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbejdsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flytilstand"</string>
<string name="add_tile" msgid="6239678623873086686">"Tilføj et felt"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Til"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Fra"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Ikke tilgængelig"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Deaktiveret"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigationslinje"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra venstre knaptype"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"godkende"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"få adgang til enheden"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Brug fingeraftryk for at åbne"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 19691c0..e6334e0 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefonnummer"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Sprachassistent"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Entsperren"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Gerät gesperrt"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Auf Fingerabdruck wird gewartet"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Der mobile Hotspot ist deaktiviert."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Der mobile Hotspot ist aktiviert."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Die Bildschirmübertragung wurde angehalten."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Arbeitsmodus pausiert."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Arbeitsmodus an."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Arbeitsmodus pausiert."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Arbeitsmodus aktiviert."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Der Datensparmodus ist deaktiviert."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Der Datensparmodus ist aktiviert."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> Datenlimit"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Warnung für <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Arbeitsprofil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausiert"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nachtlicht"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"An bei Sonnenuntergang"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Bis Sonnenaufgang"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Laut-\nlos"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Nur\nwichtige"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Nur\nWecker"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird kabellos geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird schnell geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird langsam geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Nutzer wechseln. Aktueller Nutzer: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktueller Nutzer <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Geldbörse"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Alle anzeigen"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Zum Bezahlen entsperren"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nicht eingerichtet"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Zum Verwenden entsperren"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Beim Abrufen deiner Karten ist ein Fehler aufgetreten – bitte versuch es später noch einmal"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Einstellungen für den Sperrbildschirm"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbeitsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugmodus"</string>
<string name="add_tile" msgid="6239678623873086686">"Kachel hinzufügen"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"An"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Aus"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nicht verfügbar"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Deaktiviert"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigationsleiste"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Zusätzlicher linker Schaltflächentyp"</string>
@@ -1144,7 +1136,7 @@
<string name="audio_status" msgid="4237055636967709208">"Hört etwas"</string>
<string name="game_status" msgid="1340694320630973259">"Spielt"</string>
<string name="empty_user_name" msgid="3389155775773578300">"Freunde"</string>
- <string name="empty_status" msgid="5938893404951307749">"Chat heute Abend!"</string>
+ <string name="empty_status" msgid="5938893404951307749">"Heute Abend chatten!"</string>
<string name="status_before_loading" msgid="1500477307859631381">"Der Inhalt wird bald angezeigt"</string>
<string name="missed_call" msgid="4228016077700161689">"Verpasster Anruf"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authentifizieren"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"Eingeben des Geräts"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Mit Fingerabdruck öffnen"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 3da19fc..8bc1765 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Φωτογραφική μηχανή"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Τηλέφωνο"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Φωνητική υποβοήθηση"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Ξεκλείδωμα"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Η συσκευή κλειδώθηκε"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Αναμονή για δακτυλικό αποτύπωμα"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Η λειτουργία εργασίας τέθηκε σε παύση."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Η λειτουργία εργασίας είναι ενεργή."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Η λειτουργία εργασίας τέθηκε σε παύση."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Όριο <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Προειδοποίηση για <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Προφίλ εργασίας"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Σε παύση"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Νυχτερινός φωτισμός"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Κατά τη δύση"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Μέχρι την ανατολή"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Πλήρης\nσίγαση"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Μόνο\nπροτεραιότητας"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Μόνο\nειδοποιήσεις"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ασύρματη φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Γρήγορη φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Αργή φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Εναλλαγή χρήστη, τρέχων χρήστης <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Τρέχων χρήστης <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Πορτοφόλι"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Εμφάνιση όλων"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Ξεκλείδωμα για πληρωμή"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Δεν έχει ρυθμιστεί"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ξεκλείδωμα για χρήση"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Παρουσιάστηκε πρόβλημα με τη λήψη των καρτών σας. Δοκιμάστε ξανά αργότερα"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ρυθμίσεις κλειδώματος οθόνης"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Προφίλ εργασίας"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Λειτουργία πτήσης"</string>
<string name="add_tile" msgid="6239678623873086686">"Προσθήκη πλακιδίου"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ενεργό"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Απενεργοποίηση"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Μη διαθέσιμο"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Ανενεργό"</string>
<string name="nav_bar" msgid="4642708685386136807">"Γραμμή πλοήγησης"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Διάταξη"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Επιπλέον τύπος αριστερού κουμπιού"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"έλεγχος ταυτότητας"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"εισαγωγή συσκευής"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Χρήση δακτυλικού αποτυπώματος για άνοιγμα"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 25a34ae..428a565 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Camera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Phone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Waiting for fingerprint"</string>
@@ -291,7 +292,7 @@
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Screen casting stopped."</string>
<string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Work mode paused."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Work mode on."</string>
- <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Work mode turned paused."</string>
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Work mode paused."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Work mode turned on."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver turned off."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver turned on."</string>
@@ -1150,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 3511d97..99ec519 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Camera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Phone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Waiting for fingerprint"</string>
@@ -291,7 +292,7 @@
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Screen casting stopped."</string>
<string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Work mode paused."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Work mode on."</string>
- <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Work mode turned paused."</string>
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Work mode paused."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Work mode turned on."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver turned off."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver turned on."</string>
@@ -1150,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 25a34ae..428a565 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Camera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Phone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Waiting for fingerprint"</string>
@@ -291,7 +292,7 @@
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Screen casting stopped."</string>
<string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Work mode paused."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Work mode on."</string>
- <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Work mode turned paused."</string>
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Work mode paused."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Work mode turned on."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver turned off."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver turned on."</string>
@@ -1150,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 25a34ae..428a565 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Camera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Phone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Waiting for fingerprint"</string>
@@ -291,7 +292,7 @@
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Screen casting stopped."</string>
<string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Work mode paused."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Work mode on."</string>
- <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Work mode turned paused."</string>
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Work mode paused."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Work mode turned on."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver turned off."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver turned on."</string>
@@ -1150,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 04d0ade..11e32a759 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Camera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Phone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Waiting for fingerprint"</string>
@@ -291,7 +292,7 @@
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Screen casting stopped."</string>
<string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Work mode paused."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Work mode on."</string>
- <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Work mode turned paused."</string>
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Work mode paused."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Work mode turned on."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver turned off."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver turned on."</string>
@@ -1150,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 15347dd..1b0e816 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Cámara"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Teléfono"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistente voz"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Esperando huella dactilar"</string>
@@ -289,11 +290,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Zona móvil desactivada"</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Zona móvil activada"</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmisión de pantalla detenida"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabajo pausado."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabajo activado"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Se pausó el modo de trabajo."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Se activó el modo de trabajo."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Se desactivó el Ahorro de datos."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Se activó el Ahorro de datos."</string>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabajo"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausado"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luz nocturna"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Al atardecer"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hasta el amanecer"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silencio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Solo\nprioridad"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Solo\nalarmas"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando de manera inalámbrica • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando rápido • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lento • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambiar de usuario (usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"El usuario actual es <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar todo"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloquear para pagar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Sin configurar"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocurrió un problema al obtener las tarjetas; vuelve a intentarlo más tarde"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración de pantalla de bloqueo"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avión"</string>
<string name="add_tile" msgid="6239678623873086686">"Agregar mosaico"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desactivado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"No disponible"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Inhabilitada"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegación"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Diseño"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botón izquierdo adicional"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ingresar al dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella dactilar para abrir"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Se requiere de una autenticación. Toca el sensor de huellas dactilares para autenticarte."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Llamada en curso"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 57ce1c5..33a8660 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Cámara"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Teléfono"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistente voz"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Esperando huella digital"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Punto de acceso móvil desactivado."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Punto de acceso móvil activado."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Envío de pantalla detenido."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabajo pausado."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabajo activado."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Modo de trabajo pausado."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Modo de trabajo activado."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Ahorro de datos desactivado."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Ahorro de datos activado."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabajo"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausado"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luz nocturna"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Al atardecer"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hasta el amanecer"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silencio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Solo\ncon prioridad"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Solo\nalarmas"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga sin cables • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambiar de usuario (usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar todo"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloquear para pagar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Sin configurar"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Se ha producido un problema al obtener tus tarjetas. Inténtalo de nuevo más tarde."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ajustes de pantalla de bloqueo"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
<string name="add_tile" msgid="6239678623873086686">"Añadir icono"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desactivado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"No disponible"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Inhabilitado"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegación"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Diseño"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botón a la izquierda extra"</string>
@@ -1011,7 +1003,7 @@
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"El modo Ahorro de batería se activará automáticamente cuando quede menos de un <xliff:g id="PERCENTAGE">%d</xliff:g> %% de batería."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Ajustes"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"Entendido"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Volcar pila de SysUI"</string>
+ <string name="heap_dump_tile_name" msgid="2464189856478823046">"Volcar montículo de SysUI"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"<xliff:g id="APP">%1$s</xliff:g> está usando tu <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hay aplicaciones que usan tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1130,7 +1122,7 @@
<string name="timestamp" msgid="6577851592534538533">"Hace <xliff:g id="DURATION">%1$s</xliff:g>"</string>
<string name="less_than_timestamp" msgid="6598972791137724517">"Hace menos de <xliff:g id="DURATION">%1$s</xliff:g>"</string>
<string name="over_timestamp" msgid="4765793502859358634">"Hace más de <xliff:g id="DURATION">%1$s</xliff:g>"</string>
- <string name="birthday_status" msgid="2596961629465396761">"Fecha de nacimiento"</string>
+ <string name="birthday_status" msgid="2596961629465396761">"Cumpleaños"</string>
<string name="birthday_status_content_description" msgid="682836371128282925">"Es el cumpleaños de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="upcoming_birthday_status" msgid="2005452239256870351">"Cumpleaños en breve"</string>
<string name="upcoming_birthday_status_content_description" msgid="2165036816803797148">"Se acerca el cumpleaños de <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -1144,7 +1136,7 @@
<string name="audio_status" msgid="4237055636967709208">"Escuchando"</string>
<string name="game_status" msgid="1340694320630973259">"Reproduciendo"</string>
<string name="empty_user_name" msgid="3389155775773578300">"Amigos"</string>
- <string name="empty_status" msgid="5938893404951307749">"Charlemos esta noche"</string>
+ <string name="empty_status" msgid="5938893404951307749">"¿Hablamos luego?"</string>
<string name="status_before_loading" msgid="1500477307859631381">"El contenido se mostrará en breve"</string>
<string name="missed_call" msgid="4228016077700161689">"Llamada perdida"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticarte"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"acceder al dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella digital para abrir"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index ff833d9..3d3a09b 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kaamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Häälabi"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Luku avamine"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Seade on lukustatud"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Sõrmejälje ootel"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiilside kuumkoht on välja lülitatud."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiilside kuumkoht on sisse lülitatud."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekraanikuva ülekandmine on peatatud."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Töörežiim peatati."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Töörežiim on sees."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Töörežiim peatati."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Töörežiim on sisse lülitatud."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Andmemahu säästja on välja lülitatud."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Andmemahu säästja on sisse lülitatud."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limiit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> hoiatus"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Tööprofiil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Peatatud"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Öövalgus"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Sissel. päikeselooj."</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Kuni päikesetõusuni"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Täielik\nvaikus"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Ainult\nprioriteetsed"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Ainult\nalarmid"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Juhtmeta laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kiirlaadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Aeglane laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Kasutaja vahetamine, praegune kasutaja: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Praegune kasutaja <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Rahakott"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Kuva kõik"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Avage maksmiseks"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Pole seadistatud"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Avage kasutamiseks"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Teie kaartide hankimisel ilmnes probleem, proovige hiljem uuesti"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lukustuskuva seaded"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Tööprofiil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lennukirežiim"</string>
<string name="add_tile" msgid="6239678623873086686">"Paani lisamine"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Sees"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Väljas"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Pole saadaval"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Keelatud"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigeerimisriba"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Paigutus"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Täiendava vasaku nupu tüüp"</string>
@@ -1129,7 +1121,7 @@
<string name="okay" msgid="6490552955618608554">"OK"</string>
<string name="timestamp" msgid="6577851592534538533">"<xliff:g id="DURATION">%1$s</xliff:g> tagasi"</string>
<string name="less_than_timestamp" msgid="6598972791137724517">"Vähem kui <xliff:g id="DURATION">%1$s</xliff:g> tagasi"</string>
- <string name="over_timestamp" msgid="4765793502859358634">"Üle <xliff:g id="DURATION">%1$s</xliff:g> tagasi"</string>
+ <string name="over_timestamp" msgid="4765793502859358634">"Rohkem kui <xliff:g id="DURATION">%1$s</xliff:g> tagasi"</string>
<string name="birthday_status" msgid="2596961629465396761">"Sünnipäev"</string>
<string name="birthday_status_content_description" msgid="682836371128282925">"On kasutaja <xliff:g id="NAME">%1$s</xliff:g> sünnipäev"</string>
<string name="upcoming_birthday_status" msgid="2005452239256870351">"Peagi on sünnipäev"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentimiseks"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"seadmesse sisenemiseks"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Kasutage avamiseks sõrmejälge"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 85679b9..eb2bde3 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefonoa"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ahots-laguntza"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desblokeatu"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Gailua blokeatuta dago"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Hatz-markaren zain"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Wifi-gune mugikorra desaktibatu egin da."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Wifi-gune mugikorra aktibatu egin da."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Pantaila igortzeari utzi zaio."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Lan modua pausatuta dago."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Aktibatuta dago lan modua."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Pausatu egin da lan modua."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Aktibatuta dago lan modua."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Desaktibatuta dago datu-aurrezlea."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Aktibatuta dago datu-aurrezlea."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Muga: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Abisua: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Laneko profila"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausatuta"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Gaueko argia"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ilunabarrean"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Ilunabarrera arte"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Isiltasun\nosoa"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Lehentasunezkoak\nsoilik"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Alarmak\nsoilik"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hari gabe kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bizkor kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mantso kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Aldatu erabiltzailea. <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> da saioa hasita daukana."</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Erabiltzailea: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Erakutsi guztiak"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desblokeatu ordaintzeko"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Konfiguratu gabe"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desblokeatu erabiltzeko"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Arazo bat izan da txartelak eskuratzean. Saiatu berriro geroago."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Pantaila blokeatuaren ezarpenak"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profila"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hegaldi modua"</string>
<string name="add_tile" msgid="6239678623873086686">"Gehitu lauza"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Aktibatuta"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desaktibatuta"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Ez dago erabilgarri"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Desgaituta"</string>
<string name="nav_bar" msgid="4642708685386136807">"Nabigazio-barra"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Diseinua"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ezkerreko botoi gehigarriaren mota"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifikatu"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"sartu gailuan"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Erabili hatz-marka irekitzeko"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 052a87a..aacf905 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"دوربین"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"تلفن"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"دستیار صوتی"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"باز کردن قفل"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"دستگاه قفل است"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"در انتظار اثر انگشت"</string>
@@ -289,11 +290,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"حالت کار موقتاً متوقف شده است."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"حالت کار روشن."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"حالت کار موقتاً متوقف شده است."</string>
<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>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> محدودیت"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"هشدار <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"نمایه کاری"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"موقتاً متوقف"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"نور شب"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"غروب روشن میشود"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"تا طلوع"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"سکوت\nکامل"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"فقط\nاولویتدار"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"فقط\nهشدارها"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن بیسیم • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن سریع • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن آهسته • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"تعویض کاربر، کاربر کنونی <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"کاربر کنونی <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"کیفپول"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"نمایش همه"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"باز کردن قفل برای پرداخت"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"تنظیمنشده"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"برای استفاده، قفل را باز کنید"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"هنگام دریافت کارتها مشکلی پیش آمد، لطفاً بعداً دوباره امتحان کنید"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"تنظیمات صفحه قفل"</string>
<string name="status_bar_work" msgid="5238641949837091056">"نمایه کاری"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"حالت هواپیما"</string>
<string name="add_tile" msgid="6239678623873086686">"افزودن کاشی"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"روشن"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"خاموش"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"در دسترس نیست"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"غیرفعال"</string>
<string name="nav_bar" msgid="4642708685386136807">"نوار پیمایش"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"طرحبندی"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"نوع دکمه منتهیالیه چپ"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"اصالتسنجی کردن"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"وارد شدن به دستگاه"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"از اثر انگشت برای باز کردن قفل استفاده کنید"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"اصالتسنجی لازم است. برای اصالتسنجی، حسگر اثر انگشت را لمس کنید."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"تماس تلفنی درحال انجام"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 6fc7855..674a4be 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Puhelin"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ääniapuri"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Avaa lukitus"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Laite lukittu"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Odotetaan sormenjälkeä"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiiliyhteyden hotspot poistettiin käytöstä."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiiliyhteyden hotspot otettiin käyttöön."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ruudun lähetys pysäytettiin."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Työtila keskeytetty."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Työtila on käytössä."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Työtila keskeytetty."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Työtila otettiin käyttöön."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Data Saver poistettiin käytöstä."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Data Saver otettiin käyttöön."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"kiintiö <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> – varoitus"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Työprofiili"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Keskeytetty"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Yövalo"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Auringon laskiessa"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Auringonnousuun"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Täydellinen\nhiljaisuus"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Vain\ntärkeät"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Vain\nherätykset"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu langattomasti • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu nopeasti • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu hitaasti • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Vaihda käyttäjä (nyt <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Nykyinen käyttäjä: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Näytä kaikki"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Avaa lukitus ja maksa"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Ei otettu käyttöön"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Avaa lukitus ja käytä"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Korttien noutamisessa oli ongelma, yritä myöhemmin uudelleen"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lukitusnäytön asetukset"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Työprofiili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lentokonetila"</string>
<string name="add_tile" msgid="6239678623873086686">"Lisää ruutu"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Päällä"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Pois päältä"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Ei käytettävissä"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Ei käytössä"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigointipalkki"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Asettelu"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ylimääräinen vasen painiketyyppi"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"todentaaksesi"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"avataksesi laitteen"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Avaa sormenjäljellä"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 7218b86..7bc9db9 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Appareil photo"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Téléphone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistance vocale"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Déverrouiller"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Appareil verrouillé"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"En attente de l\'empreinte digitale"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Point d\'accès mobile désactivé."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Point d\'accès mobile activé."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Diffusion d\'écran arrêtée."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Mode professionnel interrompu."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Mode Travail activé."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Mode professionnel interrompu."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Le mode Travail est activé."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Mode Économiseur de données désactivé."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Mode Économiseur de données activé."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Avertissement : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Profil professionnel"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Interrompu"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Éclairage nocturne"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activé la nuit"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Jusqu\'à l\'aube"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Aucune\ninterruption"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Priorités\nuniquement"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Alarmes\nuniquement"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"En recharge sans fil : <xliff:g id="PERCENTAGE">%2$s</xliff:g> • Terminée dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"En recharge : <xliff:g id="PERCENTAGE">%2$s</xliff:g> • Terminée dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"En recharge rapide : <xliff:g id="PERCENTAGE">%2$s</xliff:g> • Terminée dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"En recharge lente : <xliff:g id="PERCENTAGE">%2$s</xliff:g> • Terminée <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Changer d\'utilisateur (utilisateur actuel <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Tout afficher"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Déverrouiller pour payer"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Non configuré"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Un problème est survenu lors de la récupération de vos cartes, veuillez réessayer plus tard"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
<string name="add_tile" msgid="6239678623873086686">"Ajouter la tuile"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activé"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Désactivé"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Non disponible"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Désactivé"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barre de navigation"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Disposition"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Type de bouton gauche supplémentaire"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"authentifier"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Servez-vous de votre empreinte digitale pour ouvrir"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index efe1833..d7adaba 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Appareil photo"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Téléphoner"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistance vocale"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Déverrouiller"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Appareil verrouillé"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Attente de l\'empreinte digitale"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Point d\'accès mobile désactivé."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Point d\'accès mobile activé."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Diffusion d\'écran interrompue."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Le mode Travail est en pause."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Mode Travail activé"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Le mode Travail est en pause."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Le mode Travail est activé."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"L\'économiseur de données est désactivé."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"L\'économiseur de données est activé."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> au maximum"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Avertissement : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Profil professionnel"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"En pause"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Éclairage nocturne"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activé la nuit"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Jusqu\'à l\'aube"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Aucune\ninterruption"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Priorité\nuniquement"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Alarmes\nuniquement"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge sans fil • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge rapide • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge lente • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Changer d\'utilisateur (utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Tout afficher"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Déverrouiller pour payer"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Non configuré"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Problème de récupération de vos cartes. Réessayez plus tard"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
<string name="add_tile" msgid="6239678623873086686">"Ajouter un bloc"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activé"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Désactivé"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponible"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Désactivé"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barre de navigation"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Disposition"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Type de bouton gauche supplémentaire"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"s\'authentifier"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilisez votre empreinte pour ouvrir"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d5da5d5..851c79a 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Cámara"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Teléfono"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistente de voz"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Agardando pola impresión dixital"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Desactivouse a zona wifi móbil."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Activouse a zona wifi móbil."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Detívose a emisión en pantalla."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Púxose en pausa o modo de traballo."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de traballo activado."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Púxose en pausa o modo de traballo."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Activouse o modo de traballo."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Desactivouse o aforro de datos."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Activouse o aforro de datos."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advertencia <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de traballo"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"En pausa"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luz nocturna"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activación ao solpor"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Ata o amencer"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silencio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Só\nprioridade"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Só\nalarmas"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando sen fíos • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando rapidamente • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lentamente • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambiar usuario, usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Amosar todo"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloquear para pagar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Sen configurar"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Produciuse un problema ao obter as tarxetas. Téntao de novo máis tarde"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración da pantalla de bloqueo"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de traballo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
<string name="add_tile" msgid="6239678623873086686">"Engade un atallo"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desactivado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Non dispoñible"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Desactivado"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegación"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Deseño"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botón adicional á esquerda"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"poñer o dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa a impresión dixital para abrir"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 7e92ccf..167817c 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"કૅમેરો"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ફોન"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"વૉઇસ સહાય"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"અનલૉક કરો"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ડિવાઇસ લૉક કરેલું છે"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"ફિંગરપ્રિન્ટની રાહ જોઈ રહ્યાં છીએ"</string>
@@ -292,7 +294,7 @@
<!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
<skip />
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"કાર્ય મોડ ચાલુ."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
+ <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (6422896967647049692) -->
<skip />
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"કાર્ય મોડ ચાલુ કર્યો."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ડેટા સેવર બંધ કર્યું."</string>
@@ -473,14 +475,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"સાવ\nશાંતિ"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ફક્ત\nપ્રાધાન્યતા"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"ફક્ત\nએલાર્મ્સ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • વાયરલેસથી ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં ચાર્જ થઈ જશે"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં ચાર્જ થઈ જશે"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ઝડપથી ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં ચાર્જ થઈ જશે"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ધીમેથી ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં ચાર્જ થઈ જશે"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"વપરાશકર્તા સ્વિચ કરો"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"વપરાશકર્તાને સ્વિચ કરો, વર્તમાન વપરાશકર્તા <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"વર્તમાન વપરાશકર્તા <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -750,14 +748,10 @@
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>સ્ટેટસ:</b> સાઇલન્ટ પર અવનત કરવામાં આવ્યું"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>સ્ટેટસ:</b> ઉપલી રેંક આપવામાં આવી"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>સ્ટેટસ:</b> નીચલી રેંક આપવામાં આવી"</string>
- <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
- <skip />
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, બબલ તરીકે દેખાય છે"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, ખલેલ પાડશો નહીં મોડમાં વિક્ષેપ ઊભો કરે છે"</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, બબલ તરીકે દેખાય છે, ખલેલ પાડશો નહીં મોડમાં વિક્ષેપ ઊભો કરે છે"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"સેટિંગ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"પ્રાધાન્યતા"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> વાતચીતની સુવિધાઓને સપોર્ટ આપતી નથી"</string>
@@ -1152,7 +1146,7 @@
<string name="status_before_loading" msgid="1500477307859631381">"ટૂંક સમયમાં કન્ટેન્ટ બતાવવામાં આવશે"</string>
<string name="missed_call" msgid="4228016077700161689">"ચૂકી ગયેલો કૉલ"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"તાજેતરના મેસેજ, ચૂકી ગયેલા કૉલ અને સ્ટેટસ અપડેટ જુઓ"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"તાજેતરના સંદેશા, ચૂકી ગયેલા કૉલ અને સ્ટેટસ અપડેટ જુઓ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"વાતચીત"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કોઈ સંદેશ મોકલવામાં આવ્યો"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કોઈ છબી મોકલવામાં આવી"</string>
@@ -1164,4 +1158,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ખાતરી કરો"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ડિવાઇસ અનલૉક કરો"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ખોલવા માટે ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index cfe2079..1160b9f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"कैमरा"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"फ़ोन"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"आवाज़ से डिवाइस का इस्तेमाल"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"अनलॉक करें"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"डिवाइस लॉक है"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"फ़िंगरप्रिंट का इंतज़ार हो रहा है"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"वर्क मोड को रोका गया."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"वर्क मोड चालू है."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"वर्क मोड को रोका गया."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> सीमा"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"वर्क प्रोफ़ाइल"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"रोका गया है"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"नाइट लाइट"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"शाम को चालू की जाएगी"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"सुबह तक चालू रहेगी"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"पूरी तरह\nशांत"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"केवल\nप्राथमिकता"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"केवल\nअलार्म"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • वायरलेस तरीके से चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • तेज़ चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • धीरे चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"उपयोगकर्ता बदलें, मौजूदा उपयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"मौजूदा उपयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"वॉलेट"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"सभी दिखाएं"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"पैसे चुकाने के लिए, डिवाइस अनलॉक करें"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"सेट अप नहीं किया गया है"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"इस्तेमाल करने के लिए, डिवाइस अनलॉक करें"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"आपके कार्ड की जानकारी पाने में कोई समस्या हुई है. कृपया बाद में कोशिश करें"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लॉक स्क्रीन की सेटिंग"</string>
<string name="status_bar_work" msgid="5238641949837091056">"वर्क प्रोफ़ाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाई जहाज़ मोड"</string>
<string name="add_tile" msgid="6239678623873086686">"टाइल जोड़ें"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"चालू"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"बंद"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"उपलब्ध नहीं है"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"बंद है"</string>
<string name="nav_bar" msgid="4642708685386136807">"नेविगेशन बार"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"लेआउट"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"कुछ और बाएं बटन के प्रकार"</string>
@@ -1123,7 +1115,7 @@
<string name="basic_status" msgid="2315371112182658176">"ऐसी बातचीत जिसमें इंटरैक्शन डेटा मौजूद नहीं है"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"बातचीत विजेट"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"किसी बातचीत को होम स्क्रीन पर जोड़ने के लिए, उस बातचीत पर टैप करें"</string>
- <string name="no_conversations_text" msgid="7362374212649891057">"नए मैसेज पाने के लिए, यहां नज़र रखें"</string>
+ <string name="no_conversations_text" msgid="7362374212649891057">"नए मैसेज आने पर यहां देखें"</string>
<string name="priority_conversations" msgid="3967482288896653039">"अहम बातचीत"</string>
<string name="recent_conversations" msgid="8531874684782574622">"हाल ही में की गई बातचीत"</string>
<string name="okay" msgid="6490552955618608554">"ठीक है"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"पुष्टि करें"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"डिवाइस की होम स्क्रीन पर जाएं"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"खोलने के लिए, फ़िंगरप्रिंट का इस्तेमाल करें"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 9246023..672b7db 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Fotoaparat"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovna pomoć"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Otključavanje"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Čekanje na otisak prsta"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilna žarišna točka isključena."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilna žarišna točka uključena."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Emitiranje zaslona zaustavljeno."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Poslovni je način pauziran."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Način rada uključen."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Poslovni je način pauziran."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Način rada uključen."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Štednja podatkovnog prometa isključena."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Štednja podatkovnog prometa uključena."</string>
@@ -414,8 +414,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ograničenje od <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozorenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Poslovni profil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pauzirano"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Noćno svjetlo"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Uključuje se u suton"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do izlaska sunca"</string>
@@ -475,14 +474,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Potpuna\ntišina"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Samo\nprioritetno"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Samo\nalarmi"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • bežično punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • brzo punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • sporo punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Promjena korisnika, trenutačni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Trenutačan korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -679,12 +674,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Novčanik"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Prikaži sve"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Otključajte da biste platili"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nije postavljeno"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključajte da biste koristili"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pojavio se problem prilikom dohvaćanja kartica, pokušajte ponovo kasnije"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Postavke zaključanog zaslona"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u zrakoplovu"</string>
<string name="add_tile" msgid="6239678623873086686">"Dodavanje pločice"</string>
@@ -876,8 +869,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Uključeno"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Isključeno"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nedostupno"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Onemogućeno"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigacijska traka"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Izgled"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Vrsta dodatnog lijevog gumba"</string>
@@ -1166,4 +1158,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentificirali"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"pristupili uređaju"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 3cfbaba..b17d040 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Hangsegéd"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Feloldás"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Az eszköz zárolva van"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Várakozás az ujjlenyomatra"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"A mobil hotspot kikapcsolva."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"A mobil hotspot bekapcsolva."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"A képernyő átküldése leállítva."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Munkahelyi mód szüneteltetve."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Munka mód be."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Munkahelyi mód szüneteltetve."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Munka mód bekapcsolva."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Adatforgalom-csökkentő kikapcsolva."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Adatforgalom-csökkentő bekapcsolva."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> korlát"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Figyelem! <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Munkaprofil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Szünetel"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Éjszakai fény"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Be: naplemente"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Napfelkeltéig"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Teljes\nnémítás"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Csak\nprioritás"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Csak\nriasztások"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Vezeték nélküli töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Gyors töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lassú töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Felhasználóváltás (a jelenlegi felhasználó: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Jelenlegi felhasználó (<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Összes mutatása"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Feloldás a fizetéshez"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nincs beállítva"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Oldja fel a használathoz"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Probléma merült fel a kártyák lekérésekor, próbálja újra később"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lezárási képernyő beállításai"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Munkahelyi profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Repülős üzemmód"</string>
<string name="add_tile" msgid="6239678623873086686">"Mozaik hozzáadása"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Be"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Ki"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nem használható"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Kikapcsolva"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigációs sáv"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Elrendezés"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"További bal oldali gombtípus"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"a hitelesítéshez"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"eszköz megadásához"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ujjlenyomat használata a megnyitáshoz"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index ad11a26..61ed6e6 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Տեսախցիկ"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Հեռախոս"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ձայնային հուշումներ"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Ապակողպել"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Սարքը կողպված է"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Մատնահետքի սպասում"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Շարժական կապի WiFi ցրիչն անջատվեց:"</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Շարժական կապի WiFi ցրիչը միացավ:"</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Էկրանի հեռարձակումն ընդհատվեց:"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Աշխատանքային ռեժիմի աշխատանքը դադարեցված է։"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Աշխատանքային ռեժիմը միացված է:"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Աշխատանքային ռեժիմը դադարեցված է։"</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Սահմանաչափ՝ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> զգուշացում"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Աշխատանքային պրոֆիլ"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Դադարեցված է"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Գիշերային ռեժիմ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Կմիացվի մայրամուտին"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Մինչև լուսաբաց"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Ընդհանուր\nլուռ վիճակը"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Միայն\nկարևորները"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Միայն\nզարթուցիչ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Անլար լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Արագ լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Դանդաղ լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Անջատել օգտվողին"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Փոխել օգտատիրոջը. ներկայիս օգտատերն է՝ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Ընթացիկ օգտատերը՝ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Դրամապանակ"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Ցույց տալ բոլորը"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Ապակողպել՝ վճարելու համար"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Կարգավորված չէ"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ապակողպել՝ օգտագործելու համար"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Չհաջողվեց բեռնել քարտերը։ Նորից փորձեք։"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Կողպէկրանի կարգավորումներ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Android for Work-ի պրոֆիլ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ավիառեժիմ"</string>
<string name="add_tile" msgid="6239678623873086686">"Սալիկի ավելացում"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Միացված է"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Անջատված է"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Հասանելի չէ"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Անջատված է"</string>
<string name="nav_bar" msgid="4642708685386136807">"Նավիգացիայի գոտի"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Դասավորություն"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Լրացուցիչ ձախ կոճակի տեսակ"</string>
@@ -1123,7 +1115,7 @@
<string name="basic_status" msgid="2315371112182658176">"Բաց զրույց"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"Զրույցի վիջեթներ"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"Հպեք զրույցին՝ այն հիմնական էկրանին ավելացնելու համար"</string>
- <string name="no_conversations_text" msgid="7362374212649891057">"Վերադարձեք այստեղ, երբ որևէ հաղորդագրություն ստանաք"</string>
+ <string name="no_conversations_text" msgid="7362374212649891057">"Վերադարձեք այստեղ, երբ հաղորդագրություններ ստանաք"</string>
<string name="priority_conversations" msgid="3967482288896653039">"Կարևոր զրույցներ"</string>
<string name="recent_conversations" msgid="8531874684782574622">"Վերջին հաղորդագրությունները"</string>
<string name="okay" msgid="6490552955618608554">"Եղավ"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"նույնականացնել"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"նշել սարքը"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Բացելու համար օգտագործեք մատնահետքը"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 1e67b9a..880ca64 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telepon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Bantuan Suara"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Buka kunci"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Perangkat terkunci"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Menunggu sidik jari"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Hotspot seluler dinonaktifkan."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Hotspot seluler diaktifkan."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmisi layar berhenti."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Mode kerja dijeda."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Mode kerja aktif."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Mode kerja dijeda."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Mode kerja diaktifkan."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Penghemat Data nonaktif."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Penghemat Data diaktifkan."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Batas <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Peringatan <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Profil kerja"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Dijeda"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Cahaya Malam"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aktif saat malam"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Sampai pagi"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Senyap\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Hanya\nprioritas"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Hanya\nalarm"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya secara nirkabel • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya dengan cepat • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya dengan lambat • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Beralih pengguna"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Ganti pengguna, pengguna saat ini <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Pengguna saat ini <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Tampilkan semua"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Buka kunci untuk membayar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Belum disiapkan"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Terjadi masalah saat mendapatkan kartu Anda, coba lagi nanti"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setelan layar kunci"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode pesawat"</string>
<string name="add_tile" msgid="6239678623873086686">"Tambahkan ubin"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Aktif"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Nonaktif"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Tidak tersedia"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Nonaktif"</string>
<string name="nav_bar" msgid="4642708685386136807">"Bilah navigasi"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Tata Letak"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Jenis tombol ekstra kiri"</string>
@@ -1148,7 +1140,7 @@
<string name="status_before_loading" msgid="1500477307859631381">"Konten akan segera muncul"</string>
<string name="missed_call" msgid="4228016077700161689">"Panggilan tak terjawab"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"Melihat pesan terbaru, panggilan tak terjawab, dan pembaruan status"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"Lihat pesan terbaru, panggilan tak terjawab, dan pembaruan status"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Percakapan"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> mengirim pesan"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> mengirim gambar"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentikasi"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"masukkan perangkat"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gunakan sidik jari untuk membuka"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 4295b96..eb7e4ec 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Myndavél"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Sími"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Raddaðstoð"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Taka úr lás"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Tækið er læst"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Bíður eftir fingrafari"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Slökkt á farsímaaðgangsstað."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Kveikt á farsímaaðgangsstað."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Skjáútsendingu hætt."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Hlé gert á vinnustillingu."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Kveikt á vinnustillingu."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Hlé gert á vinnustillingu."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Kveikt á vinnustillingu."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Slökkt var á gagnasparnaði."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Kveikt var á gagnasparnaði."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> hámark"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> viðvörun"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Vinnusnið"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Hlé"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Næturljós"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Kveikt við sólsetur"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Til sólarupprásar"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Algjör\nþögn"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Aðeins\nforgangur"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Aðeins\nvekjarar"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Þráðlaus hleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Í hleðslu • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hraðhleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hæg hleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Skipta um notanda; núverandi notandi er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Núverandi notandi er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Veski"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Sýna allt"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Taka úr lás til að greiða"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Ekki uppsett"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Taktu úr lás til að nota"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Vandamál kom upp við að sækja kortin þín. Reyndu aftur síðar"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Stillingar fyrir læstan skjá"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Vinnusnið"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugstilling"</string>
<string name="add_tile" msgid="6239678623873086686">"Bæta reit við"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Kveikt"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Slökkt"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Ekki í boði"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Slökkt"</string>
<string name="nav_bar" msgid="4642708685386136807">"Yfirlitsstika"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Útlit"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Gerð aukahnapps til vinstri"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"auðkenna"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"opna tæki"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Opna með fingrafari"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 78b85c9..600d8b7 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Fotocamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefono"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Sblocca"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloccato"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"In attesa dell\'impronta"</string>
@@ -289,11 +290,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Hotspot mobile disattivato."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Hotspot mobile attivato."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Trasmissione dello schermo interrotta."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modalità Lavoro in pausa."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modalità Lavoro attiva."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Modalità Lavoro messa in pausa."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Modalità Lavoro attivata."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Funzione Risparmio dati disattivata."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Funzione Risparmio dati attivata."</string>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite di <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Avviso <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Profilo di lavoro"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"In pausa"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luminosità notturna"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Attivata al tramonto"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Fino all\'alba"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silenzio\ntotale"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Solo con\npriorità"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Solo\nsveglie"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica wireless • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ricarica veloce • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ricarica lenta • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambia utente, utente corrente <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utente corrente <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Portafoglio"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Espandi"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Sblocca per pagare"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nessuna configurazione"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Sblocca per usare"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Si è verificato un problema durante il recupero delle tue carte. Riprova più tardi."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Impostazioni schermata di blocco"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profilo di lavoro"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modalità aereo"</string>
<string name="add_tile" msgid="6239678623873086686">"Aggiungi riquadro"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"On"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Off"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Non disponibile"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Riquadro disattivato"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra di navigazione"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo di pulsante extra sinistra"</string>
@@ -1123,7 +1114,7 @@
<string name="basic_status" msgid="2315371112182658176">"Apri conversazione"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"Widget di conversazione"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"Tocca una conversazione per aggiungerla alla schermata Home"</string>
- <string name="no_conversations_text" msgid="7362374212649891057">"Torna qui dopo aver ricevuto qualche messaggio"</string>
+ <string name="no_conversations_text" msgid="7362374212649891057">"Torna qui quando avrai ricevuto qualche messaggio"</string>
<string name="priority_conversations" msgid="3967482288896653039">"Conversazioni prioritarie"</string>
<string name="recent_conversations" msgid="8531874684782574622">"Conversazioni recenti"</string>
<string name="okay" msgid="6490552955618608554">"OK"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"effettuare l\'autenticazione"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accedere al dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa l\'impronta per aprire"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticazione obbligatoria. Eseguila toccando il sensore di impronte digitali."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonata in corso"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 1853ecf..c9be358 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"מצלמה"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"טלפון"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"האסיסטנט"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"ארנק"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ביטול נעילה"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"המכשיר נעול"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"בהמתנה לטביעת אצבע"</string>
@@ -289,11 +290,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"נקודת האינטרנט (hotspot) כבויה."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"נקודת האינטרנט (hotspot) מופעלת."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"העברת המסך הופסקה."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"מצב העבודה הושהה."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"מצב עבודה מופעל."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"מצב העבודה הושהה."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"מצב עבודה הופעל."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"חוסך הנתונים (Data Saver) כובה."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"חוסך הנתונים (Data Saver) הופעל."</string>
@@ -416,8 +415,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"הגבלה של <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"אזהרה – <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"פרופיל עבודה"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"בהשהיה"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"תאורת לילה"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"התכונה מופעלת בשקיעה"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"עד הזריחה"</string>
@@ -477,14 +475,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"שקט\nמוחלט"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"הודעות בעדיפות\nבלבד"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"התראות\nבלבד"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה אלחוטית • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה מהירה • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה איטית • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"החלפת משתמש"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"החלפת משתמש. המשתמש הנוכחי: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"משתמש נוכחי <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +676,10 @@
<string name="wallet_title" msgid="5369767670735827105">"ארנק"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"הצגת הכול"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"לביטול הנעילה ולתשלום"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"לא מוגדר"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"יש לבטל את הנעילה כדי להשתמש"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"הייתה בעיה בקבלת הכרטיסים שלך. כדאי לנסות שוב מאוחר יותר"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"הגדרות מסך הנעילה"</string>
<string name="status_bar_work" msgid="5238641949837091056">"פרופיל עבודה"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"מצב טיסה"</string>
<string name="add_tile" msgid="6239678623873086686">"הוספת אריח"</string>
@@ -881,8 +873,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"פועל"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"כבוי"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"לא זמין"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"מושבת"</string>
<string name="nav_bar" msgid="4642708685386136807">"סרגל ניווט"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"פריסה"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"סוג נוסף של לחצן שמאלי"</string>
@@ -1172,4 +1163,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"אימות"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"הזנת מכשיר"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"שימוש בטביעת אצבע כדי לפתוח"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"נדרש אימות. יש לגעת בחיישן טביעות האצבע כדי לבצע אימות."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"מתקיימת שיחה"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 39abcde..6465b44 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"カメラ"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"電話"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"音声アシスト"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"ロック解除"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"デバイスはロックされています"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"指紋を待っています"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"モバイルアクセスポイントをOFFにしました。"</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"モバイルアクセスポイントをONにしました。"</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"画面のキャストが停止しました。"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Work モードを OFF にしました。"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Work モードがオンです。"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Workモードを一時停止しました。"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Work モードをオンにしました。"</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"データセーバーが OFF になりました。"</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"データセーバーが ON になりました。"</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"上限: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"警告: 上限は<xliff:g id="DATA_LIMIT">%s</xliff:g>です"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"仕事用プロファイル"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"OFF"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"夜間モード"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"日の入りに ON"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"日の出まで"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"サイレント\n"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"重要な\n通知のみ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"アラーム\nのみ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ワイヤレス充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 急速充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 低速充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ユーザーを切り替える、現在のユーザーは<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"現在のユーザー: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"ウォレット"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"すべて表示"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ロックを解除して支払う"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"未設定"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ロックを解除して使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"カードの取得中に問題が発生しました。しばらくしてからもう一度お試しください"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ロック画面の設定"</string>
<string name="status_bar_work" msgid="5238641949837091056">"仕事用プロファイル"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"機内モード"</string>
<string name="add_tile" msgid="6239678623873086686">"タイルを追加"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ON"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"OFF"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"使用不可"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"無効"</string>
<string name="nav_bar" msgid="4642708685386136807">"ナビゲーション バー"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"レイアウト"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"その他の左ボタンタイプ"</string>
@@ -1123,7 +1115,7 @@
<string name="basic_status" msgid="2315371112182658176">"空の会話"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"会話ウィジェット"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"会話をタップするとホーム画面に追加されます"</string>
- <string name="no_conversations_text" msgid="7362374212649891057">"メッセージを受信したら、ここでもう一度ご確認ください"</string>
+ <string name="no_conversations_text" msgid="7362374212649891057">"メッセージを受信すると、ここに表示されます"</string>
<string name="priority_conversations" msgid="3967482288896653039">"優先度の高い会話"</string>
<string name="recent_conversations" msgid="8531874684782574622">"最近の会話"</string>
<string name="okay" msgid="6490552955618608554">"OK"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"認証"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"デバイスを入力"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"指紋を使って開いてください"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 2d4d9bc..63205e3 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"კამერა"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ტელეფონი"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ხმოვანი დახმარება"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"განბლოკვა"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"მოწყობილობა ჩაკეტილია"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"თითის ანაბეჭდის მოლოდინში"</string>
@@ -289,11 +290,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"სამსახურის რეჟიმი დაპაუზებულია."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"სამსახურის რეჟიმი ჩართულია."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"სამსახურის რეჟიმი დაპაუზებულია."</string>
<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>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"ლიმიტი: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> გაფრთხილება"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"სამსახურის პროფილი"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"დაპაუზებულია"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"ღამის განათება"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ჩაირთოს მზის ჩასვლისას"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"მზის ამოსვლამდე"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"სრული\nსიჩუმე"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"მხოლოდ\nპრიორიტეტულები"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"მხოლოდ\nგაფრთხილებები"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • უსადენოდ იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • სწრაფად იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ნელა იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომხმარებლის გადართვა"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"მომხმარებლის გდართვა. ამჟამინდელი მომხმარებელი <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"ამჟამინდელი მომხმარებელი <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"ყველას ჩვენება"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"გადასახდელად განბლოკვა"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"არ არის დაყენებული"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"გამოსაყენებლად განბლოკვა"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"თქვენი ბარათების მიღებისას პრობლემა წარმოიშვა. ცადეთ ხელახლა მოგვიანებით"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ჩაკეტილი ეკრანის პარამეტრები"</string>
<string name="status_bar_work" msgid="5238641949837091056">"სამსახურის პროფილი"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"თვითმფრინავის რეჟიმი"</string>
<string name="add_tile" msgid="6239678623873086686">"მოზაიკის დამატება"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ჩართული"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"გამორთვა"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"მიუწვდომელი"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"გათიშულია"</string>
<string name="nav_bar" msgid="4642708685386136807">"ნავიგაციის ზოლი"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"განლაგება"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"მარცხენა დამატებითი ღილაკის ტიპი"</string>
@@ -1127,9 +1118,9 @@
<string name="priority_conversations" msgid="3967482288896653039">"პრიორიტეტული საუბრები"</string>
<string name="recent_conversations" msgid="8531874684782574622">"ბოლო მიმოწერები"</string>
<string name="okay" msgid="6490552955618608554">"კარგი"</string>
- <string name="timestamp" msgid="6577851592534538533">"<xliff:g id="DURATION">%1$s</xliff:g>-ს წინ"</string>
+ <string name="timestamp" msgid="6577851592534538533">"<xliff:g id="DURATION">%1$s</xliff:g>ს წინ"</string>
<string name="less_than_timestamp" msgid="6598972791137724517">"<xliff:g id="DURATION">%1$s</xliff:g>-ზე ნაკლები ხნის წინ"</string>
- <string name="over_timestamp" msgid="4765793502859358634">"<xliff:g id="DURATION">%1$s</xliff:g>-ზე მეტი ხნის წინ"</string>
+ <string name="over_timestamp" msgid="4765793502859358634">"<xliff:g id="DURATION">%1$s</xliff:g>ზე მეტი ხნის წინ"</string>
<string name="birthday_status" msgid="2596961629465396761">"დაბადების დღე"</string>
<string name="birthday_status_content_description" msgid="682836371128282925">"<xliff:g id="NAME">%1$s</xliff:g> დღეს იუბილარია"</string>
<string name="upcoming_birthday_status" msgid="2005452239256870351">"მალე დაბადების დღეა"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ავტორიზაცია"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"მოწყობილობის შეყვანა"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"გასახსნელად გამოიყენეთ თითის ანაბეჭდი"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"საჭიროა ავტორიზაცია. ავტორიზაციისთვის შეეხეთ თითის ანაბეჭდის სენსორს."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"მიმდინარე სატელეფონო ზარი"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index a4cfaaa..bb5d51f 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Камера"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Дауыс көмекшісі"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Бекітпесін ашу"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Құрылғы құлыпталды."</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Саусақ ізі күтілуде"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Жұмыс режимі кідіртілді."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Жұмыс режимі қосулы."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Жұмыс режимі кідіртілді."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> шегі"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> туралы ескерту"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Жұмыс профилі"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Кідіртілген"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Түнгі жарық"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Күн батқанда қосу"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Күн шыққанға дейін"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Толық\nтыныштық"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Тек\nбасымдық"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Тек\nдабылдар"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Сымсыз зарядталуда • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарядталуда • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Жылдам зарядталуда • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Баяу зарядталуда • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Пайдаланушыны ауыстыру"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Пайдаланушыны ауыстыру, ағымдағы пайдаланушы <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Ағымдағы пайдаланушы: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Әмиян"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Барлығын көрсету"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Төлеу үшін құлыпты ашу"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Реттелмеген"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Пайдалану үшін құлыпты ашу"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Карталарыңыз алынбады, кейінірек қайталап көріңіз."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Экран құлпының параметрлері"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жұмыс профилі"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ұшақ режимі"</string>
<string name="add_tile" msgid="6239678623873086686">"Тақтайша қосу"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Қосулы"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Өшірулі"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Қолжетімді емес"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Өшірілген"</string>
<string name="nav_bar" msgid="4642708685386136807">"Шарлау тақтасы"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Формат"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Қосымша сол жақ түйме түрі"</string>
@@ -1124,8 +1116,8 @@
<string name="select_conversation_title" msgid="6716364118095089519">"Әңгіме виджеттері"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"Негізгі экранға қосқыңыз келетін әңгімені түртіңіз."</string>
<string name="no_conversations_text" msgid="7362374212649891057">"Хабарлар алғаннан кейін осында оралыңыз."</string>
- <string name="priority_conversations" msgid="3967482288896653039">"Маңызды чаттар"</string>
- <string name="recent_conversations" msgid="8531874684782574622">"Соңғы чаттар"</string>
+ <string name="priority_conversations" msgid="3967482288896653039">"Маңызды әңгімелер"</string>
+ <string name="recent_conversations" msgid="8531874684782574622">"Соңғы әңгімелер"</string>
<string name="okay" msgid="6490552955618608554">"Жарайды"</string>
<string name="timestamp" msgid="6577851592534538533">"<xliff:g id="DURATION">%1$s</xliff:g> бұрын"</string>
<string name="less_than_timestamp" msgid="6598972791137724517">"Максимум <xliff:g id="DURATION">%1$s</xliff:g> бұрын"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"аутентификациялау"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"құрылғыны енгізу"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ашу үшін саусақ ізін пайдаланыңыз."</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 78f228a..ed7bbe9 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"ម៉ាស៊ីនថត"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ទូរសព្ទ"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ជំនួយសំឡេង"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"ដោះសោ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"បានចាក់សោឧបករណ៍"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"កំពុងរង់ចាំស្នាមម្រាមដៃ"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"បានផ្អាកមុខងារការងារ។"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"បើករបៀបការងារ"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"បានផ្អាកមុខងារការងារ។"</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"ដែនកំណត់ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ការព្រមាន"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"កម្រងព័ត៌មានការងារ"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"បានផ្អាក"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"ពន្លឺពេលយប់"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"បើកនៅពេលថ្ងៃលិច"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"រហូតដល់ពេលថ្ងៃរះ"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ស្ងៀមស្ងាត់\nទាំងស្រុង"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"អាទិភាព\nប៉ុណ្ណោះ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"សំឡេងរោទ៍\nប៉ុណ្ណោះ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្មឥតខ្សែ • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្ម • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្មយឺត • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរអ្នកប្រើ"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ប្ដូរអ្នកប្រើ អ្នកប្រើបច្ចុប្បន្ន <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"អ្នកប្រើបច្ចុប្បន្ន <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"កាបូប"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"បង្ហាញទាំងអស់"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ដោះសោដើម្បីបង់ប្រាក់"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"មិនបានរៀបចំទេ"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ដោះសោដើម្បីប្រើប្រាស់"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"មានបញ្ហាក្នុងការទាញយកកាតរបស់អ្នក សូមព្យាយាមម្ដងទៀតនៅពេលក្រោយ"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ការកំណត់អេក្រង់ចាក់សោ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ប្រវត្តិរូបការងារ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ពេលជិះយន្តហោះ"</string>
<string name="add_tile" msgid="6239678623873086686">"បន្ថែមក្រឡាល្អិត"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"បើក"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"បិទ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"មិនមាន"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"បានបិទ"</string>
<string name="nav_bar" msgid="4642708685386136807">"របាររុករក"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ប្លង់"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ប្រភេទប៊ូតុងខាងឆ្វេងបន្ថែម"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ផ្ទៀងផ្ទាត់"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"បញ្ចូលឧបករណ៍"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ប្រើស្នាមម្រាមដៃ ដើម្បីបើក"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 3cb20af..cef9e3f 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"ಕ್ಯಾಮರಾ"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ಫೋನ್"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ಧ್ವನಿ ಸಹಾಯಕ"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"ಅನ್ಲಾಕ್"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ಸಾಧನ ಲಾಕ್ ಆಗಿದೆ"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ಗಾಗಿ ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"ಕೆಲಸದ ಮೋಡ್ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"ಕೆಲಸದ ಮೋಡ್ ಆನ್ ಆಗಿದೆ."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"ಕೆಲಸದ ಮೋಡ್ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಮಿತಿ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಎಚ್ಚರಿಕೆ"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"ನೈಟ್ ಲೈಟ್"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ಸೂರ್ಯಾಸ್ತದಲ್ಲಿ"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ಸೂರ್ಯೋದಯದವರೆಗೆ"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ಸಂಪೂರ್ಣ\nನಿಶ್ಯಬ್ಧ"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ಆದ್ಯತೆ\nಮಾತ್ರ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"ಅಲಾರಮ್ಗಳು\nಮಾತ್ರ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ವೈರ್ಲೆಸ್ ಆಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ವೇಗವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ, ಪ್ರಸ್ತುತ ಬಳಕೆದಾರ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"<xliff:g id="CURRENT_USER_NAME">%s</xliff:g> ಪ್ರಸ್ತುತ ಬಳಕೆದಾರ"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"ವಾಲೆಟ್"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"ಎಲ್ಲವನ್ನೂ ತೋರಿಸಿ"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ಪಾವತಿಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"ಇನ್ನೂ ಸೆಟಪ್ ಮಾಡಿಲ್ಲ"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ಬಳಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ನಿಮ್ಮ ಕಾರ್ಡ್ಗಳನ್ನು ಪಡೆಯುವಾಗ ಸಮಸ್ಯೆ ಉಂಟಾಗಿದೆ, ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ಲಾಕ್ ಸ್ಕ್ರ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ಏರ್ಪ್ಲೇನ್ ಮೋಡ್"</string>
<string name="add_tile" msgid="6239678623873086686">"ಟೈಲ್ ಸೇರಿಸಿ"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ಆನ್"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ಆಫ್"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ಲಭ್ಯವಿಲ್ಲ"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="nav_bar" msgid="4642708685386136807">"ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ಲೇಔಟ್"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ಹೆಚ್ಚುವರಿ ಎಡ ಬಟನ್ ವಿಧ"</string>
@@ -1144,7 +1136,7 @@
<string name="audio_status" msgid="4237055636967709208">"ಆಲಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="game_status" msgid="1340694320630973259">"ಪ್ಲೇ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="empty_user_name" msgid="3389155775773578300">"ಸ್ನೇಹಿತರು"</string>
- <string name="empty_status" msgid="5938893404951307749">"ಈ ರಾತ್ರಿ ಚಾಟ್ ಮಾಡೋಣ!"</string>
+ <string name="empty_status" msgid="5938893404951307749">"ರಾತ್ರಿ ಚಾಟ್ ಮಾಡೋಣ!"</string>
<string name="status_before_loading" msgid="1500477307859631381">"ವಿಷಯ ಶೀಘ್ರದಲ್ಲೇ ಕಾಣಿಸಿಕೊಳ್ಳುತ್ತವೆ"</string>
<string name="missed_call" msgid="4228016077700161689">"ಮಿಸ್ಡ್ ಕಾಲ್"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ದೃಢೀಕರಿಸಿ"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ಸಾಧನವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ತೆರೆಯುವುದಕ್ಕಾಗಿ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಬಳಸಿ"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 5f6bbcb..ea45bd9 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"카메라"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"전화"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"음성 지원"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"잠금 해제"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"기기 잠김"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"지문 대기 중"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"직장 모드가 일시중지되어 있습니다."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"작업 모드가 사용 설정되었습니다."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"직장 모드가 일시중지되어 있습니다."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"한도: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 경고"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"직장 프로필"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"일시중지됨"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"야간 조명"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"일몰에"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"일출까지"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"모두\n차단"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"중요 알림만\n허용"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"알람만\n"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 무선 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 고속 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 저속 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"사용자 전환"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"사용자 전환, 현재 사용자 <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"현재 사용자: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"모두 표시"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"잠금 해제하여 결제"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"설정되지 않음"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"잠금 해제하여 사용"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"카드를 가져오는 중에 문제가 발생했습니다. 나중에 다시 시도해 보세요."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"잠금 화면 설정"</string>
<string name="status_bar_work" msgid="5238641949837091056">"직장 프로필"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"비행기 모드"</string>
<string name="add_tile" msgid="6239678623873086686">"타일 추가"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"사용"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"사용 안함"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"사용할 수 없음"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"사용 안함"</string>
<string name="nav_bar" msgid="4642708685386136807">"탐색 메뉴"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"레이아웃"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"추가 왼쪽 버튼 유형"</string>
@@ -1144,7 +1136,7 @@
<string name="audio_status" msgid="4237055636967709208">"듣는 중"</string>
<string name="game_status" msgid="1340694320630973259">"플레이 중"</string>
<string name="empty_user_name" msgid="3389155775773578300">"친구"</string>
- <string name="empty_status" msgid="5938893404951307749">"오늘 밤에 채팅"</string>
+ <string name="empty_status" msgid="5938893404951307749">"오늘 밤에 채팅하자!"</string>
<string name="status_before_loading" msgid="1500477307859631381">"곧 콘텐츠가 표시됩니다."</string>
<string name="missed_call" msgid="4228016077700161689">"부재중 전화"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"인증"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"기기 입력"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"지문으로 열기"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 90b1600..d2db2c3 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Камера"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Үн жардамчысы"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Кулпусун ачуу"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Түзмөк кулпуланды"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Манжаңызды сенсорго коюңуз"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Иштөө режими тындырылган."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Иштөө режими күйүк."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Иштөө режими тындырылган."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> чектөө"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> эскертүү"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Жумуш профили"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Тындырылган"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Түнкү режим"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Күн батканда күйөт"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Күн чыкканга чейин"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Тым-\nтырс"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Артыкчылыктуу\nгана"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Ойготкучтар\nгана"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зымсыз кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Тез кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Жай кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Колдонуучуну күйгүзүү, учурдагы колдонуучу <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Учурдагы колдонуучу <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Баарын көрсөтүү"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Төлөө үчүн кулпусун ачыңыз"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Жөндөлгөн эмес"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Колдонуу үчүн кулпусун ачыңыз"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Кыйытмаларды алууда ката кетти. Бир аздан кийин кайталап көрүңүз."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Кулпуланган экран жөндөөлөрү"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жумуш профили"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Учак режими"</string>
<string name="add_tile" msgid="6239678623873086686">"Тайл кошуу"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Күйүк"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Өчүк"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Жеткиликсиз"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Өчүрүлгөн"</string>
<string name="nav_bar" msgid="4642708685386136807">"Чабыттоо тилкеси"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Калып"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Сол жактагы кошумча баскычтын түрү"</string>
@@ -1124,12 +1116,12 @@
<string name="select_conversation_title" msgid="6716364118095089519">"Сүйлөшүүлөр виджеттери"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"Сүйлөшүүнү башкы экранга кошуу үчүн таптап коюңуз"</string>
<string name="no_conversations_text" msgid="7362374212649891057">"Билдирүүлөрдү алгандан кийин бул жерди кайрадан текшериңиз"</string>
- <string name="priority_conversations" msgid="3967482288896653039">"Маанилүү жазышуулар"</string>
- <string name="recent_conversations" msgid="8531874684782574622">"Акыркы жазышуулар"</string>
+ <string name="priority_conversations" msgid="3967482288896653039">"Маанилүү сүйлөшүүлөр"</string>
+ <string name="recent_conversations" msgid="8531874684782574622">"Акыркы сүйлөшүүлөр"</string>
<string name="okay" msgid="6490552955618608554">"Макул"</string>
<string name="timestamp" msgid="6577851592534538533">"<xliff:g id="DURATION">%1$s</xliff:g> мурда"</string>
<string name="less_than_timestamp" msgid="6598972791137724517">"<xliff:g id="DURATION">%1$s</xliff:g> жетпеген убакыт мурда"</string>
- <string name="over_timestamp" msgid="4765793502859358634">"<xliff:g id="DURATION">%1$s</xliff:g> ашуун мурда"</string>
+ <string name="over_timestamp" msgid="4765793502859358634">"<xliff:g id="DURATION">%1$s</xliff:g> мурун"</string>
<string name="birthday_status" msgid="2596961629465396761">"Туулган күн"</string>
<string name="birthday_status_content_description" msgid="682836371128282925">"<xliff:g id="NAME">%1$s</xliff:g> бүгүн туулган күнүн белгилеп жатат"</string>
<string name="upcoming_birthday_status" msgid="2005452239256870351">"Алдыдагы туулган күн"</string>
@@ -1148,7 +1140,7 @@
<string name="status_before_loading" msgid="1500477307859631381">"Мазмун бир аздан кийин көрүнөт"</string>
<string name="missed_call" msgid="4228016077700161689">"Жооп берилбеген чалуу"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"Акыркы билдирүүлөрдү, жооп берилбеген чалууларды жана статус жаңыртууларын көрүү"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"Акыркы билдирүүлөрдү, жооп берилбеген чалууларды жана статустардын жаңырганын көрөсүз"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Сүйлөшүү"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> билдирүү жөнөттү"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> сүрөт жөнөттү"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"аныктыгын текшерүү"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"түзмөккө кирүү"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Манжаңыздын изи менен ачыңыз"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 91bdc6e..c162c54 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"ກ້ອງ"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ໂທລະສັບ"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ຊ່ວຍເຫຼືອທາງສຽງ"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"ປົດລັອກ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ອຸປະກອນຖືກລັອກໄວ້"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"ກຳລັງລໍຖ້າລາຍນິ້ວມື"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"ຢຸດໂໝດວຽກຊົ່ວຄາວແລ້ວ."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"ໂໝດການເຮັດວຽກເປີດຢູ່."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"ຢຸດໂໝດວຽກໄວ້ຊົ່ວຄາວແລ້ວ."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"ຈຳກັດ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"ຄຳເຕືອນ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"ຢຸດຊົ່ວຄາວແລ້ວ"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"ແສງກາງຄືນ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ເປີດຕອນຕາເວັນຕົກ"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ຈົນກວ່າຕາເວັນຂຶ້ນ"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ຄວາມງຽບ\nທັງໝົດ"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ບຸລິມະສິດ\nເທົ່ານັ້ນ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"ໂມງປຸກ\nເທົ່ານັ້ນ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບໄຮ້ສາຍ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບໄວ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບຊ້າ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ປ່ຽນຜູ່ໃຊ້, ຜູ່ໃຊ້ປະຈຸບັນ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"ຜູ້ໃຊ້ປະຈຸບັນ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"ກະເປົາ"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"ສະແດງທັງໝົດ"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ປົດລັອກເພື່ອຈ່າຍ"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"ບໍ່ໄດ້ຕັ້ງຄ່າ"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ປົດລັອກເພື່ອໃຊ້"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ເກີດບັນຫາໃນການໂຫຼດບັດຂອງທ່ານ, ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ການຕັ້ງຄ່າໜ້າຈໍລັອກ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ໂໝດເຮືອບິນ"</string>
<string name="add_tile" msgid="6239678623873086686">"ເພີ່ມລາຍຕາກະໂລ່"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ເປີດ"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ປິດ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ບໍ່ສາມາດໃຊ້ໄດ້"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"ປິດການນຳໃຊ້ແລ້ວ"</string>
<string name="nav_bar" msgid="4642708685386136807">"ແຖບນຳທາງ"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ຮູບແບບ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ປະເພດປຸ່ມຊ້າຍພິເສດ"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ພິສູດຢືນຢັນ"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ເຂົ້າອຸປະກອນ"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ໃຊ້ລາຍນິ້ວມືເພື່ອເປີດ"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 407db29..3a9ccf0 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Fotoaparatas"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefonas"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Atrakinti"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Įrenginys užrakintas"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Laukiama piršto antspaudo"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiliojo ryšio viešosios interneto prieigos taškas išjungtas."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiliojo ryšio viešosios interneto prieigos taškas įjungtas."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekrano perdavimas sustabdytas."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Darbo režimas pristabdytas."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Darbo režimas įjungtas."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Darbo režimas pristabdytas."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Darbo režimas įjungtas."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Duomenų taupymo priemonė išjungta."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Duomenų taupymo priemonė įjungta."</string>
@@ -416,8 +416,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limitas: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> įspėjimas"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Darbo profilis"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pristabdyta"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nakties šviesa"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Per saulėlydį"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Iki saulėtekio"</string>
@@ -477,14 +476,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Visiška\ntyla"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Tik\nprioritetiniai"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Tik\nsignalai"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Įkraunama be laidų • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sparčiai įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lėtai įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Perjungti naudotoją, dabartinis naudotojas <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Dabartinis naudotojas <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +677,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Piniginė"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Rodyti viską"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Atrakinti, kad būtų galima mokėti"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nenustatyta"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Atrakinti, kad būtų galima naudoti"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Gaunant korteles kilo problema, bandykite dar kartą vėliau"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Užrakinimo ekrano nustatymai"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darbo profilis"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lėktuvo režimas"</string>
<string name="add_tile" msgid="6239678623873086686">"Pridėti išklotinės elementą"</string>
@@ -881,8 +874,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Įjungta"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Išjungta"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nepasiekiama"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Išjungta"</string>
<string name="nav_bar" msgid="4642708685386136807">"Naršymo juosta"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Išdėstymas"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Papildomo mygtuko kairėje tipas"</string>
@@ -1172,4 +1164,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"nustatytumėte tapatybę"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"pasiektumėte įrenginį"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Naudokite kontrolinį kodą, kad atidarytumėte"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 307f0b6..c583cba 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Tālruņa numurs"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Balss palīgs"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Atbloķēt"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Ierīce ir bloķēta"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Tiek gaidīts pirksta nospiedums."</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilais tīklājs ir izslēgts."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilais tīklājs ir ieslēgts."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekrāna apraidīšana ir apturēta."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Darba režīms pārtraukts."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Darba režīms ir ieslēgts."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Darba režīms ir pārtraukts."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Darba režīms ir ieslēgts."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Datu lietojuma samazinātājs ir izslēgts."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Datu lietojuma samazinātājs ir ieslēgts."</string>
@@ -414,8 +414,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ierobežojums: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> brīdinājums"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Darba profils"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pārtraukts"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nakts režīms"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Saulrietā"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Līdz saullēktam"</string>
@@ -475,14 +474,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Pilnīgs\nklusums"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Tikai\nprioritārie"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Tikai\nsignāli"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bezvadu uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Notiek uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ātrā uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lēnā uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Pārslēgt lietotāju; pašreizējais lietotājs: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Pašreizējais lietotājs: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -679,12 +674,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Maks"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Rādīt visu"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Lai maksātu, atbloķējiet ekrānu"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nav iestatīts"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lai izmantotu, atbloķējiet ekrānu"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ienesot jūsu kartes, radās problēma. Lūdzu, vēlāk mēģiniet vēlreiz."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Bloķēšanas ekrāna iestatījumi"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darba profils"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lidojuma režīms"</string>
<string name="add_tile" msgid="6239678623873086686">"Pievienot elementu"</string>
@@ -876,8 +869,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ieslēgts"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Izslēgts"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nav pieejams"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Atspējots"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigācijas josla"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Izkārtojums"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Kreisās puses papildu pogas veids"</string>
@@ -1166,4 +1158,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"veiktu autentificēšanu"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"izmantotu ierīci"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Atvēršanai izmantojiet pirksta nospiedumu"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index c4524f3..d306e1f 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Камера"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Гласовна помош"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Отклучување"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Уредот е заклучен"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Се чека отпечаток"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Режимот на работа е паузиран."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Режимот на работа е вклучен."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Режимот на работа е паузиран."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Лимит: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Предупредување: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Работен профил"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Паузиран"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ноќно светло"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Вклуч. на зајдисонце"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"До изгрејсонце"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Целосна\nтишина"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Само\nприоритетни"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Само\nаларми"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни безжично • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни брзо • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни бавно • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Промени го корисникот, тековен корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Тековен корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Паричник"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Прикажи ги сите"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Отклучете за да платите"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Не е поставено"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Отклучете за да користите"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Имаше проблем при преземањето на картичките. Обидете се повторно подоцна"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Поставки за заклучен екран"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Работен профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Авионски режим"</string>
<string name="add_tile" msgid="6239678623873086686">"Додај плочка"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Вклучено"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Исклучено"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Недостапно"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Оневозможено"</string>
<string name="nav_bar" msgid="4642708685386136807">"Лента за навигација"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Распоред"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Тип дополнително лево копче"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"автентицирате"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"внесете уред"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Користете отпечаток за да се отвори"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index e793e19b..750d834 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"ക്യാമറ"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ഫോണ്"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"വോയ്സ് സഹായം"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"അണ്ലോക്ക് ചെയ്യുക"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ഉപകരണം ലോക്ക് ചെയ്തു"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"ഫിംഗർപ്രിന്റിനായി കാത്തിരിക്കുന്നു"</string>
@@ -289,10 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"പ്രവർത്തന മോഡ് താൽക്കാലികമായി നിർത്തി."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"പ്രവർത്തന മോഡ് ഓണാണ്."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
+ <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (6422896967647049692) -->
<skip />
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"പ്രവർത്തന മോഡ് ഓണാക്കി."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ഡാറ്റ സേവർ ഓഫാക്കി."</string>
@@ -412,8 +413,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> പരിധി"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> മുന്നറിയിപ്പ്"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"താൽക്കാലികമായി നിർത്തി"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"നൈറ്റ് ലൈറ്റ്"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"സൂര്യാസ്തമയത്തിന്"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"സൂര്യോദയം വരെ"</string>
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"പൂർണ്ണ\nനിശബ്ദത"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"മുൻഗണന\nമാത്രം"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"അലാറങ്ങൾ\nമാത്രം"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • വയർലെസ്സ് ആയി ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • വേഗത്തിൽ ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • പതുക്കെ ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ഉപയോക്താവ് മാറുക"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ഉപയോക്താവിനെ മാറ്റുക, <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> എന്നയാളാണ് നിലവിലുള്ള ഉപയോക്താവ്"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"നിലവിലെ ഉപയോക്താവ് <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +672,10 @@
<string name="wallet_title" msgid="5369767670735827105">"വാലറ്റ്"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"എല്ലാം കാണിക്കുക"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"പണമടയ്ക്കാൻ അൺലോക്ക് ചെയ്യുക"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"സജ്ജീകരിച്ചിട്ടില്ല"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ഉപയോഗിക്കാൻ അൺലോക്ക് ചെയ്യുക"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"നിങ്ങളുടെ കാർഡുകൾ ലഭ്യമാക്കുന്നതിൽ ഒരു പ്രശ്നമുണ്ടായി, പിന്നീട് വീണ്ടും ശ്രമിക്കുക"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ലോക്ക് സ്ക്രീൻ ക്രമീകരണം"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ഫ്ലൈറ്റ് മോഡ്"</string>
<string name="add_tile" msgid="6239678623873086686">"ടൈൽ ചേർക്കുക"</string>
@@ -871,8 +865,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ഓൺ"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ഓഫ്"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ലഭ്യമല്ല"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="nav_bar" msgid="4642708685386136807">"നാവിഗേഷൻ ബാർ"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ലേഔട്ട്"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"അധിക ഇടത് ബട്ടൺ തരം"</string>
@@ -1144,11 +1137,11 @@
<string name="audio_status" msgid="4237055636967709208">"കേൾക്കുന്നു"</string>
<string name="game_status" msgid="1340694320630973259">"കളിക്കുന്നു"</string>
<string name="empty_user_name" msgid="3389155775773578300">"സുഹൃത്തുക്കൾ"</string>
- <string name="empty_status" msgid="5938893404951307749">"ഇന്നുരാത്രി ചാറ്റുചെയ്യാം!"</string>
+ <string name="empty_status" msgid="5938893404951307749">"ഇന്നുരാത്രി ചാറ്റ് ചെയ്യാം!"</string>
<string name="status_before_loading" msgid="1500477307859631381">"ഉള്ളടക്കം ഉടൻ ദൃശ്യമാകും"</string>
<string name="missed_call" msgid="4228016077700161689">"മിസ്ഡ് കോൾ"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"അടുത്തിടെയുള്ള സന്ദേശങ്ങൾ, മിസ്ഡ് കോളുകൾ, സ്റ്റാറ്റസ് അപ്ഡേറ്റുകൾ എന്നിവ കാണുക"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"അടുത്തിടെയുള്ള സന്ദേശങ്ങൾ, മിസ്ഡ് കോൾ, സ്റ്റാറ്റസ് അപ്ഡേറ്റുകൾ എന്നിവ കാണൂ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"സംഭാഷണം"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g>, ഒരു സന്ദേശം അയച്ചു"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g>, ഒരു ചിത്രം അയച്ചു"</string>
@@ -1160,4 +1153,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"പരിശോധിച്ചുറപ്പിക്കുക"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ഉപകരണം നൽകുക"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"തുറക്കുന്നതിന് നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 7001a1a..583b083 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Камер"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Утас"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Дуут туслах"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Тайлах"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Төхөөрөмжийг түгжсэн"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Хурууны хээг хүлээж байна"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Ажлыг горимыг түр зогсоосон."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Ажлын горимыг асаасан."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Ажлыг горимыг түр зогсоосон."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> хязгаар"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> анхааруулга"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Ажлын профайл"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Түр зогсоосон"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Шөнийн гэрэл"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Нар жаргах үед"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Нар мандах хүртэл"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Дуугүй\nболгох"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Зөвхөн\nхамгийн чухлыг"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Зөвхөн\nсэрүүлэг"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Утасгүй цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Хурдтай цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Удаан цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Хэрэглэгчийг сэлгэх, одоогийн хэрэглэгч <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Одоогийн хэрэглэгч <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Түрийвч"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Бүгдийг харуулах"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Төлөхийн тулд түгжээг тайлна уу"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Тохируулаагүй"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ашиглахын тулд түгжээг тайлах"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Таны картыг авахад асуудал гарлаа. Дараа дахин оролдоно уу"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Түгжигдсэн дэлгэцийн тохиргоо"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ажлын профайл"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Нислэгийн горим"</string>
<string name="add_tile" msgid="6239678623873086686">"Вебсайтын цонх нэмэх"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Идэвхтэй"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Идэвхгүй"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Боломжгүй"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Идэвхгүй болгосон"</string>
<string name="nav_bar" msgid="4642708685386136807">"Навигацын самбар"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Бүдүүвч"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Нэмэлт зүүн товчлуураар шивэх"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"баталгаажуулах"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"төхөөрөмж оруулах"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Нээхийн тулд хурууны хээг ашиглана уу"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index f26bf31..8fe5af0 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"कॅमेरा"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"फोन"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"व्हॉइस सहाय्य"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"अनलॉक करा"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"डिव्हाइस लॉक केले"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"फिंगरप्रिंटची प्रतीक्षा करत आहे"</string>
@@ -289,10 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"कार्य मोड थांबवला."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"कार्य मोड सुरू."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
+ <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (6422896967647049692) -->
<skip />
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"कार्य मोड सुरू केला."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"डेटा सर्व्हर बंद केला."</string>
@@ -412,8 +413,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> मर्यादा"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावणी"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"कार्य प्रोफाइल"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"थाबंवला"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"रात्रीचा प्रकाश"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"संध्याकाळी सुरू असते"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"सूर्योदयापर्यंत"</string>
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"संपूर्ण\nशांतता"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"केवळ\nप्राधान्य"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"केवळ\nअलार्म"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • वायरलेस पद्धतीने चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • वेगाने चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • हळू चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"वापरकर्ता स्विच करा"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"वापरकर्ता स्विच करा, वर्तमान वापरकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"वर्तमान वापरकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +672,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"सर्व दाखवा"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"पैसे देण्यासाठी अनलॉक करा"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"सेट केलेले नाही"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"वापरण्यासाठी अनलॉक करा"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"तुमची कार्ड मिळवताना समस्या आली, कृपया नंतर पुन्हा प्रयत्न करा"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लॉक स्क्रीन सेटिंग्ज"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाईल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"विमान मोड"</string>
<string name="add_tile" msgid="6239678623873086686">"टाइल जोडा"</string>
@@ -871,8 +865,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"सुरू"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"बंद"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"उपलब्ध नाही"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"बंद केली"</string>
<string name="nav_bar" msgid="4642708685386136807">"नॅव्हिगेशन बार"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"लेआउट"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"अतिरिक्त डाव्या बटणाचा प्रकार"</string>
@@ -1144,7 +1137,7 @@
<string name="audio_status" msgid="4237055636967709208">"ऐकत आहे"</string>
<string name="game_status" msgid="1340694320630973259">"प्ले करत आहे"</string>
<string name="empty_user_name" msgid="3389155775773578300">"मित्रमैत्रिणी"</string>
- <string name="empty_status" msgid="5938893404951307749">"चला, आज रात्री चॅट करूया!"</string>
+ <string name="empty_status" msgid="5938893404951307749">"आज रात्री चॅट करू या"</string>
<string name="status_before_loading" msgid="1500477307859631381">"आशय लवकरच दाखवला जाईल"</string>
<string name="missed_call" msgid="4228016077700161689">"मिस्ड कॉल"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
@@ -1160,4 +1153,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ऑथेंटिकेट करा"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"डिव्हाइस एंटर करा"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"उघडण्यासाठी फिंगरप्रिंट वापरा"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 837d900..791863a 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Bantuan Suara"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Buka kunci"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Peranti dikunci"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Menunggu cap jari"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Tempat liputan mudah alih bergerak dimatikan."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Tempat liputan mudah alih bergerak dihidupkan."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Penghantaran skrin dihentikan."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Mod kerja dijeda."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Mod kerja hidup."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Mod kerja dijeda."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Mod kerja dihidupkan."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Penjimat Data dimatikan."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Penjimat Data dihidupkan."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> had"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Amaran <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Profil kerja"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Dijeda"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Cahaya Malam"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Dihidupkan pd senja"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hingga matahari terbit"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Senyap\nsepenuhnya"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Keutamaan\nsahaja"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Penggera\nsahaja"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas secara wayarles • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas dengan cepat • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas dengan perlahan • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Tukar pengguna, pengguna semasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Pengguna semasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Tunjukkan semua"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Buka kunci untuk membayar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Tidak disediakan"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Terdapat masalah sewaktu mendapatkan kad anda. Sila cuba sebentar lagi"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Tetapan skrin kunci"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod pesawat"</string>
<string name="add_tile" msgid="6239678623873086686">"Tambahkan jubin"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Hidup"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Mati"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Tidak tersedia"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Dilumpuhkan"</string>
<string name="nav_bar" msgid="4642708685386136807">"Bar navigasi"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Reka letak"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Jenis butang kiri tambahan"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"sahkan"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"akses peranti"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gunakan cap jari untuk membuka"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 1756d28..769c962 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"ကင်မရာ"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ဖုန်း"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"အသံ အကူအညီ"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"သော့ဖွင့်ရန်"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"စက်ပစ္စည်းကို လော့ခ်ချထားသည်"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"လက်ဗွေကို စောင့်နေသည်"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"အလုပ်မုဒ် ခဏရပ်ထားသည်။"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"အလုပ် မုဒ်ကို ဖွင့်ထားပါသည်။"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"အလုပ်မုဒ် ခဏရပ်ထားသည်။"</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ကန့်သတ်ချက်"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> သတိပေးချက်"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"အလုပ်ပရိုဖိုင်"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"ခဏရပ်ထားသည်"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"ညအလင်းရောင်"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"နေဝင်ချိန်၌ ဖွင့်ရန်"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"နေထွက်ချိန် အထိ"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"လုံးဝ\nတိတ်ဆိတ်ခြင်း"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ဦးစားပေးမှု\nသာ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"နှိုးစက်များ\nသာ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ကြိုးမဲ့အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • အမြန်အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • နှေးကွေးစွာ အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"အသုံးပြုသူကို ပြောင်းရန်၊ လက်ရှိ အသုံးပြုသူ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"လတ်တလော သုံးစွဲသူ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"အားလုံးပြရန်"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ငွေပေးချေရန် လော့ခ်ဖွင့်ပါ"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"စနစ် ထည့်သွင်းမထားပါ"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"သုံးရန် လော့ခ်ဖွင့်ပါ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"သင်၏ကတ်များ ရယူရာတွင် ပြဿနာရှိနေသည်၊ နောက်မှ ထပ်စမ်းကြည့်ပါ"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"လော့ခ်မျက်နှာပြင် ဆက်တင်များ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"အလုပ် ပရိုဖိုင်"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"လေယာဉ်ပျံမုဒ်"</string>
<string name="add_tile" msgid="6239678623873086686">"လေးထောင့်ကွက် ထည့်ရန်"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ဖွင့်"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ပိတ်"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"မရနိုင်ပါ"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"ပိတ်ထားသည်"</string>
<string name="nav_bar" msgid="4642708685386136807">"ရွှေ့လျားရန်ဘားတန်း"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"အပြင်အဆင်"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"လက်ဝဲခလုတ် အမျိုးအစားအပို"</string>
@@ -1123,7 +1115,7 @@
<string name="basic_status" msgid="2315371112182658176">"စကားဝိုင်းကို ဖွင့်ရန်"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"စကားဝိုင်း ဝိဂျက်များ"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"စကားဝိုင်းကို သင်၏ ‘ပင်မစာမျက်နှာ’ သို့ထည့်ရန် တို့ပါ"</string>
- <string name="no_conversations_text" msgid="7362374212649891057">"မက်ဆေ့ဂျ်အချို့ရသည်နှင့် ဤနေရာမှာ ပြန်စစ်ဆေးပါ"</string>
+ <string name="no_conversations_text" msgid="7362374212649891057">"မက်ဆေ့ဂျ်အချို့ရလျှင် ဤနေရာသို့ ပြန်လာကြည့်ပါ"</string>
<string name="priority_conversations" msgid="3967482288896653039">"ဦးစားပေး စကားဝိုင်းများ"</string>
<string name="recent_conversations" msgid="8531874684782574622">"မကြာသေးမီက စကားဝိုင်းများ"</string>
<string name="okay" msgid="6490552955618608554">"OK"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"အထောက်အထားစိစစ်ရန်"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"စက်ပစ္စည်းသို့ ဝင်ရန်"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ဖွင့်ရန် လက်ဗွေကို သုံးပါ"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index fa6e62e..c0aefb0 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefonnummer"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Talehjelp"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Lås opp"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheten er låst"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Venger på fingeravtrykk"</string>
@@ -289,11 +290,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobil Wi-Fi-sone er slått av."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobil Wi-Fi-sone er slått på."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Skjermcastingen er stoppet."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Arbeidsmodus er satt på pause."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Arbeidsmodusen er på."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Arbeidsmodus er satt på pause."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Arbeidsmodusen er slått på."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Datasparing er slått av."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Datasparing er slått på."</string>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Grense på <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advarsel for <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Jobbprofil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Satt på pause"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nattlys"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"På ved solnedgang"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Til soloppgang"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Total\nstillhet"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Bare\nPrioritet"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Bare\nalarmer"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader trådløst • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader raskt • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader sakte • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Bytt bruker, gjeldende bruker er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Gjeldende bruker: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Vis alle"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Lås opp for å betale"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Ikke konfigurert"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås opp for å bruke"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Det oppsto et problem med henting av kortene. Prøv igjen senere"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Innstillinger for låseskjermen"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work-profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flymodus"</string>
<string name="add_tile" msgid="6239678623873086686">"Legg til felt"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"På"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Av"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Utilgjengelig"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Slått av"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigasjonsrad"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Oppsett"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra venstre-knapptype"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentiser"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"åpne enheten"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Bruk fingeravtrykk for å åpne"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering kreves. Trykk på fingeravtrykkssensoren for å autentisere."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Pågående telefonsamtale"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 7c1e919..de375d3 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"क्यामेरा"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"फोन"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"आवाज सहायता"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"खोल्नुहोस्"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"यन्त्र लक गरिएको छ"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"फिंगरप्रिन्ट कुर्दै"</string>
@@ -289,10 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"कामसम्बन्धी मोड पज गरियो।"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"कार्य मोड अन।"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
+ <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (6422896967647049692) -->
<skip />
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"कार्य मोड सक्रिय भयो।"</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"डेटा सेभरलाई निष्क्रिय पारियो।"</string>
@@ -412,8 +413,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> सीमा"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी दिँदै"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"कार्य प्रोफाइल"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"पज गरियो"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"सूर्यास्तमा सक्रिय"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"सूर्योदयसम्म"</string>
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"पूरै\nशान्त"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"प्राथमिकता \nमात्र"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"अलार्महरू \nमात्र"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • तारविनै चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा पूरै चार्ज हुन्छ"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा पूरै चार्ज हुन्छ"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • छिटो चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा पूरै चार्ज हुन्छ"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • बिस्तारै चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा पूरै चार्ज हुन्छ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"प्रयोगकर्ता फेर्नुहोस्"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"प्रयोगकर्ता, हालको प्रयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> मा स्विच गर्नुहोस्"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"हालको प्रयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +672,10 @@
<string name="wallet_title" msgid="5369767670735827105">"वालेट"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"सबै देखाइयोस्"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"भुक्तानी गर्न अनलक गर्नुहोस्"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"सेटअप गरिएको छैन"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"यो वालेट प्रयोग गर्न डिभाइस अनलक गर्नुहोस्"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"तपाईंका कार्डहरू प्राप्त गर्ने क्रममा समस्या भयो, कृपया पछि फेरि प्रयास गर्नुहोस्"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लक स्क्रिनसम्बन्धी सेटिङ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाइजहाज मोड"</string>
<string name="add_tile" msgid="6239678623873086686">"टाइल थप्नुहोस्"</string>
@@ -871,8 +865,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"अन छ"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"अफ छ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"उपलब्ध छैन"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"अफ गरियो"</string>
<string name="nav_bar" msgid="4642708685386136807">"नेभिगेशन पट्टी"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"लेआउट"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"अतिरिक्त बायाँतिरको बटनको प्रकार"</string>
@@ -1160,4 +1153,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"प्रमाणित गर्नुहोस्"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"डिभाइस हाल्नुहोस्"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"फिंगरप्रिन्ट प्रयोग गरी खोल्नुहोस्"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 26412ce..52b58b8 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Camera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefoon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Spraakassistent"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Ontgrendelen"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Apparaat vergrendeld"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Wachten op vingerafdruk"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiele hotspot staat uit."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiele hotspot staat aan."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Casten van scherm gestopt."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Werkmodus is onderbroken."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Werkmodus aan."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Werkmodus is onderbroken."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Werkmodus staat aan."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Databesparing staat uit."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Databesparing staat aan."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limiet van <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Waarschuwing voor <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Werkprofiel"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Onderbroken"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nachtverlichting"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aan bij zonsondergang"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Tot zonsopgang"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Totale\nstilte"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Alleen\nprioriteit"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Alleen\nalarmen"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Draadloos opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Snel opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Langzaam opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Schakelen tussen gebruikers, huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Alles tonen"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Ontgrendelen om te betalen"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Niet ingesteld"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontgrendelen om te gebruiken"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Er is een probleem opgetreden bij het ophalen van je kaarten. Probeer het later opnieuw."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Instellingen voor vergrendelscherm"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
<string name="add_tile" msgid="6239678623873086686">"Tegel toevoegen"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Aan"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Uit"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Niet beschikbaar"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Uit"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigatiebalk"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Lay-out"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Extra knoptype links"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"verifiëren"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"apparaat opgeven"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gebruik vingerafdruk om te openen"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 268c245..20a66b3 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"କ୍ୟାମେରା"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ଫୋନ୍"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ଭଏସ୍ ସହାୟକ"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ଡିଭାଇସ୍ ଲକ୍ ହୋଇଯାଇଛି"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"ଟିପଚିହ୍ନ ପାଇଁ ଅପେକ୍ଷା କରୁଛି"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"ୱାର୍କ ମୋଡକୁ ବିରତ କରାଯାଇଛି।"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"ୱର୍କ ମୋଡ୍ ଅନ୍।"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"ୱାର୍କ ମୋଡକୁ ବିରତ କରାଯାଇଛି।"</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ସୀମା"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ଚେତାବନୀ"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"ବିରତ କରାଯାଇଛି"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"ନାଇଟ୍ ଲାଇଟ୍"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ସୂର୍ଯ୍ୟାସ୍ତ ବେଳେ ଅନ୍ ହେବ"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ସୂର୍ଯ୍ୟୋଦୟ ପର୍ଯ୍ୟନ୍ତ"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ସମ୍ପୂର୍ଣ୍ଣ\nନୀରବ"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"କେବଳ\nପ୍ରାଥମିକତା"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"କେବଳ\nଆଲାର୍ମ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ୱାୟାରଲେସ୍ ଭାବେ ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍ ବଦଳାନ୍ତୁ"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ୟୁଜର୍ ବଦଳାନ୍ତୁ, ବର୍ତ୍ତମାନର ୟୁଜର୍ ହେଉଛନ୍ତି <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"ବର୍ତ୍ତମାନର ୟୁଜର୍ ହେଉଛନ୍ତି <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"ୱାଲେଟ୍"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"ସବୁ ଦେଖାନ୍ତୁ"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ପେମେଣ୍ଟ କରିବାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"ସେଟ୍ ଅପ୍ କରାଯାଇନାହିଁ"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ବ୍ୟବହାର କରିବାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ଆପଣଙ୍କ କାର୍ଡଗୁଡ଼ିକ ପାଇବାରେ ଏକ ସମସ୍ୟା ହୋଇଥିଲା। ଦୟାକରି ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ସ୍କ୍ରିନ୍ ଲକ୍ ସେଟିଂସ୍"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ଏରୋପ୍ଲେନ୍ ମୋଡ୍"</string>
<string name="add_tile" msgid="6239678623873086686">"ଟାଇଲ୍ ଯୋଡ଼ନ୍ତୁ"</string>
@@ -750,14 +743,10 @@
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ସ୍ଥିତି:</b> ନୀରବକୁ ଡିମୋଟ୍ କରାଯାଇଛି"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>ସ୍ଥିତି:</b> ରେଙ୍କ ଉପରକୁ କରାଯାଇଛି"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>ସ୍ଥିତି:</b> ରେଙ୍କ ତଳକୁ କରାଯାଇଛି"</string>
- <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
- <skip />
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, ଏକ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, \'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\'କୁ ବାଧା ଦିଏ"</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, ଏକ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, \'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\'କୁ ବାଧା ଦିଏ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ସେଟିଂସ୍"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ପ୍ରାଥମିକତା"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବାର୍ତ୍ତାଳାପ ଫିଚରଗୁଡ଼ିକୁ ସମର୍ଥନ କରେ ନାହିଁ"</string>
@@ -875,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ଚାଲୁ"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ବନ୍ଦ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ଅନୁପଲବ୍ଧ"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"ଅକ୍ଷମ କରାଯାଇଛି"</string>
<string name="nav_bar" msgid="4642708685386136807">"ନାଭିଗେଶନ୍ ବାର୍"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ଲେଆଉଟ୍"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ସମ୍ପୂର୍ଣ୍ଣ ବାମ ବଟନ୍ ପ୍ରକାର"</string>
@@ -1164,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ପ୍ରମାଣୀକରଣ କରନ୍ତୁ"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ଡିଭାଇସ୍ ବିଷୟରେ ସୂଚନା ଲେଖନ୍ତୁ"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ଖୋଲିବାକୁ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 635cb2f..df2caea 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"ਕੈਮਰਾ"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ਫ਼ੋਨ ਕਰੋ"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ਅਵਾਜ਼ੀ ਸਹਾਇਕ"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"ਅਣਲਾਕ ਕਰੋ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ਡੀਵਾਈਸ ਲਾਕ ਹੈ"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਉਡੀਕ ਹੋ ਰਹੀ ਹੈ"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"ਕਾਰਜ ਮੋਡ ਰੁਕਿਆ ਹੋਇਆ ਹੈ।"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"ਕੰਮ ਮੋਡ ਚਾਲੂ ਹੈ।"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"ਕਾਰਜ ਮੋਡ ਰੁਕਿਆ ਹੋਇਆ ਹੈ।"</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਸੀਮਾ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਚਿਤਾਵਨੀ"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"ਰੁਕਿਆ ਹੋਇਆ ਹੈ"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"ਰਾਤ ਦੀ ਰੋਸ਼ਨੀ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ਸੂਰਜ ਛਿਪਣ \'ਤੇ ਚਾਲੂ"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ਸੂਰਜ ਚੜ੍ਹਨ ਤੱਕ"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ਕੁਲ \n ਚੁੱਪੀ"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ਕੇਵਲ\nਤਰਜੀਹੀ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"ਕੇਵਲ\nਅਲਾਰਮ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਬਿਨਾਂ ਤਾਰ ਤੋਂ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ਵਰਤੋਂਕਾਰ, ਵਰਤਮਾਨ ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"ਮੌਜੂਦਾ ਉਪਭੋਗਤਾ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"ਵਾਲੇਟ"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"ਸਭ ਦਿਖਾਓ"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ਭੁਗਤਾਨ ਕਰਨ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ਵਰਤਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ਤੁਹਾਡੇ ਕਾਰਡ ਪ੍ਰਾਪਤ ਕਰਨ ਵਿੱਚ ਕੋਈ ਸਮੱਸਿਆ ਆਈ, ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ਲਾਕ ਸਕ੍ਰੀਨ ਸੈਟਿੰਗਾਂ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
<string name="add_tile" msgid="6239678623873086686">"ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
@@ -750,14 +743,10 @@
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਘਟਾ ਕੇ ਸ਼ਾਂਤ \'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਵਧਾਇਆ ਗਿਆ"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਘਟਾਇਆ ਗਿਆ"</string>
- <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
- <skip />
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ, ਬਬਲ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ, \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵੀ ਵਿਘਨ ਪੈ ਸਕਦਾ ਹੈ"</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ, ਬਬਲ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ, \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵੀ ਵਿਘਨ ਪੈ ਸਕਦਾ ਹੈ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ਸੈਟਿੰਗਾਂ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ਤਰਜੀਹ"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਗੱਲਬਾਤ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
@@ -875,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ਚਾਲੂ"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ਬੰਦ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ਅਣਉਪਲਬਧ"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"ਬੰਦ ਹੈ"</string>
<string name="nav_bar" msgid="4642708685386136807">"ਦਿਸ਼ਾ-ਨਿਰਦੇਸ਼ ਪੱਟੀ"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ਖਾਕਾ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ਵਧੇਰੇ ਖੱਬੇ ਬਟਨ ਕਿਸਮ"</string>
@@ -1164,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ਪ੍ਰਮਾਣਿਤ ਕਰੋ"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ਡੀਵਾਈਸ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ਖੋਲ੍ਹਣ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 6ee7453..1a9c868 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Aparat"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asystent głosowy"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Odblokuj"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Urządzenie zablokowane"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Czekam na odcisk palca"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilny hotspot został wyłączony."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilny hotspot został włączony."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Zatrzymano przesyłanie ekranu."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Wstrzymano tryb pracy."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Tryb pracy włączony."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Wstrzymano tryb pracy."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Tryb pracy włączony."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Oszczędzanie danych jest wyłączone."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Oszczędzanie danych jest włączone."</string>
@@ -416,8 +416,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limit <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Ostrzeżenie: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Profil służbowy"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Wstrzymano"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Podświetlenie nocne"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Włącz o zachodzie"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do wschodu słońca"</string>
@@ -477,14 +476,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Całkowita\ncisza"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Tylko\npriorytetowe"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Tylko\nalarmy"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ładowanie bezprzewodowe • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Szybkie ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wolne ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Przełącz użytkownika. Bieżący użytkownik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Bieżący użytkownik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +677,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Portfel"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Pokaż wszystko"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Odblokuj, aby zapłacić"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nie skonfigurowano"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odblokuj, aby użyć"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Podczas pobierania kart wystąpił problem. Spróbuj ponownie później."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ustawienia ekranu blokady"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil służbowy"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Tryb samolotowy"</string>
<string name="add_tile" msgid="6239678623873086686">"Dodaj nazwę"</string>
@@ -881,8 +874,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Wł."</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Wył."</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Niedostępne"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Wyłączono"</string>
<string name="nav_bar" msgid="4642708685386136807">"Pasek nawigacji"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Układ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Typ dodatkowego lewego przycisku"</string>
@@ -1172,4 +1164,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"uwierzytelnij"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"otwórz urządzenie"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"By otworzyć, użyj odcisku palca"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index b15a144..fa10f0f 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Câmera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistência de voz"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Carteira"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Aguardando impressão digital"</string>
@@ -289,11 +290,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"O ponto de acesso móvel foi desativado."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"O ponto de acesso móvel foi ativado."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"A transmissão de tela foi interrompida."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabalho pausado."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabalho ativado."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Modo de trabalho pausado."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Modo de trabalho ativado."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Economia de dados desativada."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Economia de dados ativada."</string>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabalho"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausado"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Modo noturno"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ativ. ao pôr do sol"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Até o nascer do sol"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silêncio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Somente\nprioridade"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Somente\nalarmes"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando sem fio • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Alternar usuário. Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Carteira"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloqueie para pagar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Dispositivos não configurados"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
<string name="add_tile" msgid="6239678623873086686">"Adicionar bloco"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponível"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Desativado"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegação"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botão esquerdo extra"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada em andamento"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 90b1931..1723de6 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Câmara"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telemóvel"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistente de voz"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"A aguardar a impressão digital…"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Zona Wi-Fi móvel desligada."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Zona Wi-Fi móvel ligada."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmissão do ecrã interrompida."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabalho colocado em pausa."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabalho ativado."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Modo de trabalho colocado em pausa."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"O modo de trabalho foi ativado."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Poupança de dados desativada."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Poupança de dados ativada."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabalho"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Em pausa"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luz noturna"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ativ. ao pôr-do-sol"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Até ao amanhecer"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silêncio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Apenas\nprioridade"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Apenas\nalarmes"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar sem fios • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar rapidamente • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar lentamente • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Mudar de utilizador; o utilizador atual é <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utilizador atual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Carteira"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloquear para pagar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Não configurado"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para utilizar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao obter os seus cartões. Tente novamente mais tarde."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Definições do ecrã de bloqueio"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avião"</string>
<string name="add_tile" msgid="6239678623873086686">"Adicionar mosaico"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponível"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Desativado"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegação"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Esquema"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botão esquerdo adicional"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"entrar no dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilize a impressão digital para abrir"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index b15a144..fa10f0f 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Câmera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistência de voz"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Carteira"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Aguardando impressão digital"</string>
@@ -289,11 +290,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"O ponto de acesso móvel foi desativado."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"O ponto de acesso móvel foi ativado."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"A transmissão de tela foi interrompida."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabalho pausado."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabalho ativado."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Modo de trabalho pausado."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Modo de trabalho ativado."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Economia de dados desativada."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Economia de dados ativada."</string>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabalho"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausado"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Modo noturno"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ativ. ao pôr do sol"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Até o nascer do sol"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silêncio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Somente\nprioridade"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Somente\nalarmes"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando sem fio • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Alternar usuário. Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Carteira"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloqueie para pagar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Dispositivos não configurados"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
<string name="add_tile" msgid="6239678623873086686">"Adicionar bloco"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponível"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Desativado"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegação"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botão esquerdo extra"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada em andamento"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 87f9e98..1fb5514 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Cameră foto"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistent vocal"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Deblocați"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispozitiv blocat"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Se așteaptă amprenta"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Hotspotul mobil este dezactivat."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Hotspotul mobil este activat."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmiterea ecranului a fost oprită."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modul de lucru s-a întrerupt."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modul de lucru este activat."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Modul de lucru a fost întrerupt."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Modul de lucru a fost activat."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Economizorul de date a fost dezactivat."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Economizorul de date a fost activat."</string>
@@ -414,8 +414,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limită de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Avertizare: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Profil de serviciu"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Întrerupt"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Lumină de noapte"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activată la apus"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Până la răsărit"</string>
@@ -475,14 +474,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Niciun\nsunet"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Numai\ncu prioritate"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Numai\nalarme"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă wireless • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă rapid • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă lent • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Comutați între utilizatori"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Schimbați utilizatorul (utilizator actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utilizator actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -679,12 +674,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Afișați-le pe toate"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Deblocați pentru a plăti"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Neconfigurat"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Deblocați pentru a folosi"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"A apărut o problemă la preluarea cardurilor. Încercați din nou mai târziu"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setările ecranului de blocare"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil de serviciu"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod Avion"</string>
<string name="add_tile" msgid="6239678623873086686">"Adăugați o casetă"</string>
@@ -876,8 +869,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activat"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Dezactivați"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponibil"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Dezactivat"</string>
<string name="nav_bar" msgid="4642708685386136807">"Bară de navigare"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Aspect"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tip de buton din extrema stângă"</string>
@@ -1166,4 +1158,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Autentificați-vă"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"Accesați dispozitivul"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Folosiți amprenta ca să deschideți"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index c64d557..805e16e 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Камера"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон."</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Аудиоподсказки"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Разблокировать."</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Устройство заблокировано"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Отсканируйте отпечаток пальца"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Рабочий режим отключен."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Рабочий режим включен."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Рабочий режим отключен."</string>
<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>
@@ -416,8 +416,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ограничение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Предупреждение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Рабочий профиль"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Приостановлен"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ночная подсветка"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Вкл. на закате"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"До рассвета"</string>
@@ -477,14 +476,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Полная\nтишина"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Только\nважные"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Только\nбудильник"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Беспроводная зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Быстрая зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Медленная зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Сменить пользователя."</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Сменить аккаунт. Вход выполнен под именем <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>."</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Выбран аккаунт пользователя <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +677,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Кошелек"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Показать все"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Разблокировать для оплаты"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Не настроено"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Разблокировать для использования"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Не удалось получить информацию о картах. Повторите попытку позже."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Настройки заблокированного экрана"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Рабочий профиль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим полета"</string>
<string name="add_tile" msgid="6239678623873086686">"Добавить кнопку быстрого доступа"</string>
@@ -881,8 +874,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Включено"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Отключено"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Недоступно"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Отключено"</string>
<string name="nav_bar" msgid="4642708685386136807">"Панель навигации"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Расположение кнопок"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Дополнительный тип кнопки \"Влево\""</string>
@@ -1172,4 +1164,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"выполнить аутентификацию"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"указать устройство"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Используйте отпечаток пальца для входа."</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 961fe17..71e9077 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"කැමරාව"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"දුරකථනය"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"හඬ සහාය"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"පසුම්බිය"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"අඟුල අරින්න"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"උපාංගය අගුලු දමා ඇත"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"ඇඟිලි සලකුණ සඳහා බලා සිටිමින්"</string>
@@ -289,11 +290,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"කාර්යාල ප්රකාරය විරාම කරන ලදි."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"වැඩ ප්රකාරය ක්රියාත්මකයි."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"කාර්යාල ප්රකාරය විරාම කරන ලදි."</string>
<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>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> සීමිත"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> අවවාද කිරීම"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"කාර්යාල පැතිකඩ"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"විරාම කරන ලදි"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"රාත්රී ආලෝකය"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"හිරු බැසීමේදී ක්රි."</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"හිරු නගින තෙක්"</string>
@@ -672,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"පසුම්බිය"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"සියල්ල පෙන්වන්න"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ගෙවීමට අගුලු හරින්න"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"පිහිටුවා නැත"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"භාවිත කිරීමට අගුලු හරින්න"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ඔබගේ කාඩ්පත ලබා ගැනීමේ ගැටලුවක් විය, කරුණාකර පසුව නැවත උත්සාහ කරන්න"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"අගුලු තිර සැකසීම්"</string>
<string name="status_bar_work" msgid="5238641949837091056">"කාර්යාල පැතිකඩ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ගුවන්යානා ප්රකාරය"</string>
<string name="add_tile" msgid="6239678623873086686">"ටයිල් එක් කරන්න"</string>
@@ -867,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ක්රියාත්මකයි"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ක්රියාවිරහිතයි"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ලබා ගත නොහැකිය"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"අබලයි"</string>
<string name="nav_bar" msgid="4642708685386136807">"සංචලන තීරුව"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"පිරිසැලසුම"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"අමතර වම් බොත්තම් වර්ගය"</string>
@@ -1156,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"සත්යාපනය කරන්න"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"උපාංගය ඇතුළු කරන්න"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"විවෘත කිරීමට ඇඟිලි සලකුණ භාවිත කරන්න"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"සත්යාපනය අවශ්යයි. සත්යාපනය කිරීමට ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ක්රියාත්මක වන දුරකථන ඇමතුම"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 44325f7..79e1cfb 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Fotoaparát"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefón"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Hlasový asistent"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Odomknúť"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Zariadenie je uzamknuté"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Čaká sa na odtlačok prsta"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilný hotspot je vypnutý."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilný hotspot je zapnutý."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Prenášanie bolo zastavené."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Pracovný režim je pozastavený."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Pracovný režim zapnutý"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Pracovný režim je pozastavený."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Pracovný režim je zapnutý."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Šetrič dát bol vypnutý."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Šetrič dát bol zapnutý."</string>
@@ -416,8 +416,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozornenie pri <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Pracovný profil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pozastavené"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nočný režim"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Zapne sa pri západe slnka"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do východu slnka"</string>
@@ -477,14 +476,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Úplné\nticho"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Iba\nprioritné"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Iba\nbudíky"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa bezdrôtovo • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa rýchlo • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa pomaly • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Prepnúť používateľa (súčasný používateľ: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuálny používateľ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +677,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Peňaženka"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Zobraziť všetko"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Odomknúť a zaplatiť"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nenastavené"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odomknúť a použiť"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pri načítavaní kariet sa vyskytol problém. Skúste to neskôr."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavenia uzamknutej obrazovky"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovný profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim v lietadle"</string>
<string name="add_tile" msgid="6239678623873086686">"Pridať dlaždicu"</string>
@@ -881,8 +874,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Zapnuté"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Vypnuté"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nedostupné"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Deaktivované"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigačný panel"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Rozloženie"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Dodatočný typ ľavého tlačidla"</string>
@@ -1135,7 +1127,7 @@
<string name="basic_status" msgid="2315371112182658176">"Otvorená konverzácia"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"Miniaplikácie konverzácií"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"Klepnite na konverzáciu a pridajte ju tak na plochu"</string>
- <string name="no_conversations_text" msgid="7362374212649891057">"Sem sa vráťte, keď dostanete nejaké správy"</string>
+ <string name="no_conversations_text" msgid="7362374212649891057">"Vráťte sa sem, až dostanete nejaké správy"</string>
<string name="priority_conversations" msgid="3967482288896653039">"Prioritné konverzácie"</string>
<string name="recent_conversations" msgid="8531874684782574622">"Nedávne konverzácie"</string>
<string name="okay" msgid="6490552955618608554">"OK"</string>
@@ -1156,7 +1148,7 @@
<string name="audio_status" msgid="4237055636967709208">"Počúvam"</string>
<string name="game_status" msgid="1340694320630973259">"Hrá sa hra"</string>
<string name="empty_user_name" msgid="3389155775773578300">"Priatelia"</string>
- <string name="empty_status" msgid="5938893404951307749">"Porozprávajme sa."</string>
+ <string name="empty_status" msgid="5938893404951307749">"Poďme sa rozprávať."</string>
<string name="status_before_loading" msgid="1500477307859631381">"Obsah sa čoskoro zobrazí"</string>
<string name="missed_call" msgid="4228016077700161689">"Zmeškaný hovor"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
@@ -1172,4 +1164,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"overte"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"vstúpte do zariadenia"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorte odtlačkom prsta"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index f11db07..6500e05 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Fotoaparat"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovni pomočnik"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Odkleni"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Naprava je zaklenjena."</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Čakanje na prstni odtis"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilna dostopna točka je izklopljena."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilna dostopna točka je vklopljena."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Predvajanje zaslona je ustavljeno."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Način za delo je začasno zaustavljen."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Način za delo vklopljen."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Način za delo je začasno zaustavljen."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Način za delo je vklopljen."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Varčevanje s podatki je izklopljeno."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Varčevanje s podatki je vklopljeno."</string>
@@ -416,8 +416,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Omejitev: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Opozorilo – <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Delovni profil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Zaustavljeno"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nočna svetloba"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ob sončnem zahodu"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do sončnega vzhoda"</string>
@@ -477,14 +476,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Popolna\ntišina"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Samo\nprednostno"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Samo\nalarmi"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Brezžično polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hitro polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Počasno polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Preklop med uporabniki, trenutni uporabnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Trenutni uporabnik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +677,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Denarnica"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Prikaži vse"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Odklenite za plačevanje"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Ni nastavljeno"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odklenite za uporabo"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pri pridobivanju kartic je prišlo do težave. Poskusite znova pozneje."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavitve zaklepanja zaslona"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil za Android Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način za letalo"</string>
<string name="add_tile" msgid="6239678623873086686">"Dodajanje ploščice"</string>
@@ -881,8 +874,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Vklopljeno"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Izklopljeno"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Ni na voljo"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Onemogočeno"</string>
<string name="nav_bar" msgid="4642708685386136807">"Vrstica za krmarjenje"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Postavitev"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Vrsta dodatnega levega gumba"</string>
@@ -1172,4 +1164,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"preverjanje pristnosti"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"vstop v napravo"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Odprite s prstnim odtisom"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 70b53cb..9df3c98 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefoni"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ndihma zanore"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Shkyç"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Pajisja është e kyçur"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Në pritje për gjurmën e gishtit"</string>
@@ -292,7 +294,7 @@
<!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
<skip />
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modaliteti i punës është i aktivizuar."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
+ <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (6422896967647049692) -->
<skip />
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Modaliteti i punës është i aktivizuar."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Kursyesi i të dhënave është çaktivizuar."</string>
@@ -473,14 +475,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Heshtje\ne plotë"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Vetëm\nme prioritet"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Vetëm\nalarmet"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet me valë • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet shpejt • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet ngadalë • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Ndërro përdoruesin. Përdoruesi aktual është <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Përdoruesi aktual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -750,14 +748,10 @@
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Statusi:</b> Ulur në nivel si në heshtje"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>Statusi:</b> Renditur më lart"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>Statusi:</b> Renditur më poshtë"</string>
- <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
- <skip />
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"Shfaqet në krye të njoftimeve të bisedës dhe si fotografia e profilit në ekranin e kyçjes"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"Shfaqet në krye të njoftimeve të bisedës, shfaqet si fotografia e profilit në ekranin e kyçjes dhe shfaqet si flluskë"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"Shfaqet në krye të njoftimeve të bisedës, shfaqet si fotografia e profilit në ekranin e kyçjes dhe ndërpret modalitetin \"Mos shqetëso\""</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shfaqet në krye të njoftimeve të bisedës dhe si fotografia e profilit në ekranin e kyçjes, shfaqet si flluskë dhe ndërpret modalitetin \"Mos shqetëso\""</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Cilësimet"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Përparësia"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mbështet veçoritë e bisedës"</string>
@@ -1164,4 +1158,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"për ta vërtetuar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"për të hyrë në pajisje"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Përdor gjurmën e gishtit për ta hapur"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index de1c9a4..055acfa 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Камера"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Гласовна помоћ"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Откључајте"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Уређај је закључан"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Чека се отисак прста"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Пословни режим је паузиран."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Режим рада је укључен."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Пословни режим је паузиран."</string>
<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>
@@ -414,8 +414,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ограничење од <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Упозорење за <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Пословни профил"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Паузирано"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ноћно светло"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Укључује се по заласку сунца"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"До изласка сунца"</string>
@@ -475,14 +474,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Потпуна\nтишина"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Само\nприорит. прекиди"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Само\nаларми"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Бежично се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Пуни се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Брзо се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Споро се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Промените корисника, актуелни корисник је <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Актуелни корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -679,12 +674,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Новчаник"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Прикажи све"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Откључај ради плаћања"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Није подешено"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Откључај ради коришћења"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Дошло је до проблема при преузимању картица. Пробајте поново касније"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Подешавања закључаног екрана"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Пословни профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим рада у авиону"</string>
<string name="add_tile" msgid="6239678623873086686">"Додај плочицу"</string>
@@ -876,8 +869,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Укључено"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Искључено"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Недоступно"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Онемогућено"</string>
<string name="nav_bar" msgid="4642708685386136807">"Трака за навигацију"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Распоред"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Додатни тип левог дугмета"</string>
@@ -1150,7 +1142,7 @@
<string name="audio_status" msgid="4237055636967709208">"Слуша се"</string>
<string name="game_status" msgid="1340694320630973259">"Игра се"</string>
<string name="empty_user_name" msgid="3389155775773578300">"Пријатељи"</string>
- <string name="empty_status" msgid="5938893404951307749">"Ћаскамо вечерас!"</string>
+ <string name="empty_status" msgid="5938893404951307749">"Ћаскамо вечерас?"</string>
<string name="status_before_loading" msgid="1500477307859631381">"Садржај ће се ускоро појавити"</string>
<string name="missed_call" msgid="4228016077700161689">"Пропуштен позив"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
@@ -1166,4 +1158,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"потврдите идентитет"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"унесите уређај"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Отворите помоћу отиска прста"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index fcbe07d..a74b2ec 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Mobil"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Röstassistent"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Lås upp"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheten är låst"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Väntar på fingeravtryck"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Den mobila surfzonen har inaktiverats."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Den mobila surfzonen har aktiverats."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Castningen av skärmen har stoppats."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Jobbläget har pausats."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Arbetsläget aktiverat."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Jobbläget har pausats."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Arbetsläget har aktiverats."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Databesparing har inaktiverats."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Databesparing har aktiverats."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Gräns: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Varning <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Jobbprofil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausat"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nattljus"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"På från solnedgången"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Till soluppgången"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Helt\ntyst"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Endast\nprioriterade"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Endast\nalarm"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas trådlöst • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas snabbt • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas långsamt • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Byt användare. Aktuell användare: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuell användare <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Visa alla"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Lås upp för att betala"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Har inte konfigurerats"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås upp för att använda"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Det gick inte att hämta dina kort. Försök igen senare."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Inställningar för låsskärm"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Jobbprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flygplansläge"</string>
<string name="add_tile" msgid="6239678623873086686">"Lägg till en ruta"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"På"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Av"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Inte tillgängligt"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Inaktiverat"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigeringsfält"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Knapptyp för extra vänster"</string>
@@ -1125,7 +1117,7 @@
<string name="select_conversation_text" msgid="3376048251434956013">"Tryck på en konversation för att lägga till den på startskärmen"</string>
<string name="no_conversations_text" msgid="7362374212649891057">"Besök den här sidan igen när du har fått meddelanden"</string>
<string name="priority_conversations" msgid="3967482288896653039">"Prioriterade konversationer"</string>
- <string name="recent_conversations" msgid="8531874684782574622">"Aktuella konversationer"</string>
+ <string name="recent_conversations" msgid="8531874684782574622">"Senaste konversationerna"</string>
<string name="okay" msgid="6490552955618608554">"Okej"</string>
<string name="timestamp" msgid="6577851592534538533">"För <xliff:g id="DURATION">%1$s</xliff:g> sedan"</string>
<string name="less_than_timestamp" msgid="6598972791137724517">"Mindre än <xliff:g id="DURATION">%1$s</xliff:g> sedan"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentisera"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ange enhet"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Öppna med fingeravtryck"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index a591d00..e60d575 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Simu"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Mapendekezo ya Sauti"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Fungua"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Kifaa kimefungwa"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Inasubiri alama ya kidole"</string>
@@ -289,11 +290,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mtandaopepe unahamishika umezimwa."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mtandaopepe unaohamishika umewashwa."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Utumaji wa skrini umesitishwa."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Hali ya kazini imesimamishwa."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Hali ya kazi imewashwa."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Hali ya kazini imesimamishwa."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Hali ya kazi imewashwa."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Kiokoa Data kimezimwa."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Kiokoa Data kimewashwa."</string>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"kikomo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Onyo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Wasifu wa kazini"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Imesimamishwa"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Mwanga wa Usiku"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Itawashwa machweo"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hadi macheo"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Kimya\nkabisa"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Kipaumbele\npekee"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Kengele\npekee"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> Inachaji bila kutumia waya • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji kwa kasi • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji polepole • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Badili mtumiaji, mtumiaji wa sasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Mtumiaji wa sasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Onyesha zote"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Fungua ili ulipe"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Haijawekwa"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Fungua ili utumie"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Hitilafu imetokea wakati wa kuleta kadi zako, tafadhali jaribu tena baadaye"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mipangilio ya kufunga skrini"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Wasifu wa kazini"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hali ya ndegeni"</string>
<string name="add_tile" msgid="6239678623873086686">"Ongeza kigae"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Imewashwa"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Imezimwa"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Hakipatikani"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Imezimwa"</string>
<string name="nav_bar" msgid="4642708685386136807">"Sehemu ya viungo muhimu"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Mpangilio"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Aina ya kitufe cha kushoto cha ziada"</string>
@@ -1124,7 +1115,7 @@
<string name="select_conversation_title" msgid="6716364118095089519">"Wijeti za mazungumzo"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"Gusa mazungumzo ili uyaweke kwenye Skrini yako ya kwanza"</string>
<string name="no_conversations_text" msgid="7362374212649891057">"Angalia hapa tena utakapopokea ujumbe"</string>
- <string name="priority_conversations" msgid="3967482288896653039">"Mazungumzo ya kipaumbele"</string>
+ <string name="priority_conversations" msgid="3967482288896653039">"Mazungumzo yenye kipaumbele"</string>
<string name="recent_conversations" msgid="8531874684782574622">"Mazungumzo ya hivi majuzi"</string>
<string name="okay" msgid="6490552955618608554">"Sawa"</string>
<string name="timestamp" msgid="6577851592534538533">"<xliff:g id="DURATION">%1$s</xliff:g> zilizopita"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"thibitisha"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"weka kifaa"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Tumia alama ya kidole kufungua"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Uthibitishaji unahitajika. Gusa kitambua alama ya kidole ili uthibitishe."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Simu inayoendelea"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index d616eab..0b56d3b 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"கேமரா"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ஃபோன்"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"குரல் உதவி"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"திற"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"சாதனம் பூட்டப்பட்டுள்ளது"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"கைரேகைக்காகக் காத்திருக்கிறது"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"பணிப் பயன்முறை இடைநிறுத்தப்பட்டது."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"பணிப் பயன்முறை இயக்கப்பட்டுள்ளது."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"பணிப் பயன்முறை இடைநிறுத்தப்பட்டது."</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> வரம்பு"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> எச்சரிக்கை"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"பணிக் கணக்கு"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"இடைநிறுத்தப்பட்டது"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"நைட் லைட்"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"மாலையில் ஆன் செய்"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"காலை வரை"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"அறிவிப்புகள்\nவேண்டாம்"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"முன்னுரிமைகள்\nமட்டும்"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"அலாரங்கள்\nமட்டும்"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • வயர்லெஸ் முறையில் சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுதும் சார்ஜாகும்"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுதும் சார்ஜாகும்"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • வேகமாகச் சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுதும் சார்ஜாகும்"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • மெதுவாக சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுதும் சார்ஜாகும்"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"பயனரை மாற்று"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"பயனரை மாற்று, தற்போதைய பயனர் <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"தற்போதைய பயனர்: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"வாலட்"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"அனைத்தையும் காட்டு"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"பணம் செலுத்த அன்லாக் செய்க"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"அமைக்கப்படவில்லை"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"பயன்படுத்துவதற்கு அன்லாக் செய்க"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"உங்கள் கார்டுகளின் விவரங்களைப் பெறுவதில் சிக்கல் ஏற்பட்டது, பிறகு முயலவும்"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"பூட்டுத் திரை அமைப்புகள்"</string>
<string name="status_bar_work" msgid="5238641949837091056">"பணிக் கணக்கு"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"விமானப் பயன்முறை"</string>
<string name="add_tile" msgid="6239678623873086686">"டைலைச் சேர்க்கும்"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ஆன்"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ஆஃப்"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"இல்லை"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"முடக்கப்பட்டது"</string>
<string name="nav_bar" msgid="4642708685386136807">"வழிசெலுத்தல் பட்டி"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"தளவமைப்பு"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"கூடுதல் இடப்புற பட்டன் வகை"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"அங்கீகரி"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"சாதனத்தைத் திற"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"கைரேகையைப் பயன்படுத்தி திறந்திடுங்கள்"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 7d44b2b..7c797b7 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"కెమెరా"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ఫోన్"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"వాయిస్ అసిస్టెంట్"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"అన్లాక్ చేయి"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"పరికరం లాక్ చేయబడింది"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"వేలిముద్ర కోసం వేచి ఉంది"</string>
@@ -289,10 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"వర్క్ మోడ్ పాజ్లో ఉంది."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"పని మోడ్ ఆన్లో ఉంది."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
+ <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (6422896967647049692) -->
<skip />
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"పని మోడ్ ఆన్ చేయబడింది."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"డేటా సేవర్ ఆఫ్ చేయబడింది."</string>
@@ -412,8 +413,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> పరిమితి"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> హెచ్చరిక"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"ఆఫీస్ ప్రొఫైల్"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"పాజ్ చేయబడింది"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"రాత్రి కాంతి"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"సూర్యాస్తమయానికి"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"సూర్యోదయం వరకు"</string>
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"మొత్తం\nనిశ్శబ్దం"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ప్రాధాన్యమైనవి\nమాత్రమే"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"అలారాలు\nమాత్రమే"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • వైర్ లేకుండా ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • వేగంగా ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తి ఛార్జ్"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తి ఛార్జ్"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"వినియోగదారుని మార్చు"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"వినియోగదారుని మార్చు, ప్రస్తుత వినియోగదారు <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"ప్రస్తుత వినియోగదారు <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +672,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"అన్నింటినీ చూపు"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"పే చేయడానికి అన్లాక్ చేయండి"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"సెటప్ చేయలేదు"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ఉపయోగించడానికి అన్లాక్ చేయండి"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"మీ కార్డ్లను పొందడంలో సమస్య ఉంది, దయచేసి తర్వాత మళ్లీ ట్రై చేయండి"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"లాక్ స్క్రీన్ సెట్టింగ్లు"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ఆఫీస్ ప్రొఫైల్"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ఎయిర్ప్లేన్ మోడ్"</string>
<string name="add_tile" msgid="6239678623873086686">"టైల్ను జోడించండి"</string>
@@ -871,8 +865,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ఆన్"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ఆఫ్ చేయి"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"అందుబాటులో లేదు"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"డిజేబుల్ చేయబడింది"</string>
<string name="nav_bar" msgid="4642708685386136807">"నావిగేషన్ బార్"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"లేఅవుట్"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"అత్యంత ఎడమ వైపు ఉన్న బటన్ రకం"</string>
@@ -1160,4 +1153,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ప్రామాణీకరించండి"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"పరికరాన్ని ఎంటర్ చేయండి"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"తెరవడానికి వేలిముద్రను ఉపయోగించండి"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index ff216a4..e7c20d2 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"กล้องถ่ายรูป"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"โทรศัพท์"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ตัวช่วยเสียง"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"ปลดล็อก"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"อุปกรณ์ถูกล็อก"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"กำลังรอลายนิ้วมือ"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"โหมดการทำงานหยุดชั่วคราว"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"โหมดการทำงานเปิดอยู่"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"โหมดการทำงานหยุดชั่วคราว"</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"ขีดจำกัด <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"คำเตือน <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"โปรไฟล์งาน"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"หยุดชั่วคราว"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"แสงตอนกลางคืน"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"เปิดตอนพระอาทิตย์ตก"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"จนพระอาทิตย์ขึ้น"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ปิดเสียง\nทั้งหมด"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"เฉพาะเรื่อง\nสำคัญ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"เฉพาะปลุก\nเท่านั้น"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จแบบไร้สาย • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จ • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จอย่างเร็ว • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จอย่างช้าๆ • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"สลับผู้ใช้"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"เปลี่ยนผู้ใช้จากผู้ใช้ปัจจุบัน <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"ผู้ใช้ปัจจุบัน <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"แสดงทั้งหมด"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ปลดล็อกเพื่อชำระเงิน"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"ไม่ได้ตั้งค่า"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ปลดล็อกเพื่อใช้"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"เกิดปัญหาในการดึงข้อมูลบัตรของคุณ โปรดลองอีกครั้งในภายหลัง"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"การตั้งค่าหน้าจอล็อก"</string>
<string name="status_bar_work" msgid="5238641949837091056">"โปรไฟล์งาน"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"โหมดบนเครื่องบิน"</string>
<string name="add_tile" msgid="6239678623873086686">"เพิ่มไทล์"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"เปิด"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ปิด"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ไม่มี"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"ปิดใช้"</string>
<string name="nav_bar" msgid="4642708685386136807">"แถบนำทาง"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"การจัดวาง"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ประเภทปุ่มทางซ้ายเพิ่มเติม"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ตรวจสอบสิทธิ์"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"เข้าถึงอุปกรณ์"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ใช้ลายนิ้วมือเพื่อเปิด"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index fe75e01..d94e7a6 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Camera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telepono"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"I-unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Naka-lock ang device"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Naghihintay ng fingerprint"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Na-off ang mobile hotspot."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Na-on ang mobile hotspot."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Itinigil ang pagka-cast sa screen."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Naka-pause ang work mode."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Naka-on ang work mode."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Naka-pause ang work mode."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Na-on ang work mode."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Na-off ang Data Saver."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Na-on ang Data Saver."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ang limitasyon"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Babala sa <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Profile sa trabaho"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Naka-pause"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Mao-on sa sunset"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hanggang sunrise"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Ganap na\nkatahimikan"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Priyoridad\nlang"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Mga alarm\nlang"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wireless na nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mabilis na nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mabagal na nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Magpalit ng user, kasalukuyang user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Kasalukuyang user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Ipakita lahat"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"I-unlock para magbayad"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Hindi naka-set up"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"I-unlock para magamit"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Nagkaproblema sa pagkuha ng iyong mga card, pakisubukan ulit sa ibang pagkakataon"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mga setting ng lock screen"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profile sa trabaho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
<string name="add_tile" msgid="6239678623873086686">"Magdagdag ng tile"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"I-on"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"I-off"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Hindi available"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Naka-disable"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigation bar"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Uri ng extra na button ng kaliwa"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"i-authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ilagay ang device"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gamitin ang fingerprint para buksan"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 6d3743f..d5185fb 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Sesli Yardım"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Cüzdan"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Kilidi aç"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Cihaz kilitlendi"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Parmak izi bekleniyor"</string>
@@ -289,11 +290,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobil hotspot kapatıldı."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobil hotspot açıldı."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekran yayını durduruldu."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Çalışma modu duraklatıldı."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Çalışma modu açık."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Çalışma modu duraklatıldı."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Çalışma modu açıldı."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Veri Tasarrufu kapatıldı."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Veri Tasarrufu açıldı."</string>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Sınır: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> uyarısı"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"İş profili"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Duraklatıldı"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Gece Işığı"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Gün batımı açılacak"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Sabaha kadar"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Tamamen\nsessiz"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Yalnızca\nöncelik"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Yalnızca\nalarmlar"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kablosuz şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hızlı şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Yavaş şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değiştirme"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Kullanıcı değiştir. Geçerli kullanıcı: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Geçerli kullanıcı: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Cüzdan"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Tümünü göster"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Ödeme için kilidi aç"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Ayarlanmadı"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Kullanmak için kilidi aç"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kartlarınız alınırken bir sorun oluştu. Lütfen daha sonra tekrar deneyin"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Kilit ekranı ayarları"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Uçak modu"</string>
<string name="add_tile" msgid="6239678623873086686">"Blok ekle"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Açık"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Kapalı"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Kullanılamıyor"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Devre dışı bırakıldı"</string>
<string name="nav_bar" msgid="4642708685386136807">"Gezinme çubuğu"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Düzen"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra sol düğme türü"</string>
@@ -1129,7 +1120,7 @@
<string name="okay" msgid="6490552955618608554">"Tamam"</string>
<string name="timestamp" msgid="6577851592534538533">"<xliff:g id="DURATION">%1$s</xliff:g> önce"</string>
<string name="less_than_timestamp" msgid="6598972791137724517">"Henüz <xliff:g id="DURATION">%1$s</xliff:g> olmadı"</string>
- <string name="over_timestamp" msgid="4765793502859358634">"<xliff:g id="DURATION">%1$s</xliff:g> üzerinde bir süre önce"</string>
+ <string name="over_timestamp" msgid="4765793502859358634">"En az <xliff:g id="DURATION">%1$s</xliff:g> önce"</string>
<string name="birthday_status" msgid="2596961629465396761">"Doğum günü"</string>
<string name="birthday_status_content_description" msgid="682836371128282925">"Bugün <xliff:g id="NAME">%1$s</xliff:g> adlı kişinin doğum günü"</string>
<string name="upcoming_birthday_status" msgid="2005452239256870351">"Yaklaşan doğum günü"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"kimlik doğrulaması yapın"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz girin"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmak için parmak izi kullanın"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kimlik doğrulaması gerekiyor. Kimlik doğrulaması için parmak izi sensörüne dokunun."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Devam eden telefon görüşmesi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index c0f6e74..df9c5bf 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Камера"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Номер телефону"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Голосові підказки"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Розблокувати"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Пристрій заблоковано"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Очікується відбиток пальця"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Робочий режим призупинено."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Робочий режим увімкнено."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Робочий режим призупинено."</string>
<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>
@@ -416,8 +416,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Обмеження: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Застереження: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Робочий профіль"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Призупинено"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Нічний екран"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Вмикається ввечері"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"До сходу сонця"</string>
@@ -477,14 +476,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Без\nсигналів"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Лише\nприорітетні"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Лише\nсигнали"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Бездротове заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Швидке заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Повільне заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Змінити користувача"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Змінити користувача, поточний користувач – <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Поточний користувач: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +677,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Гаманець"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Показати все"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Розблокувати, щоб сплатити"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Не налаштовано"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Розблокувати, щоб використовувати"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Не вдалось отримати ваші картки. Повторіть спробу пізніше."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Параметри блокування екрана"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Робочий профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим польоту"</string>
<string name="add_tile" msgid="6239678623873086686">"Додавання опції"</string>
@@ -881,8 +874,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Увімкнено"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Вимкнено"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Недоступно"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Вимкнено"</string>
<string name="nav_bar" msgid="4642708685386136807">"Панель навігації"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Макет"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Додатковий тип кнопки ліворуч"</string>
@@ -1172,4 +1164,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"пройти автентифікацію"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"відкрити пристрій"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Щоб відкрити, використайте відбиток пальця"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 491454a..be8fafb 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"کیمرا"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"فون"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"صوتی معاون"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"غیر مقفل کریں"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"آلہ مقفل کر دیا گیا"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"فنگر پرنٹ کا انتظار ہے"</string>
@@ -289,10 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"ورک موڈ موقوف ہے۔"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"کام موڈ آن ہے۔"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
+ <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (6422896967647049692) -->
<skip />
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"کام موڈ آن ہو گیا۔"</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ڈیٹا سیور آف ہو گیا۔"</string>
@@ -412,8 +413,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> حد"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> وارننگ"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"دفتری پروفائل"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"موقوف ہے"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"نائٹ لائٹ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"غروب آفتاب کے وقت آن ہوگی"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"طلوع آفتاب تک"</string>
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"مکمل\nخاموشی"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"صرف\nترجیحی"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"صرف\nالارمز"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • وائرلیس طریقے سے چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • تیزی سے چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • آہستہ چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"صارف سوئچ کریں"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"صارف سوئچ کریں، موجودہ صارف <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"موجودہ صارف <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +672,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"سبھی دکھائیں"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ادائیگی کرنے کے لیے غیر مقفل کریں"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"سیٹ اپ نہیں کیا گیا"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"استعمال کرنے کے لیے غیر مقفل کریں"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"آپ کے کارڈز حاصل کرنے میں ایک مسئلہ درپیش تھا، براہ کرم بعد میں دوبارہ کوشش کریں"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"مقفل اسکرین کی ترتیبات"</string>
<string name="status_bar_work" msgid="5238641949837091056">"دفتری پروفائل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ہوائی جہاز وضع"</string>
<string name="add_tile" msgid="6239678623873086686">"ٹائل شامل کریں"</string>
@@ -871,8 +865,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"آن"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"آف"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"غیر دستیاب ہے"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"غیر فعال ہے"</string>
<string name="nav_bar" msgid="4642708685386136807">"نیویگیشن بار"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"لے آؤٹ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"بائيں جانب کی اضافی بٹن کی قسم"</string>
@@ -1160,4 +1153,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"تصدیق کریں"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"آلہ درج کریں"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"کھولنے کے لیے فنگر پرنٹ کا استعمال کریں"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 1ba1eed..131373c 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ovozli yordam"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Qulfdan chiqarish"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Qurilma qulflandi"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Barmoq izingizni skanerlang"</string>
@@ -291,7 +292,7 @@
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ekranni translatsiya qilish to‘xtadi."</string>
<string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Ish rejimi pauzada."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Ish rejimi yoniq."</string>
- <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Ish rejimi pauza qilindi."</string>
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Ish rejimi pauzada."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Ishchi rejim yoqildi."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Trafik tejash rejimi o‘chirib qo‘yildi."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Trafik tejash rejimi yoqildi."</string>
@@ -1150,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifikatsiya"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"qurilmani ochish"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ochish uchun barmoq izidan foydalaning"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Haqiqiylikni tekshirish talab etiladi. Autentifikatsiya uchun barmoq izi skaneriga tegining."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Joriy telefon chaqiruvi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 902c30f..7a381c5 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Máy ảnh"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Điện thoại"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Trợ lý thoại"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"Mở khóa"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Đã khóa thiết bị"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Đang chờ vân tay"</string>
@@ -289,11 +291,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Đã tắt điểm phát sóng di động."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Đã bật điểm phát sóng di động."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Đã ngừng truyền màn hình."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Đã tạm dừng chế độ công việc."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Chế độ làm việc bật."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Đã tạm dừng chế độ công việc."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Chế độ làm việc đã bật."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Đã tắt Trình tiết kiệm dữ liệu."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Đã bật Trình tiết kiệm dữ liệu."</string>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Giới hạn <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Cảnh báo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Hồ sơ công việc"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Đã tạm dừng"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ánh sáng đêm"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Bật khi trời tối"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Cho đến khi trời sáng"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Hoàn toàn\ntắt tiếng"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Chỉ\nưu tiên"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Chỉ\nbáo thức"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc không dây • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc nhanh • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc chậm • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Chuyển người dùng, người dùng hiện tại <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Người dùng hiện tại <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Ví"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Hiện tất cả"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Mở khóa để thanh toán"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Chưa thiết lập"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Mở khóa để sử dụng"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Đã xảy ra sự cố khi tải thẻ của bạn. Vui lòng thử lại sau"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Cài đặt màn hình khóa"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Hồ sơ công việc"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Chế độ máy bay"</string>
<string name="add_tile" msgid="6239678623873086686">"Thêm ô"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Đang bật"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Đang tắt"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Không có sẵn"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Đã tắt"</string>
<string name="nav_bar" msgid="4642708685386136807">"Thanh điều hướng"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Bố cục"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Loại nút bổ sung bên trái"</string>
@@ -1144,7 +1136,7 @@
<string name="audio_status" msgid="4237055636967709208">"Đang nghe"</string>
<string name="game_status" msgid="1340694320630973259">"Đang chơi"</string>
<string name="empty_user_name" msgid="3389155775773578300">"Bạn bè"</string>
- <string name="empty_status" msgid="5938893404951307749">"Cùng trò chuyện tối nay nhé!"</string>
+ <string name="empty_status" msgid="5938893404951307749">"Tối nay nói chuyện nhé!"</string>
<string name="status_before_loading" msgid="1500477307859631381">"Nội dung sẽ sớm hiển thị"</string>
<string name="missed_call" msgid="4228016077700161689">"Cuộc gọi nhỡ"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"Hơn <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"xác thực"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"truy cập thiết bị"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Dùng vân tay để mở"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 728e0255..0f162be 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"相机"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"电话"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"语音助理"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"解锁"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"设备已锁定"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"正在等待提供指纹"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"工作模式已暂停。"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"工作模式开启。"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"工作模式已暂停。"</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"上限为<xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g>警告"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"工作资料"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"已暂停"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"护眼模式"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"在日落时开启"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"在日出时关闭"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"完全\n静音"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"仅限\n优先打扰"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"仅限\n闹钟"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在无线充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在快速充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在慢速充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"切换用户,当前用户为<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"当前用户为<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"电子钱包"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"全部显示"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"解锁设备才能付款"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"未设置"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解锁设备即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"获取您的卡片时出现问题,请稍后重试"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"锁定屏幕设置"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作资料"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飞行模式"</string>
<string name="add_tile" msgid="6239678623873086686">"添加图块"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"开启"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"关闭"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"不可用"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"已停用"</string>
<string name="nav_bar" msgid="4642708685386136807">"导航栏"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"布局"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"其他向左按钮类型"</string>
@@ -1148,7 +1140,7 @@
<string name="status_before_loading" msgid="1500477307859631381">"内容很快就会显示,请稍候"</string>
<string name="missed_call" msgid="4228016077700161689">"未接电话"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"查看最近的信息、未接电话和状态更新"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"查看近期的消息、未接电话和状态更新"</string>
<string name="people_tile_title" msgid="6589377493334871272">"对话"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g>发送了一条消息"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g>发送了一张图片"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"身份验证"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"进入设备"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指纹即可打开"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 165ccf9..cd02e78 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"相機"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"電話"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"語音助手"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"解鎖"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"裝置已上鎖"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"正在等待指紋"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"工作模式已暫停。"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"工作模式已開啟。"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"工作模式已暫停。"</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"上限為 <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 警告"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"工作設定檔"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"已暫停"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"夜間模式"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"在日落時開啟"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"在日出時關閉"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"完全\n靜音"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"僅限\n優先"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"僅限\n鬧鐘"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 無線充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 快速充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慢速充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"切換使用者,目前使用者是<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"目前的使用者是 <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"電子錢包"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"顯示全部"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"解鎖裝置才能付款"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"未設定"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解鎖即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"擷取資訊卡時發生問題,請稍後再試。"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"上鎖畫面設定"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作設定檔"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛行模式"</string>
<string name="add_tile" msgid="6239678623873086686">"加入圖塊"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"開啟"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"關閉"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"無法使用"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"已停用"</string>
<string name="nav_bar" msgid="4642708685386136807">"導覽列"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"配置"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"其他向左按鈕類型"</string>
@@ -1123,7 +1115,7 @@
<string name="basic_status" msgid="2315371112182658176">"開啟對話"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"對話小工具"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"輕按對話即可新增至主畫面"</string>
- <string name="no_conversations_text" msgid="7362374212649891057">"待你收到一些訊息後再回來查看"</string>
+ <string name="no_conversations_text" msgid="7362374212649891057">"等你收到一些訊息後再回來查看吧"</string>
<string name="priority_conversations" msgid="3967482288896653039">"優先對話"</string>
<string name="recent_conversations" msgid="8531874684782574622">"最近的對話"</string>
<string name="okay" msgid="6490552955618608554">"確定"</string>
@@ -1144,7 +1136,7 @@
<string name="audio_status" msgid="4237055636967709208">"正在聽取音訊"</string>
<string name="game_status" msgid="1340694320630973259">"正在玩遊戲"</string>
<string name="empty_user_name" msgid="3389155775773578300">"朋友"</string>
- <string name="empty_status" msgid="5938893404951307749">"今晚聊天吧!"</string>
+ <string name="empty_status" msgid="5938893404951307749">"今晚傾下偈啦!"</string>
<string name="status_before_loading" msgid="1500477307859631381">"即將顯示內容"</string>
<string name="missed_call" msgid="4228016077700161689">"未接來電"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"驗證"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 8e2ecda..043cf43 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -136,6 +136,8 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"相機"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"電話"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"語音小幫手"</string>
+ <!-- no translation found for accessibility_wallet_button (1458258783460555507) -->
+ <skip />
<string name="accessibility_unlock_button" msgid="122785427241471085">"解除鎖定"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"裝置已鎖定"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"正在等候指紋"</string>
@@ -289,11 +291,9 @@
<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>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"工作模式已暫停。"</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"工作模式已開啟。"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"工作模式已暫停。"</string>
<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>
@@ -412,8 +412,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"上限為 <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 警告"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"工作資料夾"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"已暫停"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"夜燈"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"於日落時開啟"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"於日出時關閉"</string>
@@ -473,14 +472,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"完全\n靜音"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"僅允許\n優先通知"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"僅允許\n鬧鐘"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 無線充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 快速充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慢速充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"切換使用者,目前使用者是<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"目前使用者是「<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>」"</string>
@@ -676,12 +671,10 @@
<string name="wallet_title" msgid="5369767670735827105">"電子錢包"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"顯示全部"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"解鎖裝置才能付款"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"未設定"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解鎖即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"擷取卡片時發生問題,請稍後再試"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"螢幕鎖定設定"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作資料夾"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛航模式"</string>
<string name="add_tile" msgid="6239678623873086686">"新增圖塊"</string>
@@ -871,8 +864,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"開啟"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"關閉"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"無法使用"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"已停用"</string>
<string name="nav_bar" msgid="4642708685386136807">"導覽列"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"配置"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"其他向左按鈕類型"</string>
@@ -1160,4 +1152,8 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"驗證"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
+ <!-- no translation found for accessibility_fingerprint_bouncer (7189102492498735519) -->
+ <skip />
+ <!-- no translation found for ongoing_phone_call_content_description (5332334388483099947) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 1c70acc..a444cd0 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -136,6 +136,7 @@
<string name="accessibility_camera_button" msgid="2938898391716647247">"Ikhamela"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Ifoni"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Isisekeli sezwi"</string>
+ <string name="accessibility_wallet_button" msgid="1458258783460555507">"I-wallet"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Vula"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Idivayisi ikhiyiwe"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"Ilindele izigxivizo zeminwe"</string>
@@ -289,11 +290,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"I-hotspot ivaliwe."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"I-hotspot ivuliwe."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ukusakaza kwesikrini kumisiwe."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Imodi yokusebenza imisiwe."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Imodi yomsebenzi ivuliwe."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"Imodi yokusebenza imisiwe."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Imodi yomsebenzi ivuliwe."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Iseva yedatha ivaliwe."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Iseva yedatha ivuliwe."</string>
@@ -412,8 +411,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> umkhawulo"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> isexwayiso"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Iphrofayela yomsebenzi"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Kumisiwe"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ukukhanya kwasebusuku"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Kuvulwe ekushoneni kwelanga"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Kuze kube sekuphumeni kwelanga"</string>
@@ -473,14 +471,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Ukuthula\niokuphelele"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Okubalulekile\nkuphela"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Ama-alamu\nkuphela"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ishajwa ngokungaxhunyiwe • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Iyashaja • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ishaja ngokushesha • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ishaja kancane • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Shintsha umsebenzisi, umsebenzisi wamanje ngu-<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Umsebenzisi wamanje <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +670,10 @@
<string name="wallet_title" msgid="5369767670735827105">"I-wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Bonisa konke"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Vula ukuze ukhokhele"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Akusethiwe"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Vula ukuze usebenzise"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kube khona inkinga yokuthola amakhadi akho, sicela uzame futhi ngemuva kwesikhathi"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Amasethingi okukhiya isikrini"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Iphrofayela yomsebenzi"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Imodi yendiza"</string>
<string name="add_tile" msgid="6239678623873086686">"Engeza ithayili"</string>
@@ -871,8 +863,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Vuliwe"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Valiwe"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Akutholakali"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Kukhutshaziwe"</string>
<string name="nav_bar" msgid="4642708685386136807">"Ibha yokuzula"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Isakhiwo"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Uhlobo lwenkinobho engakwesokunxele engeziwe"</string>
@@ -1160,4 +1151,6 @@
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"gunyaza"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"faka idivayisi"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Sebenzisa izigxivizo zeminwe ukuvula"</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Ukufakazela ubuqiniso budingekile. Thinta inzwa yezigxivizo zeminwe ukuze uqinisekise."</string>
+ <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ikholi yefoni eqhubekayo"</string>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index eb72442..067d56f 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -131,8 +131,6 @@
<declare-styleable name="SmartReplyView">
<attr name="spacing" format="dimension" />
- <attr name="singleLineButtonPaddingHorizontal" format="dimension" />
- <attr name="doubleLineButtonPaddingHorizontal" format="dimension" />
<attr name="buttonStrokeWidth" format="dimension" />
</declare-styleable>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 08a2e19..5559600 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -35,7 +35,6 @@
<color name="status_bar_clock_color">#FFFFFFFF</color>
<color name="qs_user_detail_icon_muted">#FFFFFFFF</color> <!-- not so muted after all -->
<color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
- <color name="qs_footer_action_border">#2E312C</color>
<!-- The color of the background in the separated list of the Global Actions menu -->
<color name="global_actions_separated_background">#F5F5F5</color>
@@ -247,6 +246,7 @@
<!-- Window magnification colors -->
<color name="magnification_border_color">#FF9900</color>
<color name="magnification_switch_button_color">#7F000000</color>
+ <color name="magnification_drag_handle_color">#B3000000</color>
<!-- Volume dialog colors -->
<color name="volume_dialog_background_color">@android:color/transparent</color>
@@ -286,7 +286,5 @@
<color name="accessibility_floating_menu_stroke_dark">#26FFFFFF</color> <!-- 15% -->
<!-- Wallet screen -->
- <color name="wallet_white">#FFFFFF</color>
<color name="wallet_card_border">#33FFFFFF</color>
- <color name="wallet_primary_text">@color/GM2_grey_900</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2283844..e7d714e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -623,8 +623,6 @@
<dimen name="qs_notif_collapsed_space">64dp</dimen>
- <dimen name="qs_container_bottom_padding">24dp</dimen>
-
<!-- Desired qs icon overlay size. -->
<dimen name="qs_detail_icon_overlay_size">24dp</dimen>
@@ -1147,15 +1145,14 @@
<!-- Smart reply button. Total height 48dp, visible height 32dp. -->
<dimen name="smart_reply_button_spacing">8dp</dimen>
<dimen name="smart_reply_button_padding_vertical">14dp</dimen>
- <!-- Note: The following two paddings need to be different until b/78876518 is fixed. -->
- <dimen name="smart_reply_button_padding_horizontal_single_line">20dp</dimen>
- <dimen name="smart_reply_button_padding_horizontal_double_line">19dp</dimen>
+ <dimen name="smart_reply_button_padding_horizontal">16dp</dimen>
+ <dimen name="smart_reply_button_action_padding_left">8dp</dimen>
<dimen name="smart_reply_button_min_height">48dp</dimen>
<dimen name="smart_reply_button_stroke_width">1dp</dimen>
<dimen name="smart_reply_button_font_size">14sp</dimen>
<dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
<!-- Corner radius = half of min_height to create rounded sides. -->
- <dimen name="smart_reply_button_corner_radius">24dp</dimen>
+ <dimen name="smart_reply_button_corner_radius">8dp</dimen>
<dimen name="smart_action_button_icon_size">18dp</dimen>
<dimen name="smart_action_button_icon_padding">8dp</dimen>
@@ -1293,9 +1290,9 @@
<dimen name="magnification_mirror_surface_margin">20dp</dimen>
<dimen name="magnification_frame_move_short">5dp</dimen>
<dimen name="magnification_frame_move_long">25dp</dimen>
- <dimen name="magnification_drag_view_size">38dp</dimen>
+ <dimen name="magnification_drag_view_size">36dp</dimen>
<dimen name="magnification_controls_size">90dp</dimen>
- <dimen name="magnification_switch_button_size">60dp</dimen>
+ <dimen name="magnification_switch_button_size">48dp</dimen>
<dimen name="magnification_switch_button_padding">6dp</dimen>
<dimen name="magnifier_left_right_controls_width">35dp</dimen>
<dimen name="magnifier_left_right_controls_height">45dp</dimen>
@@ -1412,6 +1409,26 @@
<dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
<dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
+ <!-- Delay after which the media will start transitioning to the full shade on
+ the lockscreen -->
+ <dimen name="lockscreen_shade_media_transition_start_delay">40dp</dimen>
+
+ <!-- Distance that the full shade transition takes in order for qs to fully transition to the
+ shade -->
+ <dimen name="lockscreen_shade_qs_transition_distance">200dp</dimen>
+
+ <!-- Distance that the full shade transition takes in order for scrim to fully transition to
+ the shade (in alpha) -->
+ <dimen name="lockscreen_shade_scrim_transition_distance">80dp</dimen>
+
+ <!-- Extra inset for the notifications when accounting for media during the lockscreen to
+ shade transition to compensate for the disappearing media -->
+ <dimen name="lockscreen_shade_transition_extra_media_inset">-48dp</dimen>
+
+ <!-- Maximum overshoot for the topPadding of notifications when transitioning to the full
+ shade -->
+ <dimen name="lockscreen_shade_max_top_overshoot">32dp</dimen>
+
<dimen name="people_space_widget_radius">28dp</dimen>
<dimen name="people_space_image_radius">20dp</dimen>
<dimen name="people_space_messages_count_radius">12dp</dimen>
@@ -1443,6 +1460,15 @@
<dimen name="accessibility_floating_menu_large_single_radius">33dp</dimen>
<dimen name="accessibility_floating_menu_large_multiple_radius">35dp</dimen>
+ <dimen name="accessibility_floating_tooltip_arrow_width">8dp</dimen>
+ <dimen name="accessibility_floating_tooltip_arrow_height">16dp</dimen>
+ <dimen name="accessibility_floating_tooltip_arrow_margin">-2dp</dimen>
+ <dimen name="accessibility_floating_tooltip_arrow_corner_radius">2dp</dimen>
+ <dimen name="accessibility_floating_tooltip_text_corner_radius">8dp</dimen>
+ <dimen name="accessibility_floating_tooltip_margin">16dp</dimen>
+ <dimen name="accessibility_floating_tooltip_padding">16dp</dimen>
+ <dimen name="accessibility_floating_tooltip_font_size">14sp</dimen>
+
<dimen name="rounded_slider_height">48dp</dimen>
<!-- rounded_slider_height / 2 -->
<dimen name="rounded_slider_corner_radius">24dp</dimen>
@@ -1484,7 +1510,8 @@
<!-- Wallet activity screen specs -->
<dimen name="wallet_icon_size">36sp</dimen>
- <dimen name="wallet_view_header_icon_size">56dp</dimen>
+ <dimen name="wallet_screen_header_icon_size">56dp</dimen>
+ <dimen name="wallet_screen_header_view_size">80dp</dimen>
<dimen name="card_margin">16dp</dimen>
<dimen name="card_carousel_dot_offset">24dp</dimen>
<dimen name="card_carousel_dot_unselected_radius">2dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 6104588..6393147 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -49,4 +49,6 @@
<bool name="flag_ongoing_call_status_bar_chip">true</bool>
<bool name="flag_smartspace">false</bool>
+
+ <bool name="flag_smartspace_deduping">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index bb9d331..bdc7bdb 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -157,9 +157,6 @@
<item type="id" name="accessibility_action_qs_move_to_position" />
<item type="id" name="accessibility_action_qs_add_to_position" />
- <!-- Accessibility actions for PIP -->
- <item type="id" name="action_pip_resize" />
-
<!-- Accessibility actions for window magnification. -->
<item type="id" name="accessibility_action_zoom_in"/>
<item type="id" name="accessibility_action_zoom_out"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 30add20..36a1bb3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -336,6 +336,8 @@
<string name="accessibility_phone_button">Phone</string>
<!-- Content description of the phone button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_voice_assist_button">Voice Assist</string>
+ <!-- Content description of the wallet button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_wallet_button">Wallet</string>
<!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_unlock_button">Unlock</string>
<!-- Content description of the lock icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -689,12 +691,8 @@
<string name="accessibility_quick_settings_hotspot_changed_on">Mobile hotspot turned on.</string>
<!-- Announcement made when the screen stopped casting (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_casting_turned_off">Screen casting stopped.</string>
- <!-- Content description of the work mode title in quick settings when off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_work_mode_off">Work mode paused.</string>
- <!-- Content description of the work mode title in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_work_mode_on">Work mode on.</string>
- <!-- Announcement made when the work mode changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_work_mode_changed_off">Work mode turned paused.</string>
+ <!-- Announcement made when the work mode changes to off (not shown on the screen). Paused is used as a verb. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_work_mode_changed_off">Work mode paused.</string>
<!-- Announcement made when the work mode changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_work_mode_changed_on">Work mode turned on.</string>
<!-- Announcement made when the Data Saver changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -951,11 +949,9 @@
<string name="quick_settings_cellular_detail_data_warning"><xliff:g id="data_limit" example="2.0 GB">%s</xliff:g> warning</string>
<!-- QuickSettings: This string is in the easy-to-view settings that a user can pull down from
the top of their phone's screen. This is a label for a toggle to turn the work profile on and
- off. "Work profile" means a separate profile on a user's phone that's specifically for their
+ off. This means a separate profile on a user's phone that's specifically for their
work apps and managed by their company. "Work" is used as an adjective. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_work_mode_label">Work profile</string>
- <!-- QuickSettings: Secondary label for work mode tile when it's off. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_work_mode_paused">Paused</string>
+ <string name="quick_settings_work_mode_label">Work apps</string>
<!-- QuickSettings: Label for the toggle to activate Night display (renamed "Night Light" with title caps). [CHAR LIMIT=20] -->
<string name="quick_settings_night_display_label">Night Light</string>
<!-- QuickSettings: Secondary text for when the Night Light will be enabled at sunset. [CHAR LIMIT=20] -->
@@ -2722,15 +2718,8 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle">toggle</string>
<!-- Device Controls strings -->
- <!-- Device Controls empty state, title [CHAR LIMIT=30] -->
- <string name="quick_controls_title">Device controls</string>
- <!-- Device Controls empty state, subtitle [CHAR LIMIT=100] -->
- <string name="quick_controls_subtitle">Add controls for your connected devices</string>
-
- <!-- Device Controls setup, title [CHAR LIMIT=50] -->
- <string name="quick_controls_setup_title">Set up device controls</string>
- <!-- Device Controls setup, subtitle [CHAR LIMIT=100] -->
- <string name="quick_controls_setup_subtitle">Hold the Power button to access your controls</string>
+ <!-- Device Controls, Quick Settings tile title [CHAR LIMIT=30] -->
+ <string name="quick_controls_title">Home controls</string>
<!-- Controls management providers screen title [CHAR LIMIT=60]-->
<string name="controls_providers_title">Choose app to add controls</string>
@@ -2759,7 +2748,7 @@
<!-- Controls management controls screen default title [CHAR LIMIT=30] -->
<string name="controls_favorite_default_title">Controls</string>
<!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
- <string name="controls_favorite_subtitle">Choose controls to access from the power menu</string>
+ <string name="controls_favorite_subtitle">Choose controls to access from Quick Settings</string>
<!-- Controls management editing screen, user direction for rearranging controls [CHAR LIMIT=NONE] -->
<string name="controls_favorite_rearrange">Hold & drag to rearrange controls</string>
@@ -2828,7 +2817,7 @@
<!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] -->
<string name="controls_media_settings_button">Settings</string>
- <!-- Title for Smartspace recommendation card within media controls [CHAR_LIMIT=50] -->
+ <!-- Title for Smartspace recommendation card within media controls. The "Play" means the action to play a media [CHAR_LIMIT=10] -->
<string name="controls_media_smartspace_rec_title">Play</string>
<!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
@@ -2851,7 +2840,7 @@
<!-- Stateless control message informing the user that a routine has started [CHAR_LIMIT=30] -->
<string name="controls_in_progress">In progress</string>
<!-- Tooltip informing user where the recently added controls are [CHAR_LIMIT=100] -->
- <string name="controls_added_tooltip">Hold Power button to see new controls</string>
+ <string name="controls_added_tooltip">Open Quick Settings to see new controls</string>
<!-- Controls menu, add [CHAR_LIMIT=30] -->
<string name="controls_menu_add">Add controls</string>
@@ -2950,8 +2939,6 @@
[CHAR LIMIT=NONE] -->
<string name="battery_state_unknown_notification_text">Tap for more information</string>
- <string name="qs_tile_label_fontFamily" translatable="false">@*android:string/config_headlineFontFamilyMedium</string>
-
<!-- Secondary label for alarm tile when there is no next alarm information [CHAR LIMIT=20] -->
<string name="qs_alarm_tile_no_alarm">No alarm set</string>
@@ -2965,7 +2952,11 @@
<!-- Accessibility action for tapping on an affordance on an unlocked lock screen (ie: "Double
tap to enter device") [CHAR LIMIT=NONE] -->
<string name="accessibility_enter_hint">enter device</string>
- <!-- Message shown to suggest authentication using [CHAR LIMIT=60]-->
+ <!-- Message shown to suggest authentication using fingerprint [CHAR LIMIT=60]-->
<string name="keyguard_try_fingerprint">Use fingerprint to open</string>
+ <!-- Accessibility announcement to inform user to unlock using the fingerprint sensor [CHAR LIMIT=NONE] -->
+ <string name="accessibility_fingerprint_bouncer">Authentication required. Touch the fingerprint sensor to authenticate.</string>
+ <!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
+ <string name="ongoing_phone_call_content_description">Ongoing phone call</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 10f7e40..9ce7589 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -197,12 +197,13 @@
<style name="TextAppearance.QS.TileLabel">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
- <item name="android:fontFamily">@string/qs_tile_label_fontFamily</item>
+ <item name="android:letterSpacing">0.01</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
</style>
<style name="TextAppearance.QS.TileLabel.Secondary">
- <item name="android:textSize">@dimen/qs_tile_text_size</item>
- <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
</style>
<style name="TextAppearance.QS.UserSwitcher">
@@ -221,11 +222,6 @@
<item name="android:textSize">@dimen/celltile_rat_type_size</item>
</style>
- <style name="TextAppearance.QS.SecurityFooter">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textSize">@dimen/qs_tile_text_size</item>
- </style>
-
<style name="TextAppearance.QS.Status">
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
@@ -234,14 +230,22 @@
<item name="android:lineHeight">20sp</item>
</style>
- <style name="TextAppearance.QS.Status.NoCarrierText">
- <item name="android:textColor">?android:attr/textColorTertiary</item>
- </style>
-
- <style name="TextAppearance.QS.Build">
+ <style name="TextAppearance.QS.SecurityFooter" parent="@style/TextAppearance.QS.Status">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
- <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="TextAppearance.QS.Status.Carriers">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ </style>
+
+ <style name="TextAppearance.QS.Status.Carriers.NoCarrierText">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="TextAppearance.QS.Status.Build">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="TextAppearance.DeviceManagementDialog">
@@ -603,11 +607,12 @@
parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info">
</style>
- <style name="TextAppearance.QSEdit.Headers"
- parent="@*android:style/TextAppearance.DeviceDefault.Body2">
- <item name="android:textSize">11sp</item>
+ <style name="TextAppearance.QSEdit" >
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.01</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
- <item name="android:textAllCaps">true</item>
</style>
<style name="QSCustomizeToolbar" parent="@*android:style/Widget.DeviceDefault.Toolbar">
@@ -649,20 +654,18 @@
</style>
<style name="MediaPlayer.SolidButton">
- <item name="android:backgroundTint">?android:attr/colorAccent</item>
+ <item name="android:backgroundTint">@color/media_player_solid_button_bg</item>
<item name="android:tint">?android:attr/colorPrimary</item>
- <item name="android:textColor">?android:attr/colorPrimary</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="MediaPlayer.AppIcon">
<item name="android:background">@drawable/qs_media_icon_background</item>
- <item name="android:backgroundTint">?android:attr/colorPrimary</item>
- <item name="android:tint">?android:attr/colorAccent</item>
+ <item name="android:backgroundTint">?android:attr/textColorPrimary</item>
</style>
<style name="MediaPlayer.Album">
<item name="android:backgroundTint">@color/media_player_album_bg</item>
-
</style>
<!-- Used to style charging animation AVD animation -->
@@ -692,6 +695,7 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowLightNavigationBar">true</item>
+ <item name="android:windowActivityTransitions">true</item>
</style>
<!-- Privacy dialog -->
@@ -874,12 +878,12 @@
<style name="Wallet.TextAppearance">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">@color/wallet_primary_text</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:singleLine">true</item>
<item name="android:textSize">14sp</item>
</style>
- <style name="Wallet.Theme">
- <item name="android:colorControlHighlight">@*android:color/primary_text_material_dark</item>
+ <style name="Wallet.Theme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:colorBackground">@android:color/system_neutral1_900</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml
index bdb8c04..a03a1d3 100644
--- a/packages/SystemUI/res/xml/media_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_collapsed.xml
@@ -69,6 +69,7 @@
android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
/>
<!-- Song name -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 1e98f86..f61cadb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -134,5 +134,8 @@
*/
void onBackPressed() = 44;
- // Next id = 45
+ /** Sets home rotation enabled. */
+ void setHomeRotationEnabled(boolean enabled) = 45;
+
+ // Next id = 46
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index bec9220..d40b94c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -40,6 +40,8 @@
InteractionJankMonitor.CUJ_LAUNCHER_OPEN_ALL_APPS;
public static final int CUJ_ALL_APPS_SCROLL =
InteractionJankMonitor.CUJ_LAUNCHER_ALL_APPS_SCROLL;
+ public static final int CUJ_APP_LAUNCH_FROM_WIDGET =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET;
@IntDef({
CUJ_APP_LAUNCH_FROM_RECENTS,
@@ -47,6 +49,7 @@
CUJ_APP_CLOSE_TO_HOME,
CUJ_APP_CLOSE_TO_PIP,
CUJ_QUICK_SWITCH,
+ CUJ_APP_LAUNCH_FROM_WIDGET,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 927bce0..2cf3ad2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -105,6 +105,8 @@
public static final int SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1 << 17;
// The IME is showing
public static final int SYSUI_STATE_IME_SHOWING = 1 << 18;
+ // The window magnification is overlapped with system gesture insets at the bottom.
+ public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -125,7 +127,8 @@
SYSUI_STATE_GLOBAL_ACTIONS_SHOWING,
SYSUI_STATE_ONE_HANDED_ACTIVE,
SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
- SYSUI_STATE_IME_SHOWING
+ SYSUI_STATE_IME_SHOWING,
+ SYSUI_STATE_MAGNIFICATION_OVERLAP
})
public @interface SystemUiStateFlags {}
@@ -153,6 +156,7 @@
str.add((flags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
? "allow_gesture" : "");
str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : "");
+ str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : "");
return str.toString();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 1cc488f..700ec49 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -19,6 +19,7 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.IRecentsAnimationController;
+import android.view.SurfaceControl;
import android.window.PictureInPictureSurfaceTransaction;
import android.window.TaskSnapshot;
@@ -76,11 +77,13 @@
* updated accordingly. This should be called before `finish`
* @param taskId Task id of the Activity in PiP mode.
* @param finishTransaction leash operations for the final transform.
+ * @param overlay the surface control for an overlay being shown above the pip (can be null)
*/
public void setFinishTaskTransaction(int taskId,
- PictureInPictureSurfaceTransaction finishTransaction) {
+ PictureInPictureSurfaceTransaction finishTransaction,
+ SurfaceControl overlay) {
try {
- mAnimationController.setFinishTaskTransaction(taskId, finishTransaction);
+ mAnimationController.setFinishTaskTransaction(taskId, finishTransaction, overlay);
} catch (RemoteException e) {
Log.d(TAG, "Failed to set finish task bounds", e);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 22d934e..ee55bf0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -28,6 +28,7 @@
import android.annotation.SuppressLint;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
@@ -158,10 +159,11 @@
public void startAnimation(IBinder token, TransitionInfo info,
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) {
+ final ArrayMap<SurfaceControl, SurfaceControl> leashMap = new ArrayMap<>();
final RemoteAnimationTargetCompat[] appsCompat =
- RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
+ RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */, t, leashMap);
final RemoteAnimationTargetCompat[] wallpapersCompat =
- RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
+ RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */, t, leashMap);
// TODO(bc-unlock): Build wrapped object for non-apps target.
final RemoteAnimationTargetCompat[] nonAppsCompat =
new RemoteAnimationTargetCompat[0];
@@ -211,7 +213,7 @@
// Need to "boost" the closing things since that's what launcher expects.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- final SurfaceControl leash = change.getLeash();
+ final SurfaceControl leash = leashMap.get(change.getLeash());
final int mode = info.getChanges().get(i).getMode();
// Only deal with independent layers
if (!TransitionInfo.isIndependent(change, info)) continue;
@@ -227,14 +229,14 @@
}
} else {
if (launcherTask != null) {
- counterLauncher.addChild(t, launcherTask.getLeash());
+ counterLauncher.addChild(t, leashMap.get(launcherTask.getLeash()));
}
if (wallpaper != null && rotateDelta != 0 && wallpaper.getParent() != null) {
counterWallpaper.setup(t, info.getChange(wallpaper.getParent()).getLeash(),
rotateDelta, displayW, displayH);
if (counterWallpaper.mSurface != null) {
t.setLayer(counterWallpaper.mSurface, -1);
- counterWallpaper.addChild(t, wallpaper.getLeash());
+ counterWallpaper.addChild(t, leashMap.get(wallpaper.getLeash()));
}
}
}
@@ -252,6 +254,12 @@
for (int i = 0; i < info.getChanges().size(); ++i) {
info.getChanges().get(i).getLeash().release();
}
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = 0; i < leashMap.size(); ++i) {
+ if (leashMap.keyAt(i) == leashMap.valueAt(i)) continue;
+ t.remove(leashMap.valueAt(i));
+ }
+ t.apply();
finishCallback.onTransitionFinished(null /* wct */);
} catch (RemoteException e) {
Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index fcab71c..2407d21 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -17,11 +17,21 @@
package com.android.systemui.shared.system;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.util.ArrayMap;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -97,10 +107,94 @@
}
}
- public RemoteAnimationTargetCompat(TransitionInfo.Change change, int order) {
+
+ /**
+ * Almost a copy of Transitions#setupStartState.
+ * TODO: remove when there is proper cross-process transaction sync.
+ */
+ @SuppressLint("NewApi")
+ private static void setupLeash(@NonNull SurfaceControl leash,
+ @NonNull TransitionInfo.Change change, int layer,
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
+ boolean isOpening = info.getType() == TRANSIT_OPEN || info.getType() == TRANSIT_TO_FRONT;
+ // Put animating stuff above this line and put static stuff below it.
+ int zSplitLine = info.getChanges().size();
+ // changes should be ordered top-to-bottom in z
+ final int mode = change.getMode();
+
+ // Don't move anything that isn't independent within its parents
+ if (!TransitionInfo.isIndependent(change, info)) {
+ if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
+ t.show(leash);
+ t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
+ }
+ return;
+ }
+
+ boolean hasParent = change.getParent() != null;
+
+ if (!hasParent) {
+ t.reparent(leash, info.getRootLeash());
+ t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
+ change.getStartAbsBounds().top - info.getRootOffset().y);
+ }
+ t.show(leash);
+ // Put all the OPEN/SHOW on top
+ if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
+ if (isOpening) {
+ t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
+ if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
+ // if transferred, it should be left visible.
+ t.setAlpha(leash, 0.f);
+ }
+ } else {
+ // put on bottom and leave it visible
+ t.setLayer(leash, zSplitLine - layer);
+ }
+ } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
+ if (isOpening) {
+ // put on bottom and leave visible
+ t.setLayer(leash, zSplitLine - layer);
+ } else {
+ // put on top
+ t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
+ }
+ } else { // CHANGE
+ t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
+ }
+ }
+
+ @SuppressLint("NewApi")
+ private static SurfaceControl createLeash(TransitionInfo info, TransitionInfo.Change change,
+ int order, SurfaceControl.Transaction t) {
+ // TODO: once we can properly sync transactions across process, then get rid of this leash.
+ if (change.getParent() != null && (change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+ // Special case for wallpaper atm. Normally these are left alone; but, a quirk of
+ // making leashes means we have to handle them specially.
+ return change.getLeash();
+ }
+ SurfaceControl leashSurface = new SurfaceControl.Builder()
+ .setName(change.getLeash().toString() + "_transition-leash")
+ .setContainerLayer().setParent(change.getParent() == null ? info.getRootLeash()
+ : info.getChange(change.getParent()).getLeash()).build();
+ // Copied Transitions setup code (which expects bottom-to-top order, so we swap here)
+ setupLeash(leashSurface, change, info.getChanges().size() - order, info, t);
+ t.reparent(change.getLeash(), leashSurface);
+ t.setAlpha(change.getLeash(), 1.0f);
+ t.show(change.getLeash());
+ t.setPosition(change.getLeash(), 0, 0);
+ t.setLayer(change.getLeash(), 0);
+ return leashSurface;
+ }
+
+ public RemoteAnimationTargetCompat(TransitionInfo.Change change, int order,
+ TransitionInfo info, SurfaceControl.Transaction t) {
taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;
mode = newModeToLegacyMode(change.getMode());
- leash = new SurfaceControlCompat(change.getLeash());
+
+ // TODO: once we can properly sync transactions across process, then get rid of this leash.
+ leash = new SurfaceControlCompat(createLeash(info, change, order, t));
+
isTranslucent = (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0
|| (change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0;
clipRect = null;
@@ -139,15 +233,21 @@
*
* @param wallpapers If true, this will return wallpaper targets; otherwise it returns
* non-wallpaper targets.
+ * @param leashMap Temporary map of change leash -> launcher leash. Is an output, so should be
+ * populated by this function. If null, it is ignored.
*/
- public static RemoteAnimationTargetCompat[] wrap(TransitionInfo info, boolean wallpapers) {
+ public static RemoteAnimationTargetCompat[] wrap(TransitionInfo info, boolean wallpapers,
+ SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>();
for (int i = 0; i < info.getChanges().size(); i++) {
boolean changeIsWallpaper =
(info.getChanges().get(i).getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0;
if (wallpapers != changeIsWallpaper) continue;
out.add(new RemoteAnimationTargetCompat(info.getChanges().get(i),
- info.getChanges().size() - i));
+ info.getChanges().size() - i, info, t));
+ if (leashMap == null) continue;
+ leashMap.put(info.getChanges().get(i).getLeash(),
+ out.get(out.size() - 1).leash.mSurfaceControl);
}
return out.toArray(new RemoteAnimationTargetCompat[out.size()]);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 5708855..653d730 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -29,6 +29,7 @@
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.IRecentsAnimationController;
import android.view.SurfaceControl;
@@ -108,10 +109,11 @@
public void startAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishedCallback) {
+ final ArrayMap<SurfaceControl, SurfaceControl> leashMap = new ArrayMap<>();
final RemoteAnimationTargetCompat[] apps =
- RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
+ RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */, t, leashMap);
final RemoteAnimationTargetCompat[] wallpapers =
- RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
+ RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */, t, leashMap);
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
mToken = transition;
// This transition is for opening recents, so recents is on-top. We want to draw
@@ -120,7 +122,8 @@
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) {
- t.setLayer(change.getLeash(), info.getChanges().size() * 3 - i);
+ t.setLayer(leashMap.get(change.getLeash()),
+ info.getChanges().size() * 3 - i);
if (change.getTaskInfo() != null) {
pausingTask = change.getTaskInfo().token;
}
@@ -131,7 +134,8 @@
t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);
}
t.apply();
- mRecentsSession.setup(controller, info, finishedCallback, pausingTask);
+ mRecentsSession.setup(controller, info, finishedCallback, pausingTask,
+ leashMap);
recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
new Rect());
}
@@ -173,9 +177,11 @@
private WindowContainerToken mPausingTask = null;
private TransitionInfo mInfo = null;
private SurfaceControl mOpeningLeash = null;
+ private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
- IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask) {
+ IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask,
+ ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
if (mInfo != null) {
throw new IllegalStateException("Trying to run a new recents animation while"
+ " recents is already active.");
@@ -184,6 +190,7 @@
mInfo = info;
mFinishCB = finishCB;
mPausingTask = pausingTask;
+ mLeashMap = leashMap;
}
@SuppressLint("NewApi")
@@ -211,11 +218,14 @@
}
// We are receiving a new opening task, so convert to onTaskAppeared.
final int layer = mInfo.getChanges().size() * 3;
- t.reparent(mOpeningLeash, mInfo.getRootLeash());
- t.setLayer(mOpeningLeash, layer);
- t.hide(mOpeningLeash);
+ final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
+ openingTask, layer, mInfo, t);
+ mLeashMap.put(mOpeningLeash, target.leash.mSurfaceControl);
+ t.reparent(target.leash.mSurfaceControl, mInfo.getRootLeash());
+ t.setLayer(target.leash.mSurfaceControl, layer);
+ t.hide(target.leash.mSurfaceControl);
t.apply();
- recents.onTaskAppeared(new RemoteAnimationTargetCompat(openingTask, layer));
+ recents.onTaskAppeared(target);
return true;
}
@@ -236,9 +246,9 @@
}
@Override public void setFinishTaskTransaction(int taskId,
- PictureInPictureSurfaceTransaction finishTransaction) {
+ PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
if (mWrapped != null) {
- mWrapped.setFinishTaskTransaction(taskId, finishTransaction);
+ mWrapped.setFinishTaskTransaction(taskId, finishTransaction, overlay);
}
}
@@ -272,6 +282,12 @@
}
// Release surface references now. This is apparently to free GPU
// memory while doing quick operations (eg. during CTS).
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = 0; i < mLeashMap.size(); ++i) {
+ if (mLeashMap.keyAt(i) == mLeashMap.valueAt(i)) continue;
+ t.remove(mLeashMap.valueAt(i));
+ }
+ t.apply();
for (int i = 0; i < mInfo.getChanges().size(); ++i) {
mInfo.getChanges().get(i).getLeash().release();
}
@@ -281,6 +297,7 @@
mPausingTask = null;
mInfo = null;
mOpeningLeash = null;
+ mLeashMap = null;
}
@Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 825e008..332de6c 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -37,7 +37,8 @@
import java.util.TimeZone;
/**
- * Controller for an AnimatableClockView. Instantiated by {@link KeyguardClockSwitchController}.
+ * Controller for an AnimatableClockView on the keyguard. Instantiated by
+ * {@link KeyguardClockSwitchController}.
*/
public class AnimatableClockController extends ViewController<AnimatableClockView> {
private static final int FORMAT_NUMBER = 1234567890;
@@ -46,6 +47,7 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardBypassController mBypassController;
+ private final BatteryController mBatteryController;
private final int mDozingColor = Color.WHITE;
private int mLockScreenColor;
@@ -53,6 +55,7 @@
private boolean mIsCharging;
private float mDozeAmount;
private Locale mLocale;
+ private boolean mAttached; // if keyguard isn't showing, mAttached = false
private final NumberFormat mBurmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"));
private final String mBurmeseNumerals;
@@ -73,44 +76,47 @@
mBroadcastDispatcher = broadcastDispatcher;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBypassController = bypassController;
+ mBatteryController = batteryController;
mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
mBurmeseLineSpacing = getContext().getResources().getFloat(
R.dimen.keyguard_clock_line_spacing_scale_burmese);
mDefaultLineSpacing = getContext().getResources().getFloat(
R.dimen.keyguard_clock_line_spacing_scale);
-
- batteryController.addCallback(new BatteryController.BatteryStateChangeCallback() {
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- if (!mIsCharging && charging) {
- mView.animateCharge(mIsDozing);
- }
- mIsCharging = charging;
- }
- });
}
- private BroadcastReceiver mLocaleBroadcastReceiver = new BroadcastReceiver() {
+ private final BatteryController.BatteryStateChangeCallback mBatteryCallback =
+ new BatteryController.BatteryStateChangeCallback() {
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ if (!mIsCharging && charging) {
+ mView.animateCharge(mIsDozing);
+ }
+ mIsCharging = charging;
+ }
+ };
+
+ private final BroadcastReceiver mLocaleBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateLocale();
}
};
- @Override
- protected void onViewAttached() {
- updateLocale();
- mBroadcastDispatcher.registerReceiver(mLocaleBroadcastReceiver,
- new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mIsDozing = mStatusBarStateController.isDozing();
- mDozeAmount = mStatusBarStateController.getDozeAmount();
- mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
-
- refreshTime();
- initColors();
- }
+ private final KeyguardUpdateMonitorCallback mKeyguardPersistentCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ // call attached/detached methods on visibility changes. benefits include:
+ // - no animations when keyguard/clock view aren't visible
+ // - resets state when keyguard is visible again (ie: font weight)
+ if (showing) {
+ onViewAttached();
+ } else {
+ onViewDetached();
+ }
+ }
+ };
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@@ -125,10 +131,46 @@
};
@Override
+ protected void onViewAttached() {
+ if (mAttached) {
+ return;
+ }
+ mAttached = true;
+ updateLocale();
+ mBroadcastDispatcher.registerReceiver(mLocaleBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mIsDozing = mStatusBarStateController.isDozing();
+ mDozeAmount = mStatusBarStateController.getDozeAmount();
+ mBatteryController.addCallback(mBatteryCallback);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardPersistentCallback);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardPersistentCallback);
+
+ refreshTime();
+ initColors();
+ }
+
+ @Override
protected void onViewDetached() {
+ if (!mAttached) {
+ return;
+ }
+
+ mAttached = false;
mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
+ mBatteryController.removeCallback(mBatteryCallback);
+ if (!mView.isAttachedToWindow()) {
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardPersistentCallback);
+ }
+ }
+
+ /** Animate the clock appearance */
+ public void animateAppear() {
+ if (!mIsDozing) mView.animateAppearOnLockscreen();
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index 41d991c..63867c0 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -44,6 +44,7 @@
private static final CharSequence SINGLE_LINE_FORMAT_12_HOUR = "h:mm";
private static final CharSequence SINGLE_LINE_FORMAT_24_HOUR = "HH:mm";
private static final long DOZE_ANIM_DURATION = 300;
+ private static final long APPEAR_ANIM_DURATION = 350;
private static final long CHARGE_ANIM_DURATION_PHASE_0 = 500;
private static final long CHARGE_ANIM_DURATION_PHASE_1 = 1000;
@@ -156,6 +157,30 @@
mLockScreenColor = lockScreenColor;
}
+ void animateAppearOnLockscreen() {
+ if (mTextAnimator == null) {
+ return;
+ }
+
+ setTextStyle(
+ mDozingWeight,
+ -1 /* text size, no update */,
+ mLockScreenColor,
+ false /* animate */,
+ 0 /* duration */,
+ 0 /* delay */,
+ null /* onAnimationEnd */);
+
+ setTextStyle(
+ mLockScreenWeight,
+ -1 /* text size, no update */,
+ mLockScreenColor,
+ true, /* animate */
+ APPEAR_ANIM_DURATION,
+ 0 /* delay */,
+ null /* onAnimationEnd */);
+ }
+
void animateDisappear() {
if (mTextAnimator == null) {
return;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 407146f..1c4cde8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -35,6 +35,7 @@
private static final long CLOCK_OUT_MILLIS = 150;
private static final long CLOCK_IN_MILLIS = 200;
+ private static final long SMARTSPACE_MOVE_MILLIS = 350;
/**
* Optional/alternative clock injected via plugin.
@@ -54,6 +55,8 @@
* show it below the alternate clock.
*/
private View mKeyguardStatusArea;
+ /** Mutually exclusive with mKeyguardStatusArea */
+ private View mSmartspaceView;
/**
* Maintain state so that a newly connected plugin can be initialized.
@@ -67,6 +70,7 @@
private AnimatorSet mClockInAnim = null;
private AnimatorSet mClockOutAnim = null;
+ private ObjectAnimator mSmartspaceAnim = null;
/**
* If the Keyguard Slice has a header (big center-aligned text.)
@@ -177,17 +181,22 @@
private void animateClockChange(boolean useLargeClock) {
if (mClockInAnim != null) mClockInAnim.cancel();
if (mClockOutAnim != null) mClockOutAnim.cancel();
+ if (mSmartspaceAnim != null) mSmartspaceAnim.cancel();
View in, out;
int direction = 1;
+ float smartspaceYTranslation;
if (useLargeClock) {
out = mClockFrame;
in = mLargeClockFrame;
if (indexOfChild(in) == -1) addView(in);
direction = -1;
+ smartspaceYTranslation = mSmartspaceView == null ? 0
+ : mClockFrame.getY() - mSmartspaceView.getY();
} else {
in = mClockFrame;
out = mLargeClockFrame;
+ smartspaceYTranslation = 0f;
// Must remove in order for notifications to appear in the proper place
removeView(out);
@@ -222,6 +231,19 @@
mClockInAnim.start();
mClockOutAnim.start();
+
+ if (mSmartspaceView != null) {
+ mSmartspaceAnim = ObjectAnimator.ofFloat(mSmartspaceView, View.TRANSLATION_Y,
+ smartspaceYTranslation);
+ mSmartspaceAnim.setDuration(SMARTSPACE_MOVE_MILLIS);
+ mSmartspaceAnim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mSmartspaceAnim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ mSmartspaceAnim = null;
+ }
+ });
+ mSmartspaceAnim.start();
+ }
}
/**
@@ -237,15 +259,18 @@
}
/**
- * Set whether or not the lock screen is showing notifications.
+ * Based upon whether notifications are showing or not, display/hide the large clock and
+ * the smaller version.
*/
- void setHasVisibleNotifications(boolean hasVisibleNotifications) {
+ boolean willSwitchToLargeClock(boolean hasVisibleNotifications) {
if (hasVisibleNotifications == mHasVisibleNotifications) {
- return;
+ return false;
}
- animateClockChange(!hasVisibleNotifications);
+ boolean useLargeClock = !hasVisibleNotifications;
+ animateClockChange(useLargeClock);
mHasVisibleNotifications = hasVisibleNotifications;
+ return useLargeClock;
}
public Paint getPaint() {
@@ -303,6 +328,10 @@
}
}
+ void setSmartspaceView(View smartspaceView) {
+ mSmartspaceView = smartspaceView;
+ }
+
void updateColors(ColorExtractor.GradientColors colors) {
mSupportsDarkText = colors.supportsDarkText();
mColorPalette = colors.getColorPalette();
@@ -317,6 +346,7 @@
pw.println(" mClockFrame: " + mClockFrame);
pw.println(" mLargeClockFrame: " + mLargeClockFrame);
pw.println(" mKeyguardStatusArea: " + mKeyguardStatusArea);
+ pw.println(" mSmartspaceView: " + mSmartspaceView);
pw.println(" mDarkAmount: " + mDarkAmount);
pw.println(" mSupportsDarkText: " + mSupportsDarkText);
pw.println(" mColorPalette: " + Arrays.toString(mColorPalette));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 4b71a3a..aa7f9a2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -19,42 +19,22 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.app.PendingIntent;
import android.app.WallpaperManager;
-import android.app.smartspace.SmartspaceConfig;
-import android.app.smartspace.SmartspaceManager;
-import android.app.smartspace.SmartspaceSession;
-import android.app.smartspace.SmartspaceTarget;
-import android.content.Intent;
-import android.content.pm.UserInfo;
import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin.IntentStarter;
import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -62,14 +42,10 @@
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
-import com.android.systemui.util.settings.SecureSettings;
import java.util.Locale;
-import java.util.Optional;
import java.util.TimeZone;
-import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -85,9 +61,8 @@
private final KeyguardSliceViewController mKeyguardSliceViewController;
private final NotificationIconAreaController mNotificationIconAreaController;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final Executor mUiExecutor;
private final BatteryController mBatteryController;
- private final FeatureFlags mFeatureFlags;
+ private final LockscreenSmartspaceController mSmartspaceController;
/**
* Clock for both small and large sizes
@@ -97,20 +72,8 @@
private AnimatableClockController mLargeClockViewController;
private FrameLayout mLargeClockFrame;
- private SmartspaceSession mSmartspaceSession;
- private SmartspaceSession.OnTargetsAvailableListener mSmartspaceCallback;
- private ConfigurationController mConfigurationController;
- private ActivityStarter mActivityStarter;
- private FalsingManager mFalsingManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardBypassController mBypassController;
- private Handler mHandler;
- private UserTracker mUserTracker;
- private SecureSettings mSecureSettings;
- private ContentObserver mSettingsObserver;
- private boolean mShowSensitiveContentForCurrentUser;
- private boolean mShowSensitiveContentForManagedUser;
- private UserHandle mManagedUserHandle;
/**
* Listener for changes to the color palette.
@@ -118,59 +81,30 @@
* The color palette changes when the wallpaper is changed.
*/
private final ColorExtractor.OnColorsChangedListener mColorsListener =
- new ColorExtractor.OnColorsChangedListener() {
- @Override
- public void onColorsChanged(ColorExtractor extractor, int which) {
- if ((which & WallpaperManager.FLAG_LOCK) != 0) {
- mView.updateColors(getGradientColors());
- }
- }
- };
-
- private final ConfigurationController.ConfigurationListener mConfigurationListener =
- new ConfigurationController.ConfigurationListener() {
- @Override
- public void onThemeChanged() {
- updateWallpaperColor();
- }
- };
-
- private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
-
- private final StatusBarStateController.StateListener mStatusBarStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onDozeAmountChanged(float linear, float eased) {
- if (mSmartspaceView != null) {
- mSmartspaceView.setDozeAmount(eased);
- }
+ (extractor, which) -> {
+ if ((which & WallpaperManager.FLAG_LOCK) != 0) {
+ mView.updateColors(getGradientColors());
}
};
+ private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
+
// If set, will replace keyguard_status_area
- private BcSmartspaceDataPlugin.SmartspaceView mSmartspaceView;
- private Optional<BcSmartspaceDataPlugin> mSmartspacePlugin;
+ private View mSmartspaceView;
@Inject
public KeyguardClockSwitchController(
KeyguardClockSwitch keyguardClockSwitch,
StatusBarStateController statusBarStateController,
- SysuiColorExtractor colorExtractor, ClockManager clockManager,
+ SysuiColorExtractor colorExtractor,
+ ClockManager clockManager,
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
BroadcastDispatcher broadcastDispatcher,
- FeatureFlags featureFlags,
- @Main Executor uiExecutor,
BatteryController batteryController,
- ConfigurationController configurationController,
- ActivityStarter activityStarter,
- FalsingManager falsingManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController bypassController,
- @Main Handler handler,
- UserTracker userTracker,
- SecureSettings secureSettings,
- Optional<BcSmartspaceDataPlugin> smartspacePlugin) {
+ LockscreenSmartspaceController smartspaceController) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
@@ -178,18 +112,10 @@
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
mBroadcastDispatcher = broadcastDispatcher;
- mFeatureFlags = featureFlags;
- mUiExecutor = uiExecutor;
mBatteryController = batteryController;
- mConfigurationController = configurationController;
- mActivityStarter = activityStarter;
- mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBypassController = bypassController;
- mHandler = handler;
- mUserTracker = userTracker;
- mSecureSettings = secureSettings;
- mSmartspacePlugin = smartspacePlugin;
+ mSmartspaceController = smartspaceController;
}
/**
@@ -198,16 +124,6 @@
@Override
public void onInit() {
mKeyguardSliceViewController.init();
- }
-
- @Override
- protected void onViewAttached() {
- if (CUSTOM_CLOCKS_ENABLED) {
- mClockManager.addOnClockChangedListener(mClockChangedListener);
- }
- mColorExtractor.addOnColorsChangedListener(mColorsListener);
- mView.updateColors(getGradientColors());
- updateAodIcons();
mClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
@@ -231,120 +147,46 @@
mKeyguardUpdateMonitor,
mBypassController);
mLargeClockViewController.init();
+ }
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mConfigurationController.addCallback(mConfigurationListener);
+ @Override
+ protected void onViewAttached() {
+ if (CUSTOM_CLOCKS_ENABLED) {
+ mClockManager.addOnClockChangedListener(mClockChangedListener);
+ }
+ mColorExtractor.addOnColorsChangedListener(mColorsListener);
+ mView.updateColors(getGradientColors());
+ updateAodIcons();
- if (mFeatureFlags.isSmartspaceEnabled() && mSmartspacePlugin.isPresent()) {
- BcSmartspaceDataPlugin smartspaceDataPlugin = mSmartspacePlugin.get();
+ if (mSmartspaceController.isEnabled()) {
+ mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
+
View ksa = mView.findViewById(R.id.keyguard_status_area);
int ksaIndex = mView.indexOfChild(ksa);
ksa.setVisibility(View.GONE);
- mSmartspaceView = smartspaceDataPlugin.getView(mView);
- mSmartspaceView.registerDataProvider(smartspaceDataPlugin);
- mSmartspaceView.setIntentStarter(new IntentStarter() {
- public void startIntent(View v, Intent i) {
- mActivityStarter.startActivity(i, true /* dismissShade */);
- }
-
- public void startPendingIntent(PendingIntent pi) {
- mActivityStarter.startPendingIntentDismissingKeyguard(pi);
- }
- });
- mSmartspaceView.setFalsingManager(mFalsingManager);
- updateWallpaperColor();
- View asView = (View) mSmartspaceView;
-
// Place smartspace view below normal clock...
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
MATCH_PARENT, WRAP_CONTENT);
lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view);
- mView.addView(asView, ksaIndex, lp);
+ mView.addView(mSmartspaceView, ksaIndex, lp);
int padding = getContext().getResources()
.getDimensionPixelSize(R.dimen.below_clock_padding_start);
- asView.setPadding(padding, 0, padding, 0);
+ mSmartspaceView.setPadding(padding, 0, padding, 0);
// ... but above the large clock
lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
- lp.addRule(RelativeLayout.BELOW, asView.getId());
+ lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
mLargeClockFrame.setLayoutParams(lp);
View nic = mView.findViewById(
R.id.left_aligned_notification_icon_container);
lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
- lp.addRule(RelativeLayout.BELOW, asView.getId());
+ lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
nic.setLayoutParams(lp);
- mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
- .createSmartspaceSession(
- new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
- mSmartspaceCallback = targets -> {
- targets.removeIf(this::filterSmartspaceTarget);
- smartspaceDataPlugin.onTargetsAvailable(targets);
- };
- mSmartspaceSession.addOnTargetsAvailableListener(mUiExecutor, mSmartspaceCallback);
- mSettingsObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- reloadSmartspace();
- }
- };
-
- getContext().getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
- true, mSettingsObserver, UserHandle.USER_ALL);
- reloadSmartspace();
- }
-
- float dozeAmount = mStatusBarStateController.getDozeAmount();
- mStatusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
- }
-
- @VisibleForTesting
- boolean filterSmartspaceTarget(SmartspaceTarget t) {
- if (!t.isSensitive()) return false;
-
- if (t.getUserHandle().equals(mUserTracker.getUserHandle())) {
- return !mShowSensitiveContentForCurrentUser;
- }
- if (t.getUserHandle().equals(mManagedUserHandle)) {
- return !mShowSensitiveContentForManagedUser;
- }
-
- return false;
- }
-
- private void reloadSmartspace() {
- mManagedUserHandle = getWorkProfileUser();
- final String setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
-
- mShowSensitiveContentForCurrentUser =
- mSecureSettings.getIntForUser(setting, 0, mUserTracker.getUserId()) == 1;
- if (mManagedUserHandle != null) {
- int id = mManagedUserHandle.getIdentifier();
- mShowSensitiveContentForManagedUser =
- mSecureSettings.getIntForUser(setting, 0, id) == 1;
- }
-
- mSmartspaceSession.requestSmartspaceUpdate();
- }
-
- private UserHandle getWorkProfileUser() {
- for (UserInfo userInfo : mUserTracker.getUserProfiles()) {
- if (userInfo.isManagedProfile()) {
- return userInfo.getUserHandle();
- }
- }
- return null;
- }
-
- private void updateWallpaperColor() {
- if (mSmartspaceView != null) {
- int color = Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColor);
- mSmartspaceView.setPrimaryTextColor(color);
+ mView.setSmartspaceView(mSmartspaceView);
}
}
@@ -356,16 +198,16 @@
mColorExtractor.removeOnColorsChangedListener(mColorsListener);
mView.setClockPlugin(null, mStatusBarStateController.getState());
- if (mSmartspaceSession != null) {
- mSmartspaceSession.removeOnTargetsAvailableListener(mSmartspaceCallback);
- mSmartspaceSession.close();
- mSmartspaceSession = null;
- }
- mStatusBarStateController.removeCallback(mStatusBarStateListener);
- mConfigurationController.removeCallback(mConfigurationListener);
+ mSmartspaceController.disconnect();
- if (mSettingsObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mSettingsObserver);
+ // TODO: This is an unfortunate necessity since smartspace plugin retains a single instance
+ // of the smartspace view -- if we don't remove the view, it can't be reused by a later
+ // instance of this class. In order to fix this, we need to modify the plugin so that
+ // (a) we get a new view each time and (b) we can properly clean up an old view by making
+ // it unregister itself as a plugin listener.
+ if (mSmartspaceView != null) {
+ mView.removeView(mSmartspaceView);
+ mSmartspaceView = null;
}
}
@@ -380,7 +222,9 @@
* Set whether or not the lock screen is showing notifications.
*/
public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
- mView.setHasVisibleNotifications(hasVisibleNotifications);
+ if (mView.willSwitchToLargeClock(hasVisibleNotifications)) {
+ mLargeClockViewController.animateAppear();
+ }
}
/**
@@ -415,6 +259,9 @@
mClockViewController.refreshTime();
mLargeClockViewController.refreshTime();
}
+ if (mSmartspaceController != null) {
+ mSmartspaceController.requestSmartspaceUpdate();
+ }
mView.refresh();
}
@@ -436,7 +283,7 @@
scale, props, animate);
if (mSmartspaceView != null) {
- PropertyAnimator.setProperty((View) mSmartspaceView, AnimatableProperty.TRANSLATION_X,
+ PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X,
x, props, animate);
}
@@ -463,8 +310,6 @@
NotificationIconContainer nic = (NotificationIconContainer)
mView.findViewById(
com.android.systemui.R.id.left_aligned_notification_icon_container);
-
- // alt icon area is set in KeyguardClockSwitchController
mNotificationIconAreaController.setupAodIcons(nic);
}
@@ -510,14 +355,4 @@
private int getCurrentLayoutDirection() {
return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
}
-
- @VisibleForTesting
- ConfigurationController.ConfigurationListener getConfigurationListener() {
- return mConfigurationListener;
- }
-
- @VisibleForTesting
- ContentObserver getSettingsObserver() {
- return mSettingsObserver;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index ab15630..ff20805 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -12,6 +12,7 @@
val isListeningForFace: Boolean,
val isBouncer: Boolean,
val isAuthInterruptActive: Boolean,
+ val isOccludingAppRequestingFaceAuth: Boolean,
val isKeyguardAwake: Boolean,
val isListeningForFaceAssistant: Boolean,
val isSwitchingUser: Boolean,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index d06c8bc..3ebd652 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -481,7 +481,7 @@
if (resources.getBoolean(R.bool.can_use_one_handed_bouncer)
&& resources.getBoolean(
- com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
gravity = resources.getInteger(
R.integer.keyguard_host_view_one_handed_gravity);
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 54ecf95..ca4d73b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -278,7 +278,7 @@
private boolean canUseOneHandedBouncer() {
// Is it enabled?
if (!getResources().getBoolean(
- com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 588f4bb..388c085 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -51,8 +51,6 @@
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
private final Rect mClipBounds = new Rect();
- private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
-
@Inject
public KeyguardStatusViewController(
KeyguardStatusView keyguardStatusView,
@@ -192,28 +190,18 @@
* Update position of the view with an optional animation
*/
public void updatePosition(int x, int y, float scale, boolean animate) {
- // We animate the status view visible/invisible using Y translation, so don't change it
- // while the animation is running.
- if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
- PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
- animate);
- }
+ PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
+ animate);
- if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
- // reset any prior movement
- PropertyAnimator.setProperty(mView, AnimatableProperty.X, 0,
- CLOCK_ANIMATION_PROPERTIES, animate);
+ mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES,
+ animate);
+ }
- mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES,
- animate);
- } else {
- // reset any prior movement
- mKeyguardClockSwitchController.updatePosition(0, 0f, CLOCK_ANIMATION_PROPERTIES,
- animate);
-
- PropertyAnimator.setProperty(mView, AnimatableProperty.X, x,
- CLOCK_ANIMATION_PROPERTIES, animate);
- }
+ /**
+ * @return {@code true} if we are currently animating the screen off from unlock
+ */
+ public boolean isAnimatingScreenOffFromUnlocked() {
+ return mKeyguardVisibilityHelper.isAnimatingScreenOffFromUnlocked();
}
/**
@@ -254,7 +242,6 @@
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onLockScreenModeChanged(int mode) {
- mLockScreenMode = mode;
mKeyguardSliceViewController.updateLockScreenMode(mode);
mView.setCanShowOwnerInfo(false);
mView.updateLogoutView(false);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 321c6b7..44df02a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -252,7 +252,6 @@
@Override
public void onStateChanged(int newState) {
mStatusBarState = newState;
- updateBiometricListeningState();
}
@Override
@@ -276,6 +275,8 @@
private boolean mHasLockscreenWallpaper;
private boolean mAssistantVisible;
private boolean mKeyguardOccluded;
+ private boolean mOccludingAppRequestingFp;
+ private boolean mOccludingAppRequestingFace;
private boolean mSecureCameraLaunched;
@VisibleForTesting
protected boolean mTelephonyCapable;
@@ -587,6 +588,29 @@
updateBiometricListeningState();
}
+
+ /**
+ * Request to listen for face authentication when an app is occluding keyguard.
+ * @param request if true and mKeyguardOccluded, request face auth listening, else default
+ * to normal behavior.
+ * See {@link KeyguardUpdateMonitor#shouldListenForFace()}
+ */
+ public void requestFaceAuthOnOccludingApp(boolean request) {
+ mOccludingAppRequestingFace = request;
+ updateFaceListeningState();
+ }
+
+ /**
+ * Request to listen for fingerprint when an app is occluding keyguard.
+ * @param request if true and mKeyguardOccluded, request fingerprint listening, else default
+ * to normal behavior.
+ * See {@link KeyguardUpdateMonitor#shouldListenForFingerprint(boolean)}
+ */
+ public void requestFingerprintAuthOnOccludingApp(boolean request) {
+ mOccludingAppRequestingFp = request;
+ updateFingerprintListeningState();
+ }
+
/**
* Invoked when the secure camera is launched.
*/
@@ -2093,14 +2117,16 @@
@VisibleForTesting
protected boolean shouldListenForFingerprint(boolean isUdfps) {
+ final boolean userDoesNotHaveTrust = !getUserHasTrust(getCurrentUser());
final boolean shouldListenKeyguardState =
- mKeyguardIsVisible
- || !mDeviceInteractive
- || (mBouncer && !mKeyguardGoingAway)
- || mGoingToSleep
- || shouldListenForFingerprintAssistant()
- || (mKeyguardOccluded && mIsDreaming)
- || (isUdfps && mKeyguardOccluded);
+ mKeyguardIsVisible
+ || !mDeviceInteractive
+ || (mBouncer && !mKeyguardGoingAway)
+ || mGoingToSleep
+ || shouldListenForFingerprintAssistant()
+ || (mKeyguardOccluded && mIsDreaming)
+ || (mKeyguardOccluded && userDoesNotHaveTrust
+ && (mOccludingAppRequestingFp || isUdfps));
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -2116,8 +2142,9 @@
final boolean shouldListenUdfpsState = !isUdfps
|| (!getUserCanSkipBouncer(getCurrentUser())
- && !isEncryptedOrLockdown(getCurrentUser())
- && mStrongAuthTracker.hasUserAuthenticatedSinceBoot());
+ && !isEncryptedOrLockdown(getCurrentUser())
+ && mStrongAuthTracker.hasUserAuthenticatedSinceBoot()
+ && userDoesNotHaveTrust);
return shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState
&& shouldListenUdfpsState;
@@ -2166,12 +2193,12 @@
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
final boolean shouldListen =
- (mBouncer || mAuthInterruptActive || awakeKeyguard
+ (mBouncer || mAuthInterruptActive || mOccludingAppRequestingFace || awakeKeyguard
|| shouldListenForFaceAssistant())
&& !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer
&& !mKeyguardGoingAway && mBiometricEnabledForUser.get(user) && !mLockIconPressed
&& strongAuthAllowsScanning && mIsPrimaryUser
- && !mSecureCameraLaunched;
+ && (!mSecureCameraLaunched || mOccludingAppRequestingFace);
// Aggregate relevant fields for debug logging.
if (DEBUG_FACE || DEBUG_SPEW) {
@@ -2181,6 +2208,7 @@
shouldListen,
mBouncer,
mAuthInterruptActive,
+ mOccludingAppRequestingFace,
awakeKeyguard,
shouldListenForFaceAssistant(),
mSwitchingUser,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 947f141..b6a58dc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -22,6 +22,9 @@
import com.android.systemui.animation.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.phone.DozeParameters;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -36,6 +39,9 @@
private final KeyguardStateController mKeyguardStateController;
private final DozeParameters mDozeParameters;
private boolean mKeyguardViewVisibilityAnimating;
+ private boolean mLastOccludedState = false;
+ private boolean mAnimatingScreenOff;
+ private final AnimationProperties mAnimationProperties = new AnimationProperties();
public KeyguardVisibilityHelper(View view, KeyguardStateController keyguardStateController,
DozeParameters dozeParameters) {
@@ -57,6 +63,7 @@
boolean goingToFullShade,
int oldStatusBarState) {
mView.animate().cancel();
+ boolean isOccluded = mKeyguardStateController.isOccluded();
mKeyguardViewVisibilityAnimating = false;
if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
&& statusBarState != KEYGUARD) || goingToFullShade) {
@@ -87,30 +94,60 @@
} else if (statusBarState == KEYGUARD) {
if (keyguardFadingAway) {
mKeyguardViewVisibilityAnimating = true;
+ float target = mView.getY() - mView.getHeight() * 0.05f;
+ int delay = 0;
+ int duration = 125;
+ // We animate the Y properly separately using the PropertyAnimator, as the panel
+ // view als needs to update the end position.
+ mAnimationProperties.setDuration(duration).setDelay(delay);
+ PropertyAnimator.cancelAnimation(mView, AnimatableProperty.Y);
+ PropertyAnimator.setProperty(mView, AnimatableProperty.Y, target,
+ mAnimationProperties,
+ true /* animate */);
mView.animate()
.alpha(0)
- .translationYBy(-mView.getHeight() * 0.05f)
.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
- .setDuration(125)
- .setStartDelay(0)
+ .setDuration(duration)
+ .setStartDelay(delay)
.withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
.start();
- } else if (mDozeParameters.shouldControlUnlockedScreenOff()) {
- mKeyguardViewVisibilityAnimating = true;
-
+ } else if (mLastOccludedState && !isOccluded) {
+ // An activity was displayed over the lock screen, and has now gone away
mView.setVisibility(View.VISIBLE);
mView.setAlpha(0f);
- float curTranslationY = mView.getTranslationY();
- mView.setTranslationY(curTranslationY - mView.getHeight() * 0.1f);
mView.animate()
- .setStartDelay((int) (StackStateAnimator.ANIMATION_DURATION_WAKEUP * .6f))
.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(1f)
- .translationY(curTranslationY)
.withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
.start();
+ } else if (mDozeParameters.shouldControlUnlockedScreenOff()) {
+ mKeyguardViewVisibilityAnimating = true;
+ mAnimatingScreenOff = true;
+
+ mView.setVisibility(View.VISIBLE);
+ mView.setAlpha(0f);
+ float currentY = mView.getY();
+ mView.setY(currentY - mView.getHeight() * 0.1f);
+ int duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+ int delay = (int) (duration * .6f);
+ // We animate the Y properly separately using the PropertyAnimator, as the panel
+ // view als needs to update the end position.
+ mAnimationProperties.setDuration(duration).setDelay(delay);
+ PropertyAnimator.cancelAnimation(mView, AnimatableProperty.Y);
+ PropertyAnimator.setProperty(mView, AnimatableProperty.Y, currentY,
+ mAnimationProperties,
+ true /* animate */);
+
+ mView.animate()
+ .setStartDelay(delay)
+ .setDuration(duration)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .alpha(1f)
+ .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
+ .start();
+
} else {
mView.setVisibility(View.VISIBLE);
mView.setAlpha(1f);
@@ -119,6 +156,8 @@
mView.setVisibility(View.GONE);
mView.setAlpha(1f);
}
+
+ mLastOccludedState = isOccluded;
}
private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> {
@@ -133,5 +172,13 @@
private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
mKeyguardViewVisibilityAnimating = false;
+ mAnimatingScreenOff = false;
};
+
+ /**
+ * @return {@code true} if we are currently animating the screen off from unlock
+ */
+ public boolean isAnimatingScreenOffFromUnlocked() {
+ return mAnimatingScreenOff;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index b5f2ab2..0ea01fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -45,11 +45,13 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Objects;
import javax.inject.Inject;
@@ -67,6 +69,7 @@
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final AuthController mAuthController;
@NonNull private final AccessibilityManager mAccessibilityManager;
+ @NonNull private final ConfigurationController mConfigurationController;
private boolean mHasUdfpsOrFaceAuthFeatures;
private boolean mUdfpsEnrolled;
@@ -75,6 +78,9 @@
@NonNull private final Drawable mButton;
@NonNull private final Drawable mUnlockIcon;
@NonNull private final Drawable mLockIcon;
+ @NonNull private final CharSequence mDisabledLabel;
+ @NonNull private final CharSequence mUnlockedLabel;
+ @NonNull private final CharSequence mLockedLabel;
private boolean mIsDozing;
private boolean mIsBouncerShowing;
@@ -99,7 +105,8 @@
@NonNull FalsingManager falsingManager,
@NonNull AuthController authController,
@NonNull DumpManager dumpManager,
- @NonNull AccessibilityManager accessibilityManager
+ @NonNull AccessibilityManager accessibilityManager,
+ @NonNull ConfigurationController configurationController
) {
super(view);
mStatusBarStateController = statusBarStateController;
@@ -109,6 +116,7 @@
mKeyguardStateController = keyguardStateController;
mFalsingManager = falsingManager;
mAccessibilityManager = accessibilityManager;
+ mConfigurationController = configurationController;
final Context context = view.getContext();
mButton = context.getResources().getDrawable(
@@ -121,6 +129,10 @@
com.android.internal.R.drawable.ic_lock, context.getTheme()),
context.getResources().getDimensionPixelSize(
com.android.systemui.R.dimen.udfps_unlock_icon_inset));
+ mDisabledLabel = context.getResources().getString(
+ R.string.accessibility_udfps_disabled_button);
+ mUnlockedLabel = context.getResources().getString(R.string.accessibility_unlock_button);
+ mLockedLabel = context.getResources().getString(R.string.accessibility_lock_icon);
dumpManager.registerDumpable("LockIconViewController", this);
}
@@ -137,7 +149,13 @@
final boolean hasUdfps = mAuthController.getUdfpsSensorLocation() != null;
mHasUdfpsOrFaceAuthFeatures = hasFaceAuth || hasUdfps;
if (!mHasUdfpsOrFaceAuthFeatures) {
- ((ViewGroup) mView.getParent()).removeView(mView);
+ // Posting since removing a view in the middle of onAttach can lead to a crash in the
+ // iteration loop when the view isn't last
+ mView.setVisibility(View.GONE);
+ mView.post(() -> {
+ mView.setVisibility(View.VISIBLE);
+ ((ViewGroup) mView.getParent()).removeView(mView);
+ });
return;
}
@@ -164,10 +182,8 @@
mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
mStatusBarState = mStatusBarStateController.getState();
- mUnlockIcon.setTint(Utils.getColorAttrDefaultColor(mView.getContext(),
- R.attr.wallpaperTextColorAccent));
- mLockIcon.setTint(Utils.getColorAttrDefaultColor(mView.getContext(),
- R.attr.wallpaperTextColorAccent));
+ updateColors();
+ mConfigurationController.addCallback(mConfigurationListener);
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mStatusBarStateController.addCallback(mStatusBarStateListener);
@@ -180,6 +196,7 @@
@Override
protected void onViewDetached() {
+ mConfigurationController.removeCallback(mConfigurationListener);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mKeyguardStateController.removeCallback(mKeyguardStateCallback);
@@ -225,25 +242,27 @@
&& mFaceAuthEnrolled;
updateClickListener();
+ final CharSequence prevContentDescription = mView.getContentDescription();
if (mShowButton) {
mView.setImageDrawable(mButton);
mView.setVisibility(View.VISIBLE);
- mView.setContentDescription(getResources().getString(
- R.string.accessibility_udfps_disabled_button));
+ mView.setContentDescription(mDisabledLabel);
} else if (mShowUnlockIcon) {
mView.setImageDrawable(mUnlockIcon);
mView.setVisibility(View.VISIBLE);
- mView.setContentDescription(getResources().getString(
- R.string.accessibility_unlock_button));
+ mView.setContentDescription(mUnlockedLabel);
} else if (mShowLockIcon) {
mView.setImageDrawable(mLockIcon);
mView.setVisibility(View.VISIBLE);
- mView.setContentDescription(getResources().getString(
- R.string.accessibility_lock_icon));
+ mView.setContentDescription(mLockedLabel);
} else {
mView.setVisibility(View.INVISIBLE);
mView.setContentDescription(null);
}
+ if (!Objects.equals(prevContentDescription, mView.getContentDescription())
+ && mView.getContentDescription() != null) {
+ mView.announceForAccessibility(mView.getContentDescription());
+ }
}
private final View.AccessibilityDelegate mAccessibilityDelegate =
@@ -258,20 +277,12 @@
getResources().getString(R.string.accessibility_enter_hint));
public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(v, info);
- removeAllActions(info);
if (mShowButton || mShowLockIcon) {
info.addAction(mAccessibilityAuthenticateHint);
} else if (mShowUnlockIcon) {
info.addAction(mAccessibilityEnterHint);
}
}
-
- private void removeAllActions(AccessibilityNodeInfo info) {
- info.removeAction(mAccessibilityAuthenticateHint);
- info.removeAction(mAccessibilityEnterHint);
- info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
- mView.setLongClickable(false);
- }
};
private boolean isLockScreen() {
@@ -286,6 +297,7 @@
mView.setOnClickListener(v -> onAffordanceClick());
if (mAccessibilityManager.isTouchExplorationEnabled()) {
mView.setOnLongClickListener(null);
+ mView.setLongClickable(false);
} else {
mView.setOnLongClickListener(v -> onAffordanceClick());
}
@@ -297,6 +309,13 @@
&& !mKeyguardStateController.isKeyguardGoingAway();
}
+ private void updateColors() {
+ final int color = Utils.getColorAttrDefaultColor(mView.getContext(),
+ R.attr.wallpaperTextColorAccent);
+ mUnlockIcon.setTint(color);
+ mLockIcon.setTint(color);
+ }
+
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println(" mShowBouncerButton: " + mShowButton);
@@ -376,6 +395,24 @@
}
};
+ private final ConfigurationController.ConfigurationListener mConfigurationListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onUiModeChanged() {
+ updateColors();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ updateColors();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ updateColors();
+ }
+ };
+
private final AccessibilityManager.TouchExplorationStateChangeListener
mTouchExplorationStateChangeListener = enabled -> updateClickListener();
}
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 76cec0b..64a683e 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -305,10 +305,15 @@
continue;
}
Rect subImage = new Rect(
- Math.round(area.left * b.getWidth()),
- Math.round(area.top * b.getHeight()),
- Math.round(area.right * b.getWidth()),
- Math.round(area.bottom * b.getHeight()));
+ (int) Math.floor(area.left * b.getWidth()),
+ (int) Math.floor(area.top * b.getHeight()),
+ (int) Math.ceil(area.right * b.getWidth()),
+ (int) Math.ceil(area.bottom * b.getHeight()));
+ if (subImage.isEmpty()) {
+ // Do not notify client. treat it as too small to sample
+ colors.add(null);
+ continue;
+ }
Bitmap colorImg = Bitmap.createBitmap(b,
subImage.left, subImage.top, subImage.width(), subImage.height());
WallpaperColors color = WallpaperColors.fromBitmap(colorImg);
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 7f18379..bf09975 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -73,7 +73,8 @@
Key.TOUCHED_RINGER_TOGGLE,
Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP,
Key.HAS_SEEN_REVERSE_BOTTOM_SHEET,
- Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
+ Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT,
+ Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP
})
// TODO: annotate these with their types so {@link PrefsCommandLine} can know how to set them
public @interface Key {
@@ -122,6 +123,8 @@
String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
String HAS_SEEN_REVERSE_BOTTOM_SHEET = "HasSeenReverseBottomSheet";
String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
+ String HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP =
+ "HasSeenAccessibilityFloatingMenuDockTooltip";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index be50eb1..afda2a4 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -55,8 +55,6 @@
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.HandlerThread;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Secure;
@@ -91,11 +89,13 @@
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -122,7 +122,7 @@
@VisibleForTesting
protected boolean mIsRegistered;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final Handler mMainHandler;
+ private final Executor mMainExecutor;
private final TunerService mTunerService;
private final SecureSettings mSecureSettings;
private DisplayManager.DisplayListener mDisplayListener;
@@ -153,6 +153,7 @@
private WindowManager mWindowManager;
private int mRotation;
private SecureSetting mColorInversionSetting;
+ private DelayableExecutor mExecutor;
private Handler mHandler;
private boolean mPendingRotationChange;
private boolean mIsRoundedCornerMultipleRadius;
@@ -212,7 +213,7 @@
@Inject
public ScreenDecorations(Context context,
- @Main Handler handler,
+ @Main Executor mainExecutor,
SecureSettings secureSettings,
BroadcastDispatcher broadcastDispatcher,
TunerService tunerService,
@@ -220,7 +221,7 @@
PrivacyDotViewController dotViewController,
ThreadFactory threadFactory) {
super(context);
- mMainHandler = handler;
+ mMainExecutor = mainExecutor;
mSecureSettings = secureSettings;
mBroadcastDispatcher = broadcastDispatcher;
mTunerService = tunerService;
@@ -235,17 +236,10 @@
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
- mHandler = startHandlerThread();
- mHandler.post(this::startOnScreenDecorationsThread);
- mDotViewController.setUiExecutor(
- mThreadFactory.buildDelayableExecutorOnLooper(mHandler.getLooper()));
- }
-
- @VisibleForTesting
- Handler startHandlerThread() {
- HandlerThread thread = new HandlerThread("ScreenDecorations");
- thread.start();
- return thread.getThreadHandler();
+ mHandler = mThreadFactory.builderHandlerOnNewThread("ScreenDecorations");
+ mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler);
+ mExecutor.execute(this::startOnScreenDecorationsThread);
+ mDotViewController.setUiExecutor(mExecutor);
}
private void startOnScreenDecorationsThread() {
@@ -332,7 +326,7 @@
mDisplayManager.getDisplay(DEFAULT_DISPLAY).getMetrics(metrics);
mDensity = metrics.density;
- mMainHandler.post(() -> mTunerService.addTunable(this, SIZE));
+ mExecutor.execute(() -> mTunerService.addTunable(this, SIZE));
// Watch color inversion and invert the overlay as needed.
if (mColorInversionSetting == null) {
@@ -351,10 +345,10 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
mBroadcastDispatcher.registerReceiver(mUserSwitchIntentReceiver, filter,
- new HandlerExecutor(mHandler), UserHandle.ALL);
+ mExecutor, UserHandle.ALL);
mIsRegistered = true;
} else {
- mMainHandler.post(() -> mTunerService.removeTunable(this));
+ mMainExecutor.execute(() -> mTunerService.removeTunable(this));
if (mColorInversionSetting != null) {
mColorInversionSetting.setListening(false);
@@ -567,7 +561,7 @@
Resources res = mContext.getResources();
boolean enabled = res.getBoolean(R.bool.config_enableDisplayCutoutProtection);
if (enabled) {
- mCameraListener = CameraAvailabilityListener.Factory.build(mContext, mHandler::post);
+ mCameraListener = CameraAvailabilityListener.Factory.build(mContext, mExecutor);
mCameraListener.addTransitionCallback(mCameraTransitionCallback);
mCameraListener.startListening();
}
@@ -624,7 +618,7 @@
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
int oldRotation = mRotation;
mPendingRotationChange = false;
updateOrientation();
@@ -825,7 +819,7 @@
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (mOverlays == null) return;
if (SIZE.equals(key)) {
Point size = mRoundedDefault;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
index b69001d..c472457 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
@@ -63,17 +63,15 @@
}
/**
- * Gets the object by the element index.
+ * Returns the object with the given display id.
*
- * <p> If the index is bigger than the array size, an {@link ArrayIndexOutOfBoundsException} is
- * thrown for apps targeting {@link android.os.Build.VERSION_CODES#Q} and later </p>
*
- * @param index the element index
+ * @param displayId the logical display Id
* @return T
- * @see SparseArray#valueAt(int)
*/
- public T valueAt(int index) {
- return mSparseArray.valueAt(index);
+ @Nullable
+ public T valueAt(int displayId) {
+ return mSparseArray.get(displayId);
}
@NonNull
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
index 4c892e29..4b30ec3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
@@ -113,7 +113,7 @@
final float rawX = event.getRawX();
final float rawY = event.getRawY();
boolean handled = false;
- switch (event.getAction()) {
+ switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mPointerDown.set(rawX, rawY);
mHandler.postAtTime(mCancelTapGestureRunnable,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 4f5fdc9..cee395b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -18,10 +18,11 @@
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -37,9 +38,13 @@
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
import javax.inject.Inject;
/**
@@ -52,34 +57,33 @@
public class WindowMagnification extends SystemUI implements WindowMagnifierCallback,
CommandQueue.Callbacks {
private static final String TAG = "WindowMagnification";
- private static final int CONFIG_MASK =
- ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION
- | ActivityInfo.CONFIG_LOCALE;
private final ModeSwitchesController mModeSwitchesController;
private final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
private final CommandQueue mCommandQueue;
+ private final OverviewProxyService mOverviewProxyService;
private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
private Configuration mLastConfiguration;
+ private SysUiState mSysUiState;
private static class AnimationControllerSupplier extends
DisplayIdIndexSupplier<WindowMagnificationAnimationController> {
private final Context mContext;
private final Handler mHandler;
- private final NavigationModeController mNavigationModeController;
private final WindowMagnifierCallback mWindowMagnifierCallback;
+ private final SysUiState mSysUiState;
AnimationControllerSupplier(Context context, Handler handler,
- NavigationModeController navigationModeController,
- WindowMagnifierCallback windowMagnifierCallback, DisplayManager displayManager) {
+ WindowMagnifierCallback windowMagnifierCallback,
+ DisplayManager displayManager, SysUiState sysUiState) {
super(displayManager);
mContext = context;
mHandler = handler;
- mNavigationModeController = navigationModeController;
mWindowMagnifierCallback = windowMagnifierCallback;
+ mSysUiState = sysUiState;
}
@Override
@@ -89,10 +93,7 @@
final WindowMagnificationController controller = new WindowMagnificationController(
mContext,
mHandler, new SfVsyncFrameCallbackProvider(), null,
- new SurfaceControl.Transaction(), mWindowMagnifierCallback);
- final int navBarMode = mNavigationModeController.addListener(
- controller::onNavigationModeChanged);
- controller.onNavigationModeChanged(navBarMode);
+ new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState);
return new WindowMagnificationAnimationController(windowContext, controller);
}
}
@@ -103,24 +104,22 @@
@Inject
public WindowMagnification(Context context, @Main Handler mainHandler,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
- NavigationModeController navigationModeController) {
+ SysUiState sysUiState, OverviewProxyService overviewProxyService) {
super(context);
mHandler = mainHandler;
mLastConfiguration = new Configuration(context.getResources().getConfiguration());
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mCommandQueue = commandQueue;
mModeSwitchesController = modeSwitchesController;
+ mSysUiState = sysUiState;
+ mOverviewProxyService = overviewProxyService;
mAnimationControllerSupplier = new AnimationControllerSupplier(context,
- mHandler, navigationModeController, this,
- context.getSystemService(DisplayManager.class));
+ mHandler, this, context.getSystemService(DisplayManager.class), sysUiState);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
final int configDiff = newConfig.diff(mLastConfiguration);
- if ((configDiff & CONFIG_MASK) == 0) {
- return;
- }
mLastConfiguration.setTo(newConfig);
mAnimationControllerSupplier.forEach(
animationController -> animationController.onConfigurationChanged(configDiff));
@@ -132,6 +131,28 @@
@Override
public void start() {
mCommandQueue.addCallback(this);
+ mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ if (isConnected) {
+ updateSysUiStateFlag();
+ }
+ }
+ });
+ }
+
+ private void updateSysUiStateFlag() {
+ //TODO(b/187510533): support multi-display once SysuiState supports it.
+ final WindowMagnificationAnimationController controller =
+ mAnimationControllerSupplier.valueAt(Display.DEFAULT_DISPLAY);
+ if (controller != null) {
+ controller.updateSysUiStateFlag();
+ } else {
+ // The instance is initialized when there is an IPC request. Considering
+ // self-crash cases, we need to reset the flag in such situation.
+ mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
+ .commitUpdate(Display.DEFAULT_DISPLAY);
+ }
}
@MainThread
@@ -210,6 +231,13 @@
}
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(TAG);
+ mAnimationControllerSupplier.forEach(
+ animationController -> animationController.dump(pw));
+ }
+
private void setWindowMagnificationConnection() {
if (mWindowMagnificationConnectionImpl == null) {
mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 5758b15..36fef3e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -31,6 +31,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -269,6 +270,14 @@
mController.enableWindowMagnification(sentScale, centerX, centerY);
}
+ public void updateSysUiStateFlag() {
+ mController.updateSysUIStateFlag();
+ }
+
+ void dump(PrintWriter pw) {
+ mController.dump(pw);
+ }
+
private static ValueAnimator newValueAnimator(Resources resources) {
final ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index ae16703..fcb090a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -16,9 +16,10 @@
package com.android.systemui.accessibility;
+import static android.view.WindowInsets.Type.systemGestures;
import static android.view.WindowManager.LayoutParams;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
@@ -28,6 +29,7 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -52,14 +54,17 @@
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.Locale;
@@ -111,6 +116,7 @@
private final View.OnLayoutChangeListener mMirrorSurfaceViewLayoutChangeListener;
private final Runnable mMirrorViewRunnable;
private final Runnable mUpdateStateDescriptionRunnable;
+ private final Runnable mWindowInsetChangeRunnable;
private View mMirrorView;
private SurfaceView mMirrorSurfaceView;
private int mMirrorSurfaceMargin;
@@ -119,9 +125,8 @@
private int mOuterBorderSize;
// The boundary of magnification frame.
private final Rect mMagnificationFrameBoundary = new Rect();
-
- private int mNavBarMode;
- private int mNavGestureHeight;
+ // The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid.
+ private int mSystemGestureTop = -1;
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
private final MagnificationGestureDetector mGestureDetector;
@@ -130,6 +135,9 @@
private Locale mLocale;
private NumberFormat mPercentFormat;
private float mBounceEffectAnimationScale;
+ private SysUiState mSysUiState;
+ // Set it to true when the view is overlapped with the gesture insets at the bottom.
+ private boolean mOverlapWithGestureInsets;
@Nullable
private MirrorWindowControl mMirrorWindowControl;
@@ -137,11 +145,12 @@
WindowMagnificationController(@UiContext Context context, @NonNull Handler handler,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
- @NonNull WindowMagnifierCallback callback) {
+ @NonNull WindowMagnifierCallback callback, SysUiState sysUiState) {
mContext = context;
mHandler = handler;
mSfVsyncFrameProvider = sfVsyncFrameProvider;
mWindowMagnifierCallback = callback;
+ mSysUiState = sysUiState;
final Display display = mContext.getDisplay();
mDisplayId = mContext.getDisplayId();
@@ -170,13 +179,18 @@
mMirrorViewRunnable = () -> {
if (mMirrorView != null) {
mMirrorView.getBoundsOnScreen(mMirrorViewBounds);
+ updateSystemUIStateIfNeeded();
mWindowMagnifierCallback.onWindowMagnifierBoundsChanged(
mDisplayId, mMirrorViewBounds);
}
};
mMirrorViewLayoutChangeListener =
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ if (!mHandler.hasCallbacks(mMirrorViewRunnable)) {
mHandler.post(mMirrorViewRunnable);
+ }
+ };
+
mMirrorSurfaceViewLayoutChangeListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
-> applyTapExcludeRegion();
@@ -199,6 +213,7 @@
mMirrorView.setStateDescription(formatStateDescription(mScale));
}
};
+ mWindowInsetChangeRunnable = this::onWindowInsetChanged;
}
private void updateDimensions() {
@@ -210,7 +225,6 @@
R.dimen.magnification_drag_view_size);
mOuterBorderSize = mResources.getDimensionPixelSize(
R.dimen.magnification_outer_border_margin);
- updateNavigationBarDimensions();
}
private void computeBounceAnimationScale() {
@@ -220,16 +234,16 @@
mBounceEffectAnimationScale = Math.min(animationScaleMax, ANIMATION_BOUNCE_EFFECT_SCALE);
}
- private void updateNavigationBarDimensions() {
- if (!supportsSwipeUpGesture()) {
- mNavGestureHeight = 0;
- return;
+ private boolean updateSystemGestureInsetsTop() {
+ final WindowMetrics windowMetrics = mWm.getCurrentWindowMetrics();
+ final Insets insets = windowMetrics.getWindowInsets().getInsets(systemGestures());
+ final int gestureTop =
+ insets.bottom != 0 ? windowMetrics.getBounds().bottom - insets.bottom : -1;
+ if (gestureTop != mSystemGestureTop) {
+ mSystemGestureTop = gestureTop;
+ return true;
}
- mNavGestureHeight = (mWindowBounds.width() > mWindowBounds.height())
- ? mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height_landscape)
- : mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_gesture_height);
+ return false;
}
/**
@@ -255,6 +269,7 @@
if (mMirrorWindowControl != null) {
mMirrorWindowControl.destroyControl();
}
+ updateSystemUIStateIfNeeded();
}
/**
@@ -277,6 +292,10 @@
}
}
+ private void updateSystemUIStateIfNeeded() {
+ updateSysUIState(false);
+ }
+
private void updateAccessibilityWindowTitleIfNeeded() {
if (!isWindowVisible()) return;
LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams();
@@ -284,13 +303,6 @@
mWm.updateViewLayout(mMirrorView, params);
}
- /** Handles MirrorWindow position when the navigation bar mode changed. */
- public void onNavigationModeChanged(int mode) {
- mNavBarMode = mode;
- updateNavigationBarDimensions();
- updateMirrorViewLayout();
- }
-
/** Handles MirrorWindow position when the device rotation changed. */
private void onRotate() {
final Display display = mContext.getDisplay();
@@ -299,7 +311,6 @@
setMagnificationFrameBoundary();
mRotation = display.getRotation();
- updateNavigationBarDimensions();
if (!isWindowVisible()) {
return;
@@ -351,6 +362,7 @@
params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
params.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ params.receiveInsetsIgnoringZOrder = true;
params.setTitle(mContext.getString(R.string.magnification_window_title));
params.accessibilityTitle = getAccessibilityWindowTitle();
@@ -368,6 +380,12 @@
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
mMirrorView.addOnLayoutChangeListener(mMirrorViewLayoutChangeListener);
mMirrorView.setAccessibilityDelegate(new MirrorWindowA11yDelegate());
+ mMirrorView.setOnApplyWindowInsetsListener((v, insets) -> {
+ if (!mHandler.hasCallbacks(mWindowInsetChangeRunnable)) {
+ mHandler.post(mWindowInsetChangeRunnable);
+ }
+ return v.onApplyWindowInsets(insets);
+ });
mWm.addView(mMirrorView, params);
@@ -377,6 +395,12 @@
addDragTouchListeners();
}
+ private void onWindowInsetChanged() {
+ if (updateSystemGestureInsetsTop()) {
+ updateSystemUIStateIfNeeded();
+ }
+ }
+
private void applyTapExcludeRegion() {
final Region tapExcludeRegion = calculateTapExclude();
final IWindow window = IWindow.Stub.asInterface(mMirrorView.getWindowToken());
@@ -464,17 +488,12 @@
return;
}
final int maxMirrorViewX = mWindowBounds.width() - mMirrorView.getWidth();
- final int maxMirrorViewY =
- mWindowBounds.height() - mMirrorView.getHeight() - mNavGestureHeight;
+ final int maxMirrorViewY = mWindowBounds.height() - mMirrorView.getHeight();
+
LayoutParams params =
(LayoutParams) mMirrorView.getLayoutParams();
params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
- // If nav bar mode supports swipe-up gesture, the Y position of mirror view should not
- // overlap nav bar window to prevent window-dragging obscured.
- if (supportsSwipeUpGesture()) {
- params.y = Math.min(params.y, maxMirrorViewY);
- }
// Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView
// able to move close to the screen edges.
@@ -508,6 +527,10 @@
return false;
}
+ public void updateSysUIStateFlag() {
+ updateSysUIState(true);
+ }
+
/**
* Calculates the desired source bounds. This will be the area under from the center of the
* displayFrame, factoring in scale.
@@ -569,6 +592,16 @@
return false;
}
+ private void updateSysUIState(boolean force) {
+ final boolean overlap = isWindowVisible() && mSystemGestureTop > 0
+ && mMirrorViewBounds.bottom > mSystemGestureTop;
+ if (force || overlap != mOverlapWithGestureInsets) {
+ mOverlapWithGestureInsets = overlap;
+ mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, mOverlapWithGestureInsets)
+ .commitUpdate(mDisplayId);
+ }
+ }
+
@Override
public void surfaceCreated(SurfaceHolder holder) {
createMirror();
@@ -676,10 +709,6 @@
return mMirrorView != null;
}
- private boolean supportsSwipeUpGesture() {
- return mNavBarMode == NAV_BAR_MODE_2BUTTON || mNavBarMode == NAV_BAR_MODE_GESTURAL;
- }
-
private CharSequence formatStateDescription(float scale) {
// Cache the locale-appropriate NumberFormat. Configuration locale is guaranteed
// non-null, so the first time this is called we will always get the appropriate
@@ -722,6 +751,14 @@
scaleAnimator.start();
}
+ public void dump(PrintWriter pw) {
+ pw.println("WindowMagnificationController (displayId=" + mDisplayId + "):");
+ pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
+ pw.println(" mScale:" + mScale);
+ pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
+ pw.println(" mSystemGestureTop:" + mSystemGestureTop);
+ }
+
private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index 3b3bad3..ee62768 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -18,11 +18,13 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
+import static com.android.systemui.Prefs.Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP;
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.ShapeType;
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.SizeType;
@@ -34,15 +36,19 @@
import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Prefs;
/**
* Contains logic for an accessibility floating menu view.
*/
public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
- private static final int DEFAULT_FADE_EFFECT_ENABLED = 1;
+ private static final int DEFAULT_FADE_EFFECT_IS_ENABLED = 1;
+ private static final int DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED = 0;
private static final float DEFAULT_OPACITY_VALUE = 0.55f;
private final Context mContext;
private final AccessibilityFloatingMenuView mMenuView;
+ private final MigrationTooltipView mMigrationTooltipView;
+ private final DockTooltipView mDockTooltipView;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ContentObserver mContentObserver =
@@ -86,6 +92,8 @@
AccessibilityFloatingMenu(Context context, AccessibilityFloatingMenuView menuView) {
mContext = context;
mMenuView = menuView;
+ mMigrationTooltipView = new MigrationTooltipView(mContext, mMenuView);
+ mDockTooltipView = new DockTooltipView(mContext, mMenuView);
}
@Override
@@ -105,6 +113,9 @@
getOpacityValue(mContext));
mMenuView.setSizeType(getSizeType(mContext));
mMenuView.setShapeType(getShapeType(mContext));
+ mMenuView.setOnDragEndListener(this::showDockTooltipIfNecessary);
+
+ showMigrationTooltipIfNecessary();
registerContentObservers();
}
@@ -116,14 +127,48 @@
}
mMenuView.hide();
+ mMigrationTooltipView.hide();
+ mDockTooltipView.hide();
unregisterContentObservers();
}
+ // Migration tooltip was the android S feature. It's just used on the Android version from R
+ // to S. In addition, it only shows once.
+ private void showMigrationTooltipIfNecessary() {
+ if (isMigrationTooltipPromptEnabled(mContext)) {
+ mMigrationTooltipView.show();
+
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT, /* disabled */ 0);
+ }
+ }
+
+ private static boolean isMigrationTooltipPromptEnabled(Context context) {
+ return Settings.Secure.getInt(
+ context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
+ DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED) == /* enabled */ 1;
+ }
+
+ /**
+ * Shows tooltip when user drags accessibility floating menu for the first time.
+ */
+ private void showDockTooltipIfNecessary() {
+ if (!Prefs.get(mContext).getBoolean(
+ HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, false)) {
+ // if the menu is an oval, the user has already dragged it out, so show the tooltip.
+ if (mMenuView.isOvalShape()) {
+ mDockTooltipView.show();
+ }
+
+ Prefs.putBoolean(mContext, HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, true);
+ }
+ }
+
private static boolean isFadeEffectEnabled(Context context) {
return Settings.Secure.getInt(
context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
- DEFAULT_FADE_EFFECT_ENABLED) == /* enable */ 1;
+ DEFAULT_FADE_EFFECT_IS_ENABLED) == /* enabled */ 1;
}
private static float getOpacityValue(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 934e20d..e85bd88 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -19,6 +19,8 @@
import static android.util.MathUtils.constrain;
import static android.util.MathUtils.sq;
+import static java.util.Objects.requireNonNull;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -43,7 +45,9 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.animation.Animation;
import android.view.animation.OvershootInterpolator;
+import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import androidx.annotation.DimenRes;
@@ -60,6 +64,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
/**
* Accessibility floating menu is used for the actions of accessibility features, it's also the
@@ -74,10 +79,13 @@
private static final int FADE_OUT_DURATION_MS = 1000;
private static final int FADE_EFFECT_DURATION_MS = 3000;
private static final int SNAP_TO_LOCATION_DURATION_MS = 150;
- private static final int MIN_WINDOW_X = 0;
private static final int MIN_WINDOW_Y = 0;
private static final float LOCATION_Y_PERCENTAGE = 0.8f;
+ private static final int ANIMATION_START_OFFSET = 600;
+ private static final int ANIMATION_DURATION_MS = 600;
+ private static final float ANIMATION_TO_X_VALUE = 0.5f;
+
private boolean mIsFadeEffectEnabled;
private boolean mIsShowing;
private boolean mIsDownInEnlargedTouchArea;
@@ -107,6 +115,7 @@
private float mPercentageY = LOCATION_Y_PERCENTAGE;
private float mSquareScaledTouchSlop;
private final Configuration mLastConfiguration;
+ private Optional<OnDragEndListener> mOnDragEndListener = Optional.empty();
private final RecyclerView mListView;
private final AccessibilityTargetAdapter mAdapter;
private float mFadeOutValue;
@@ -161,6 +170,17 @@
int RIGHT = 1;
}
+ /**
+ * Interface for a callback to be invoked when the floating menu was dragging.
+ */
+ interface OnDragEndListener {
+
+ /**
+ * Invoked when the floating menu has dragged end.
+ */
+ void onDragEnd();
+ }
+
public AccessibilityFloatingMenuView(Context context) {
this(context, new RecyclerView(context));
}
@@ -191,7 +211,6 @@
mPercentageY = calculateCurrentPercentageY();
updateLocationWith(mAlignment, mPercentageY);
- updateMarginsWith(mAlignment);
updateInsetWith(getResources().getConfiguration().uiMode, mAlignment);
@@ -201,6 +220,8 @@
updateRadiusWith(mSizeType, mRadiusType, mTargets.size());
fadeOut();
+
+ mOnDragEndListener.ifPresent(OnDragEndListener::onDragEnd);
}
});
@@ -242,7 +263,8 @@
: ShapeType.OVAL;
final int newWindowX = currentRawX + mRelativeToPointerDownX;
final int newWindowY = currentRawY + mRelativeToPointerDownY;
- mCurrentLayoutParams.x = constrain(newWindowX, MIN_WINDOW_X, getMaxWindowX());
+ mCurrentLayoutParams.x =
+ constrain(newWindowX, getMinWindowX(), getMaxWindowX());
mCurrentLayoutParams.y = constrain(newWindowY, MIN_WINDOW_Y, getMaxWindowY());
mWindowManager.updateViewLayout(this, mCurrentLayoutParams);
}
@@ -252,9 +274,10 @@
if (mIsDragging) {
mIsDragging = false;
+ final int minX = getMinWindowX();
final int maxX = getMaxWindowX();
- final int endX = mCurrentLayoutParams.x > ((MIN_WINDOW_X + maxX) / 2)
- ? maxX : MIN_WINDOW_X;
+ final int endX = mCurrentLayoutParams.x > ((minX + maxX) / 2)
+ ? maxX : minX;
final int endY = mCurrentLayoutParams.y;
snapToLocation(endX, endY);
@@ -266,7 +289,7 @@
// Must switch the oval shape type before tapping the corresponding item in the
// list view, otherwise it can't work on it.
- if (mShapeType == ShapeType.HALF_OVAL) {
+ if (!isOvalShape()) {
setShapeType(ShapeType.OVAL);
return true;
@@ -299,10 +322,6 @@
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
- if (super.performAccessibilityAction(action, arguments)) {
- return true;
- }
-
fadeIn();
final Rect bounds = getAvailableBounds();
@@ -340,7 +359,7 @@
return true;
}
- return false;
+ return super.performAccessibilityAction(action, arguments);
}
void show() {
@@ -367,6 +386,10 @@
return mIsShowing;
}
+ boolean isOvalShape() {
+ return mShapeType == ShapeType.OVAL;
+ }
+
void onTargetsChanged(List<AccessibilityTarget> newTargets) {
fadeIn();
@@ -411,6 +434,41 @@
fadeOut();
}
+ public void setOnDragEndListener(OnDragEndListener onDragListener) {
+ mOnDragEndListener = Optional.ofNullable(onDragListener);
+ }
+
+ void startTranslateXAnimation() {
+ fadeIn();
+
+ final float toXValue = mAlignment == Alignment.RIGHT
+ ? ANIMATION_TO_X_VALUE
+ : -ANIMATION_TO_X_VALUE;
+ final TranslateAnimation animation =
+ new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
+ Animation.RELATIVE_TO_SELF, toXValue,
+ Animation.RELATIVE_TO_SELF, 0,
+ Animation.RELATIVE_TO_SELF, 0);
+ animation.setDuration(ANIMATION_DURATION_MS);
+ animation.setRepeatMode(Animation.REVERSE);
+ animation.setInterpolator(new OvershootInterpolator());
+ animation.setRepeatCount(Animation.INFINITE);
+ animation.setStartOffset(ANIMATION_START_OFFSET);
+ mListView.startAnimation(animation);
+ }
+
+ void stopTranslateXAnimation() {
+ mListView.clearAnimation();
+
+ fadeOut();
+ }
+
+ Rect getWindowLocationOnScreen() {
+ final int left = mCurrentLayoutParams.x;
+ final int top = mCurrentLayoutParams.y;
+ return new Rect(left, top, left + getWindowWidth(), top + getWindowHeight());
+ }
+
void updateOpacityWith(boolean isFadeEffectEnabled, float newOpacityValue) {
mIsFadeEffectEnabled = isFadeEffectEnabled;
mFadeOutValue = newOpacityValue;
@@ -486,9 +544,8 @@
final int currentX = (int) event.getX();
final int currentY = (int) event.getY();
- final int menuHalfWidth = getLayoutWidth() / 2;
final Rect touchDelegateBounds =
- new Rect(mMargin, mMargin, mMargin + menuHalfWidth, mMargin + getLayoutHeight());
+ new Rect(mMargin, mMargin, mMargin + getLayoutWidth(), mMargin + getLayoutHeight());
if (action == MotionEvent.ACTION_DOWN
&& touchDelegateBounds.contains(currentX, currentY)) {
mIsDownInEnlargedTouchArea = true;
@@ -534,11 +591,7 @@
}
private Handler createUiHandler() {
- final Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalArgumentException("looper must not be null");
- }
- return new Handler(looper);
+ return new Handler(requireNonNull(Looper.myLooper(), "looper must not be null"));
}
private void updateDimensions() {
@@ -577,6 +630,7 @@
final LayoutParams layoutParams =
new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
+ layoutParams.setMargins(mMargin, mMargin, mMargin, mMargin);
mListView.setLayoutParams(layoutParams);
final InstantInsetLayerDrawable layerDrawable =
new InstantInsetLayerDrawable(new Drawable[]{background});
@@ -594,8 +648,6 @@
final int elevation =
getResources().getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
mListView.setElevation(elevation);
-
- updateMarginsWith(mAlignment);
}
private WindowManager.LayoutParams createDefaultLayoutParams() {
@@ -603,7 +655,8 @@
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
params.windowAnimations = android.R.style.Animation_Translucent;
params.gravity = Gravity.START | Gravity.TOP;
@@ -650,6 +703,10 @@
mWindowManager.updateViewLayout(this, mCurrentLayoutParams);
}
+ private int getMinWindowX() {
+ return -mMargin;
+ }
+
private int getMaxWindowX() {
return mScreenWidth - mMargin - getLayoutWidth();
}
@@ -670,7 +727,7 @@
* Updates the floating menu to be fixed at the side of the screen.
*/
private void updateLocationWith(@Alignment int side, float percentageCurrentY) {
- mCurrentLayoutParams.x = (side == Alignment.RIGHT) ? getMaxWindowX() : MIN_WINDOW_X;
+ mCurrentLayoutParams.x = (side == Alignment.RIGHT) ? getMaxWindowX() : getMinWindowX();
mCurrentLayoutParams.y = (int) (percentageCurrentY * getMaxWindowY());
mWindowManager.updateViewLayout(this, mCurrentLayoutParams);
}
@@ -681,20 +738,6 @@
mListView.animate().translationX(side == Alignment.RIGHT ? offset : -offset);
}
- private void updateMarginsWith(@Alignment int side) {
- final LayoutParams layoutParams = (LayoutParams) mListView.getLayoutParams();
- final int marginLeft = (side == Alignment.LEFT) ? 0 : mMargin;
- final int marginRight = (side == Alignment.RIGHT) ? 0 : mMargin;
-
- if (marginLeft == layoutParams.leftMargin
- && marginRight == layoutParams.rightMargin) {
- return;
- }
-
- layoutParams.setMargins(marginLeft, mMargin, marginRight, mMargin);
- mListView.setLayoutParams(layoutParams);
- }
-
private void updateScrollModeWith(boolean hasExceededMaxLayoutHeight) {
mListView.setOverScrollMode(hasExceededMaxLayoutHeight
? OVER_SCROLL_ALWAYS
@@ -760,7 +803,7 @@
@Alignment
private int calculateCurrentAlignment() {
- return mCurrentLayoutParams.x >= ((MIN_WINDOW_X + getMaxWindowX()) / 2)
+ return mCurrentLayoutParams.x >= ((getMinWindowX() + getMaxWindowX()) / 2)
? Alignment.RIGHT
: Alignment.LEFT;
}
@@ -809,7 +852,7 @@
}
private int getWindowWidth() {
- return mMargin + getLayoutWidth();
+ return mMargin * 2 + getLayoutWidth();
}
private int getWindowHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
new file mode 100644
index 0000000..d8e80fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import android.text.Annotation;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.style.ClickableSpan;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * A span that turns the text wrapped by annotation tag into the clickable link text.
+ */
+class AnnotationLinkSpan extends ClickableSpan {
+ private final Optional<View.OnClickListener> mClickListener;
+
+ private AnnotationLinkSpan(View.OnClickListener listener) {
+ mClickListener = Optional.ofNullable(listener);
+ }
+
+ @Override
+ public void onClick(View view) {
+ mClickListener.ifPresent(listener -> listener.onClick(view));
+ }
+
+ /**
+ * Makes the text has the link with the click action. In addition, the span will match first
+ * LinkInfo and attach into the text.
+ *
+ * @param text the text wrapped by annotation tag
+ * @param linkInfos used to attach the click action into the corresponding span
+ * @return the text attached with the span
+ */
+ static CharSequence linkify(CharSequence text, LinkInfo... linkInfos) {
+ final SpannableString msg = new SpannableString(text);
+ final Annotation[] spans =
+ msg.getSpans(/* queryStart= */ 0, msg.length(), Annotation.class);
+ final SpannableStringBuilder builder = new SpannableStringBuilder(msg);
+
+ Arrays.asList(spans).forEach(annotationTag -> {
+ final String key = annotationTag.getValue();
+ final Optional<LinkInfo> linkInfo =
+ Arrays.asList(linkInfos).stream().filter(
+ info -> info.mAnnotation.isPresent()
+ && info.mAnnotation.get().equals(key)).findFirst();
+
+ linkInfo.flatMap(info -> info.mListener).ifPresent(listener -> {
+ final AnnotationLinkSpan span = new AnnotationLinkSpan(listener);
+ builder.setSpan(span,
+ msg.getSpanStart(annotationTag),
+ msg.getSpanEnd(annotationTag),
+ msg.getSpanFlags(span));
+ });
+ });
+
+ return builder;
+ }
+
+ /**
+ * Data class to store the annotation and the click action.
+ */
+ static class LinkInfo {
+ static final String DEFAULT_ANNOTATION = "link";
+ private final Optional<String> mAnnotation;
+ private final Optional<View.OnClickListener> mListener;
+
+ LinkInfo(@NonNull String annotation, View.OnClickListener listener) {
+ mAnnotation = Optional.of(annotation);
+ mListener = Optional.ofNullable(listener);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java
new file mode 100644
index 0000000..3085854
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static android.util.TypedValue.COMPLEX_UNIT_PX;
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import android.annotation.UiContext;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Bundle;
+import android.text.method.MovementMethod;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.recents.TriangleShape;
+
+/**
+ * Base tooltip view that shows the information about the operation of the
+ * Accessibility floating menu. In addition, the anchor view is only for {@link
+ * AccessibilityFloatingMenuView}, it should be more suited for displaying one-off menus to avoid
+ * the performance hit for the extra window.
+ */
+class BaseTooltipView extends FrameLayout {
+ private int mFontSize;
+ private int mTextViewMargin;
+ private int mTextViewPadding;
+ private int mTextViewCornerRadius;
+ private int mArrowMargin;
+ private int mArrowWidth;
+ private int mArrowHeight;
+ private int mArrowCornerRadius;
+ private int mScreenWidth;
+ private boolean mIsShowing;
+ private TextView mTextView;
+ private final WindowManager.LayoutParams mCurrentLayoutParams;
+ private final WindowManager mWindowManager;
+ private final AccessibilityFloatingMenuView mAnchorView;
+
+ BaseTooltipView(@UiContext Context context, AccessibilityFloatingMenuView anchorView) {
+ super(context);
+ mWindowManager = context.getSystemService(WindowManager.class);
+ mAnchorView = anchorView;
+ mCurrentLayoutParams = createDefaultLayoutParams();
+
+ initViews();
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ mAnchorView.onConfigurationChanged(newConfig);
+ updateTooltipView();
+
+ mWindowManager.updateViewLayout(this, mCurrentLayoutParams);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ hide();
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+
+ info.addAction(AccessibilityAction.ACTION_DISMISS);
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (action == AccessibilityAction.ACTION_DISMISS.getId()) {
+ hide();
+ return true;
+ }
+
+ return super.performAccessibilityAction(action, arguments);
+ }
+
+ void show() {
+ if (isShowing()) {
+ return;
+ }
+
+ mIsShowing = true;
+ updateTooltipView();
+
+ mWindowManager.addView(this, mCurrentLayoutParams);
+ }
+
+ void hide() {
+ if (!isShowing()) {
+ return;
+ }
+
+ mIsShowing = false;
+ mWindowManager.removeView(this);
+ }
+
+ void setDescription(CharSequence text) {
+ mTextView.setText(text);
+ }
+
+ void setMovementMethod(MovementMethod movement) {
+ mTextView.setMovementMethod(movement);
+ }
+
+ private boolean isShowing() {
+ return mIsShowing;
+ }
+
+ private void initViews() {
+ final View contentView =
+ LayoutInflater.from(getContext()).inflate(
+ R.layout.accessibility_floating_menu_tooltip, this, false);
+
+ mTextView = contentView.findViewById(R.id.text);
+
+ addView(contentView);
+ }
+
+ private static WindowManager.LayoutParams createDefaultLayoutParams() {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ params.windowAnimations = android.R.style.Animation_Translucent;
+ params.gravity = Gravity.START | Gravity.TOP;
+
+ return params;
+ }
+
+ private void updateDimensions() {
+ final Resources res = getResources();
+ final DisplayMetrics dm = res.getDisplayMetrics();
+ mScreenWidth = dm.widthPixels;
+ mArrowWidth =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_arrow_width);
+ mArrowHeight =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_arrow_height);
+ mArrowMargin =
+ res.getDimensionPixelSize(
+ R.dimen.accessibility_floating_tooltip_arrow_margin);
+ mArrowCornerRadius =
+ res.getDimensionPixelSize(
+ R.dimen.accessibility_floating_tooltip_arrow_corner_radius);
+ mFontSize =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_font_size);
+ mTextViewMargin =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_margin);
+ mTextViewPadding =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_padding);
+ mTextViewCornerRadius =
+ res.getDimensionPixelSize(
+ R.dimen.accessibility_floating_tooltip_text_corner_radius);
+ }
+
+ private void updateTooltipView() {
+ updateDimensions();
+ updateTextView();
+
+ final Rect anchorViewLocation = mAnchorView.getWindowLocationOnScreen();
+ updateArrowWith(anchorViewLocation);
+ updateWidthWith(anchorViewLocation);
+ updateLocationWith(anchorViewLocation);
+ }
+
+ private void updateTextView() {
+ mTextView.setTextSize(COMPLEX_UNIT_PX, mFontSize);
+ mTextView.setPadding(mTextViewPadding, mTextViewPadding, mTextViewPadding,
+ mTextViewPadding);
+
+ final GradientDrawable gradientDrawable = (GradientDrawable) mTextView.getBackground();
+ gradientDrawable.setCornerRadius(mTextViewCornerRadius);
+ }
+
+ private void updateArrowWith(Rect anchorViewLocation) {
+ final boolean isAnchorViewOnLeft = isAnchorViewOnLeft(anchorViewLocation);
+ final View arrowView = findViewById(isAnchorViewOnLeft
+ ? R.id.arrow_left
+ : R.id.arrow_right);
+ arrowView.setVisibility(VISIBLE);
+ drawArrow(arrowView, isAnchorViewOnLeft);
+
+ final LinearLayout.LayoutParams layoutParams =
+ (LinearLayout.LayoutParams) arrowView.getLayoutParams();
+ layoutParams.width = mArrowWidth;
+ layoutParams.height = mArrowHeight;
+
+ final int leftMargin = isAnchorViewOnLeft ? 0 : mArrowMargin;
+ final int rightMargin = isAnchorViewOnLeft ? mArrowMargin : 0;
+ layoutParams.setMargins(leftMargin, 0, rightMargin, 0);
+ arrowView.setLayoutParams(layoutParams);
+ }
+
+ private void updateWidthWith(Rect anchorViewLocation) {
+ final ViewGroup.LayoutParams layoutParams = mTextView.getLayoutParams();
+ layoutParams.width = getTextWidthWith(anchorViewLocation);
+ mTextView.setLayoutParams(layoutParams);
+ }
+
+ private void updateLocationWith(Rect anchorViewLocation) {
+ mCurrentLayoutParams.x = isAnchorViewOnLeft(anchorViewLocation)
+ ? anchorViewLocation.width()
+ : mScreenWidth - getWindowWidthWith(anchorViewLocation)
+ - anchorViewLocation.width();
+ mCurrentLayoutParams.y =
+ anchorViewLocation.centerY() - (getTextHeightWith(anchorViewLocation) / 2);
+ }
+
+ private void drawArrow(View view, boolean isPointingLeft) {
+ final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ final TriangleShape triangleShape =
+ TriangleShape.createHorizontal(layoutParams.width, layoutParams.height,
+ isPointingLeft);
+ final ShapeDrawable arrowDrawable = new ShapeDrawable(triangleShape);
+ final Paint arrowPaint = arrowDrawable.getPaint();
+ arrowPaint.setColor(Utils.getColorAttrDefaultColor(getContext(),
+ com.android.internal.R.attr.colorAccentPrimary));
+ final CornerPathEffect effect = new CornerPathEffect(mArrowCornerRadius);
+ arrowPaint.setPathEffect(effect);
+ view.setBackground(arrowDrawable);
+ }
+
+ private boolean isAnchorViewOnLeft(Rect anchorViewLocation) {
+ return anchorViewLocation.left < (mScreenWidth / 2);
+ }
+
+ private int getTextWidthWith(Rect anchorViewLocation) {
+ final int widthSpec =
+ MeasureSpec.makeMeasureSpec(getAvailableTextWidthWith(anchorViewLocation), AT_MOST);
+ final int heightSpec =
+ MeasureSpec.makeMeasureSpec(0, UNSPECIFIED);
+ mTextView.measure(widthSpec, heightSpec);
+ return mTextView.getMeasuredWidth();
+ }
+
+ private int getTextHeightWith(Rect anchorViewLocation) {
+ final int widthSpec =
+ MeasureSpec.makeMeasureSpec(getAvailableTextWidthWith(anchorViewLocation), AT_MOST);
+ final int heightSpec =
+ MeasureSpec.makeMeasureSpec(0, UNSPECIFIED);
+ mTextView.measure(widthSpec, heightSpec);
+ return mTextView.getMeasuredHeight();
+ }
+
+ private int getAvailableTextWidthWith(Rect anchorViewLocation) {
+ return mScreenWidth - anchorViewLocation.width() - mArrowWidth - mArrowMargin
+ - mTextViewMargin;
+ }
+
+ private int getWindowWidthWith(Rect anchorViewLocation) {
+ return getTextWidthWith(anchorViewLocation) + mArrowWidth + mArrowMargin;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java
new file mode 100644
index 0000000..49056a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import android.content.Context;
+
+import com.android.systemui.R;
+
+/**
+ * Dock tooltip view that shows the info about moving the Accessibility button to the edge to hide.
+ */
+class DockTooltipView extends BaseTooltipView {
+ private final AccessibilityFloatingMenuView mAnchorView;
+
+ DockTooltipView(Context context, AccessibilityFloatingMenuView anchorView) {
+ super(context, anchorView);
+ mAnchorView = anchorView;
+
+ setDescription(
+ getContext().getText(R.string.accessibility_floating_button_docking_tooltip));
+ }
+
+ @Override
+ void hide() {
+ super.hide();
+
+ mAnchorView.stopTranslateXAnimation();
+ }
+
+ @Override
+ void show() {
+ super.show();
+
+ mAnchorView.startTranslateXAnimation();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java
new file mode 100644
index 0000000..e4f3e31
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_BUTTON_COMPONENT_NAME;
+
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.text.method.LinkMovementMethod;
+
+import com.android.systemui.R;
+
+/**
+ * Migration tooltip view that shows the information about the Accessibility button was replaced
+ * with the floating menu.
+ */
+class MigrationTooltipView extends BaseTooltipView {
+ MigrationTooltipView(Context context, AccessibilityFloatingMenuView anchorView) {
+ super(context, anchorView);
+
+ final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
+ ACCESSIBILITY_BUTTON_COMPONENT_NAME.flattenToShortString());
+
+ final AnnotationLinkSpan.LinkInfo linkInfo = new AnnotationLinkSpan.LinkInfo(
+ AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
+ v -> {
+ getContext().startActivity(intent);
+ hide();
+ });
+
+ final int textResId = R.string.accessibility_floating_button_migration_tooltip;
+ setDescription(AnnotationLinkSpan.linkify(getContext().getText(textResId), linkInfo));
+ setMovementMethod(LinkMovementMethod.getInstance());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 5f27400..3075617 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -59,7 +59,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
-import com.android.systemui.biometrics.HbmTypes.HbmType;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
@@ -72,6 +71,8 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import java.util.Optional;
+
import javax.inject.Inject;
/**
@@ -87,7 +88,7 @@
*/
@SuppressWarnings("deprecation")
@SysUISingleton
-public class UdfpsController implements DozeReceiver, HbmCallback {
+public class UdfpsController implements DozeReceiver {
private static final String TAG = "UdfpsController";
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
@@ -105,11 +106,12 @@
@NonNull private final DumpManager mDumpManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final KeyguardViewMediator mKeyguardViewMediator;
- @NonNull private final Vibrator mVibrator;
+ @Nullable private final Vibrator mVibrator;
@NonNull private final Handler mMainHandler;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
+ @Nullable private final UdfpsHbmCallback mHbmCallback;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -135,7 +137,8 @@
private boolean mScreenOn;
private Runnable mAodInterruptRunnable;
- private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
+ @VisibleForTesting
+ static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
@@ -144,7 +147,8 @@
private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK);
private final VibrationEffect mEffectTextureTick =
VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
- private final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ @VisibleForTesting
+ final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
private final VibrationEffect mEffectHeavy =
VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
private final VibrationEffect mDoubleClick =
@@ -152,6 +156,9 @@
private final Runnable mAcquiredVibration = new Runnable() {
@Override
public void run() {
+ if (mVibrator == null) {
+ return;
+ }
String effect = Settings.Global.getString(mContext.getContentResolver(),
"udfps_acquired_type");
mVibrator.vibrate(getVibration(effect, mEffectTick), VIBRATION_SONIFICATION_ATTRIBUTES);
@@ -331,7 +338,7 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_OUTSIDE:
udfpsView.onTouchOutsideView();
- break;
+ return true;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_HOVER_ENTER:
// To simplify the lifecycle of the velocity tracker, make sure it's never null
@@ -389,24 +396,28 @@
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
// TODO: this should eventually be removed after ux testing
- final ContentResolver contentResolver = mContext.getContentResolver();
- int startEnabled = Settings.Global.getInt(contentResolver,
- "udfps_start", 0);
- if (startEnabled > 0) {
- String startEffectSetting = Settings.Global.getString(
- contentResolver, "udfps_start_type");
- mVibrator.vibrate(getVibration(startEffectSetting, mEffectClick),
- VIBRATION_SONIFICATION_ATTRIBUTES);
+ if (mVibrator != null) {
+ final ContentResolver contentResolver =
+ mContext.getContentResolver();
+ int startEnabled = Settings.Global.getInt(contentResolver,
+ "udfps_start", 1);
+ if (startEnabled > 0) {
+ String startEffectSetting = Settings.Global.getString(
+ contentResolver, "udfps_start_type");
+ mVibrator.vibrate(getVibration(startEffectSetting,
+ mEffectClick), VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
+
+ int acquiredEnabled = Settings.Global.getInt(contentResolver,
+ "udfps_acquired", 0);
+ if (acquiredEnabled > 0) {
+ int delay = Settings.Global.getInt(contentResolver,
+ "udfps_acquired_delay", 500);
+ mMainHandler.removeCallbacks(mAcquiredVibration);
+ mMainHandler.postDelayed(mAcquiredVibration, delay);
+ }
}
- int acquiredEnabled = Settings.Global.getInt(contentResolver,
- "udfps_acquired", 0);
- if (acquiredEnabled > 0) {
- int delay = Settings.Global.getInt(contentResolver,
- "udfps_acquired_delay", 500);
- mMainHandler.removeCallbacks(mAcquiredVibration);
- mMainHandler.postDelayed(mAcquiredVibration, delay);
- }
handled = true;
} else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
Log.v(TAG, "onTouch | finger move: " + touchInfo);
@@ -456,11 +467,13 @@
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
- @NonNull ScreenLifecycle screenLifecycle) {
+ @NonNull ScreenLifecycle screenLifecycle,
+ @Nullable Vibrator vibrator,
+ @NonNull Optional<UdfpsHbmCallback> hbmCallback) {
mContext = context;
// TODO (b/185124905): inject main handler and vibrator once done prototyping
mMainHandler = new Handler(Looper.getMainLooper());
- mVibrator = context.getSystemService(Vibrator.class);
+ mVibrator = vibrator;
mInflater = inflater;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
// fingerprint manager should never be null.
@@ -476,6 +489,7 @@
mFalsingManager = falsingManager;
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
+ mHbmCallback = hbmCallback.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
@@ -588,6 +602,8 @@
default:
// Do nothing to stay in portrait mode.
}
+ // avoid announcing window title
+ mCoreLayoutParams.accessibilityTitle = " ";
return mCoreLayoutParams;
}
@@ -609,11 +625,19 @@
Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason);
mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
mView.setSensorProperties(mSensorProps);
- mView.setHbmCallback(this);
+ mView.setHbmCallback(mHbmCallback);
UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
animation.init();
mView.setAnimationViewController(animation);
+ // This view overlaps the sensor area, so prevent it from being selectable
+ // during a11y.
+ if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
+ || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) {
+ mView.setImportantForAccessibility(
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ }
+
mWindowManager.addView(mView, computeLayoutParams(animation));
mAccessibilityManager.addTouchExplorationStateChangeListener(
mTouchExplorationStateChangeListener);
@@ -781,17 +805,6 @@
mView.stopIllumination();
}
- @Override
- public void enableHbm(@HbmType int hbmType, @Nullable Surface surface) {
- // Do nothing. This method can be implemented for devices that require the high-brightness
- // mode for fingerprint illumination.
- }
-
- @Override
- public void disableHbm(@HbmType int hbmType, @Nullable Surface surface) {
- // Do nothing. This method can be implemented for devices that require the high-brightness
- // mode for fingerprint illumination.
- }
private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) {
if (TextUtils.isEmpty(effect)) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index cd5abd7..83ae865 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -24,6 +26,9 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.animation.AccelerateDecelerateInterpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -36,19 +41,35 @@
public class UdfpsEnrollDrawable extends UdfpsDrawable {
private static final String TAG = "UdfpsAnimationEnroll";
- static final float PROGRESS_BAR_RADIUS = 180.f;
+ static final float PROGRESS_BAR_RADIUS = 360.f;
+
+ private static final long ANIM_DURATION = 800;
+ // 1 + SCALE_MAX is the maximum that the moving target will animate to
+ private static final float SCALE_MAX = 0.25f;
@NonNull private final Drawable mMovingTargetFpIcon;
@NonNull private final Paint mSensorOutlinePaint;
@NonNull private final Paint mBlueFill;
@NonNull private final Paint mBlueStroke;
+ @NonNull private final Handler mHandler;
@Nullable private RectF mSensorRect;
@Nullable private UdfpsEnrollHelper mEnrollHelper;
+ // Moving target animator set
+ @Nullable AnimatorSet mAnimatorSet;
+ // Moving target location
+ float mCurrentX;
+ float mCurrentY;
+ // Moving target size
+ float mCurrentScale = 1.f;
+
+
UdfpsEnrollDrawable(@NonNull Context context) {
super(context);
+ mHandler = new Handler(Looper.getMainLooper());
+
mSensorOutlinePaint = new Paint(0 /* flags */);
mSensorOutlinePaint.setAntiAlias(true);
mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon));
@@ -90,26 +111,60 @@
invalidateSelf();
}
+ void onEnrollmentProgress(int remaining, int totalSteps) {
+ if (mEnrollHelper.isCenterEnrollmentComplete()) {
+ mHandler.post(() -> {
+ if (mAnimatorSet != null && mAnimatorSet.isRunning()) {
+ mAnimatorSet.end();
+ }
+
+ final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
+
+ final ValueAnimator x = ValueAnimator.ofFloat(mCurrentX, point.x);
+ x.addUpdateListener(animation -> {
+ mCurrentX = (float) animation.getAnimatedValue();
+ invalidateSelf();
+ });
+
+ final ValueAnimator y = ValueAnimator.ofFloat(mCurrentY, point.y);
+ y.addUpdateListener(animation -> {
+ mCurrentY = (float) animation.getAnimatedValue();
+ invalidateSelf();
+ });
+
+ final ValueAnimator scale = ValueAnimator.ofFloat(0, (float) Math.PI);
+ scale.setDuration(ANIM_DURATION);
+ scale.addUpdateListener(animation -> {
+ // Grow then shrink
+ mCurrentScale = 1 +
+ SCALE_MAX * (float) Math.sin((float) animation.getAnimatedValue());
+ invalidateSelf();
+ });
+
+ mAnimatorSet = new AnimatorSet();
+
+ mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
+ mAnimatorSet.setDuration(ANIM_DURATION);
+ mAnimatorSet.playTogether(x, y, scale);
+ mAnimatorSet.start();
+ });
+ }
+ }
+
@Override
public void draw(@NonNull Canvas canvas) {
if (isIlluminationShowing()) {
return;
}
- if (mSensorRect != null) {
- canvas.drawOval(mSensorRect, mSensorOutlinePaint);
- }
- mFingerprintDrawable.draw(canvas);
-
// Draw moving target
if (mEnrollHelper.isCenterEnrollmentComplete()) {
- mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha);
- mSensorOutlinePaint.setAlpha(mAlpha == 255 ? 64 : mAlpha);
-
canvas.save();
- final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
- canvas.translate(point.x, point.y);
+ canvas.translate(mCurrentX, mCurrentY);
+
if (mSensorRect != null) {
+ canvas.scale(mCurrentScale, mCurrentScale,
+ mSensorRect.centerX(), mSensorRect.centerY());
canvas.drawOval(mSensorRect, mBlueFill);
canvas.drawOval(mSensorRect, mBlueStroke);
}
@@ -117,6 +172,10 @@
mMovingTargetFpIcon.draw(canvas);
canvas.restore();
} else {
+ if (mSensorRect != null) {
+ canvas.drawOval(mSensorRect, mSensorOutlinePaint);
+ }
+ mFingerprintDrawable.draw(canvas);
mFingerprintDrawable.setAlpha(mAlpha);
mSensorOutlinePaint.setAlpha(mAlpha);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 521c495..470fb8c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -26,6 +26,7 @@
import android.provider.Settings;
import android.util.Log;
import android.util.TypedValue;
+import android.view.accessibility.AccessibilityManager;
import java.util.ArrayList;
import java.util.List;
@@ -53,6 +54,7 @@
@NonNull private final Context mContext;
// IUdfpsOverlayController reason
private final int mEnrollReason;
+ private final boolean mAccessibilityEnabled;
@NonNull private final List<PointF> mGuidedEnrollmentPoints;
private int mTotalSteps = -1;
@@ -67,6 +69,10 @@
public UdfpsEnrollHelper(@NonNull Context context, int reason) {
mContext = context;
mEnrollReason = reason;
+
+ final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
+ mAccessibilityEnabled = am.isEnabled();
+
mGuidedEnrollmentPoints = new ArrayList<>();
// Number of pixels per mm
@@ -148,6 +154,8 @@
boolean isCenterEnrollmentComplete() {
if (mTotalSteps == -1 || mRemainingSteps == -1) {
return false;
+ } else if (mAccessibilityEnabled) {
+ return false;
}
final int stepsEnrolled = mTotalSteps - mRemainingSteps;
return stepsEnrolled >= NUM_CENTER_TOUCHES;
@@ -155,6 +163,10 @@
@NonNull
PointF getNextGuidedEnrollmentPoint() {
+ if (mAccessibilityEnabled) {
+ return new PointF(0f, 0f);
+ }
+
float scale = SCALE;
if (Build.IS_ENG || Build.IS_USERDEBUG) {
scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index 75e8638..3bf1864 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -60,4 +60,8 @@
void setEnrollHelper(UdfpsEnrollHelper enrollHelper) {
mFingerprintDrawable.setEnrollHelper(enrollHelper);
}
+
+ void onEnrollmentProgress(int remaining, int totalSteps) {
+ mFingerprintDrawable.onEnrollmentProgress(remaining, totalSteps);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 1ebbfbf..c1f3fe0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -94,6 +94,7 @@
* Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
mProgressBar.setProgress(interpolatedProgress, true);
+ mView.onEnrollmentProgress(remaining, totalSteps);
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
index d90d0f8..85f0d27 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
@@ -19,18 +19,18 @@
import android.annotation.Nullable;
import android.view.Surface;
-import com.android.systemui.biometrics.HbmTypes.HbmType;
+import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
/**
* Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
* enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
* illumination is no longer necessary.
*/
-public interface HbmCallback {
+public interface UdfpsHbmCallback {
/**
* UdfpsView will call this to enable the HBM when the fingerprint illumination is needed.
*
- * @param hbmType The type of HBM that should be enabled. See {@link HbmTypes}.
+ * @param hbmType The type of HBM that should be enabled. See {@link UdfpsHbmTypes}.
* @param surface The surface for which the HBM is requested, in case the HBM implementation
* needs to set special surface flags to enable the HBM. Can be null.
*/
@@ -39,7 +39,7 @@
/**
* UdfpsView will call this to disable the HBM when the illumination is not longer needed.
*
- * @param hbmType The type of HBM that should be disabled. See {@link HbmTypes}.
+ * @param hbmType The type of HBM that should be disabled. See {@link UdfpsHbmTypes}.
* @param surface The surface for which the HBM is requested, in case the HBM implementation
* needs to unset special surface flags to disable the HBM. Can be null.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
index f798005..3ab0bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
@@ -25,7 +25,7 @@
/**
* Different high-brightness mode (HBM) types that are relevant to this package.
*/
-public final class HbmTypes {
+public final class UdfpsHbmTypes {
/** HBM that applies to the whole screen. */
public static final int GLOBAL_HBM = IUdfpsHbmListener.GLOBAL_HBM;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
index 8bea05b..1676bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
@@ -26,7 +26,7 @@
/**
* @param callback Invoked when HBM should be enabled or disabled.
*/
- void setHbmCallback(@Nullable HbmCallback callback);
+ void setHbmCallback(@Nullable UdfpsHbmCallback callback);
/**
* Invoked when illumination should start.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
index 6f7e6c3..8894093 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
@@ -69,7 +69,8 @@
mHintAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mHintAnimator.addUpdateListener(anim -> setStrokeWidth((float) anim.getAnimatedValue()));
- mLockScreenColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
+ mLockScreenColor = Utils.getColorAttrDefaultColor(mContext,
+ R.attr.wallpaperTextColorAccent);
mAmbientDisplayColor = Color.WHITE;
updateIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 52576a7..804e2ab 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -148,7 +148,7 @@
/**
* Animates in the bg protection circle behind the fp icon to highlight the icon.
*/
- void animateUdfpsBouncer() {
+ void animateUdfpsBouncer(Runnable onEndAnimation) {
if (mBgProtection.getVisibility() == View.VISIBLE && mBgProtection.getAlpha() == 1f) {
// already fully highlighted, don't re-animate
return;
@@ -186,6 +186,14 @@
ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f),
ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f),
fpIconAnim);
+ mAnimatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (onEndAnimation != null) {
+ onEndAnimation.run();
+ }
+ }
+ });
mAnimatorSet.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 33d0d0c..78588b2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -27,6 +27,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -128,6 +129,7 @@
mStatusBarStateController.removeCallback(mStateListener);
mKeyguardViewManager.setAlternateAuthInterceptor(null);
mTransitioningFromHome = false;
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
if (mCancelRunnable != null) {
mCancelRunnable.run();
@@ -164,9 +166,13 @@
mShowingUdfpsBouncer = show;
updatePauseAuth();
if (mShowingUdfpsBouncer) {
- mView.animateUdfpsBouncer();
+ mView.animateUdfpsBouncer(() ->
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true));
+ mView.announceForAccessibility(mView.getContext().getString(
+ R.string.accessibility_fingerprint_bouncer));
} else {
- mView.animateAwayUdfpsBouncer(() -> mKeyguardViewManager.cancelPostAuthActions());
+ mView.animateAwayUdfpsBouncer(null);
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
}
return true;
}
@@ -228,8 +234,8 @@
*/
private void maybeShowInputBouncer() {
if (mShowingUdfpsBouncer) {
- mKeyguardViewManager.resetAlternateAuth(false);
mKeyguardViewManager.showBouncer(true);
+ mKeyguardViewManager.resetAlternateAuth(false);
}
}
@@ -266,7 +272,7 @@
private void updateAlpha() {
// fade icon on transition to showing bouncer
int alpha = mShowingUdfpsBouncer ? 255
- : Math.abs((int) MathUtils.map(.4f, 0f, .7f, 255f,
+ : Math.abs((int) MathUtils.constrainedMap(0f, 255f, .4f, .7f,
mInputBouncerHiddenAmount));
mView.setUnpausedAlpha(alpha);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
index 4d441bd..aa5f0f6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -31,7 +31,7 @@
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import com.android.systemui.biometrics.HbmTypes.HbmType;
+import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
/**
* Under-display fingerprint sensor Surface View. The surface should be used for HBM-specific things
@@ -41,7 +41,7 @@
private static final String TAG = "UdfpsSurfaceView";
private static final String SETTING_HBM_TYPE =
"com.android.systemui.biometrics.UdfpsSurfaceView.hbmType";
- private static final @HbmType int DEFAULT_HBM_TYPE = HbmTypes.GLOBAL_HBM;
+ private static final @HbmType int DEFAULT_HBM_TYPE = UdfpsHbmTypes.GLOBAL_HBM;
/**
* This is used instead of {@link android.graphics.drawable.Drawable}, because the latter has
@@ -57,7 +57,7 @@
private final @HbmType int mHbmType;
@NonNull private RectF mSensorRect;
- @Nullable private HbmCallback mHbmCallback;
+ @Nullable private UdfpsHbmCallback mHbmCallback;
public UdfpsSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -90,7 +90,7 @@
}
@Override
- public void setHbmCallback(@Nullable HbmCallback callback) {
+ public void setHbmCallback(@Nullable UdfpsHbmCallback callback) {
mHbmCallback = callback;
}
@@ -102,7 +102,7 @@
Log.e(TAG, "startIllumination | mHbmCallback is null");
}
- if (mHbmType == HbmTypes.GLOBAL_HBM) {
+ if (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) {
drawImmediately(mIlluminationDotDrawable);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index f10d5f3..a1d3040 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -101,7 +101,7 @@
}
@Override
- public void setHbmCallback(@Nullable HbmCallback callback) {
+ public void setHbmCallback(@Nullable UdfpsHbmCallback callback) {
mHbmSurfaceView.setHbmCallback(callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 0f202b0..9697369 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -257,7 +257,9 @@
@Override
public void onTouchEvent(MotionEvent ev) {
- if (!mKeyguardStateController.isShowing() || mStatusBarStateController.isDozing()) {
+ if (!mKeyguardStateController.isShowing()
+ || (mStatusBarStateController.isDozing()
+ && !mStatusBarStateController.isPulsing())) {
avoidGesture();
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
index 5557c86..469e60a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
@@ -51,7 +51,6 @@
private val userTracker: UserTracker,
private val secureSettings: SecureSettings
) {
-
private val contentResolver: ContentResolver
get() = context.contentResolver
@@ -66,7 +65,7 @@
init {
if (featureEnabled) {
secureSettings.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT),
+ Settings.Secure.getUriFor(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS),
false, /* notifyForDescendants */
showWhileLockedObserver
)
@@ -116,7 +115,7 @@
private fun updateShowWhileLocked() {
canShowWhileLockedSetting = secureSettings.getInt(
- Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0) != 0
+ Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0) != 0
}
enum class Visibility {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index cba3dab..8ad5099 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -55,8 +55,9 @@
companion object {
private const val TAG = "ControlsProviderSelectorActivity"
+ const val BACK_SHOULD_EXIT = "back_should_exit"
}
-
+ private var backShouldExit = false
private lateinit var recyclerView: RecyclerView
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = listingController.currentUserId
@@ -102,13 +103,17 @@
}
}
requireViewById<View>(R.id.done).visibility = View.GONE
+
+ backShouldExit = intent.getBooleanExtra(BACK_SHOULD_EXIT, false)
}
override fun onBackPressed() {
- val i = Intent().apply {
- component = ComponentName(applicationContext, ControlsActivity::class.java)
+ if (!backShouldExit) {
+ val i = Intent().apply {
+ component = ComponentName(applicationContext, ControlsActivity::class.java)
+ }
+ startActivity(i, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
}
- startActivity(i, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
animateExitAndFinish()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 43607dd..b15bb45 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -46,6 +46,7 @@
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
+import androidx.annotation.ColorInt
import com.android.internal.graphics.ColorUtils
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
@@ -313,7 +314,8 @@
@ColorRes bgColor: Int
) {
val bg = context.resources.getColor(R.color.control_default_background, context.theme)
- var (newClipColor, newAlpha) = if (enabled) {
+
+ val (newClipColor, newAlpha) = if (enabled) {
// allow color overrides for the enabled state only
val color = cws.control?.getCustomColor()?.let {
val state = intArrayOf(android.R.attr.state_enabled)
@@ -326,58 +328,87 @@
ALPHA_DISABLED
)
}
+ val newBaseColor = if (behavior is ToggleRangeBehavior) {
+ ColorUtils.blendARGB(bg, newClipColor, toggleBackgroundIntensity)
+ } else {
+ bg
+ }
- clipLayer.getDrawable().apply {
+ clipLayer.drawable?.apply {
clipLayer.alpha = ALPHA_DISABLED
-
- val newBaseColor = if (behavior is ToggleRangeBehavior) {
- ColorUtils.blendARGB(bg, newClipColor, toggleBackgroundIntensity)
- } else {
- bg
- }
stateAnimator?.cancel()
if (animated) {
- val oldColor = if (this is GradientDrawable) {
- this.color?.defaultColor ?: newClipColor
- } else {
- newClipColor
- }
- val oldBaseColor = baseLayer.color?.defaultColor ?: newBaseColor
- val oldAlpha = layout.alpha
-
- // Animate both alpha and background colors. Only animate colors for
- // GradientDrawables and not static images as used for the ThumbnailTemplate.
- stateAnimator = ValueAnimator.ofInt(clipLayer.alpha, newAlpha).apply {
- addUpdateListener {
- alpha = it.animatedValue as Int
- if (this is GradientDrawable) {
- this.setColor(ColorUtils.blendARGB(oldColor, newClipColor,
- it.animatedFraction))
- }
- baseLayer.setColor(ColorUtils.blendARGB(oldBaseColor, newBaseColor,
- it.animatedFraction))
- layout.alpha = MathUtils.lerp(oldAlpha, 1f, it.animatedFraction)
- }
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- stateAnimator = null
- }
- })
- duration = STATE_ANIMATION_DURATION
- interpolator = Interpolators.CONTROL_STATE
- start()
- }
+ startBackgroundAnimation(this, newAlpha, newClipColor, newBaseColor)
} else {
- alpha = newAlpha
- if (this is GradientDrawable) {
- this.setColor(newClipColor)
- }
- baseLayer.setColor(newBaseColor)
- layout.alpha = 1f
+ applyBackgroundChange(
+ this, newAlpha, newClipColor, newBaseColor, newLayoutAlpha = 1f
+ )
}
}
}
+ private fun startBackgroundAnimation(
+ clipDrawable: Drawable,
+ newAlpha: Int,
+ @ColorInt newClipColor: Int,
+ @ColorInt newBaseColor: Int
+ ) {
+ val oldClipColor = if (clipDrawable is GradientDrawable) {
+ clipDrawable.color?.defaultColor ?: newClipColor
+ } else {
+ newClipColor
+ }
+ val oldBaseColor = baseLayer.color?.defaultColor ?: newBaseColor
+ val oldAlpha = layout.alpha
+
+ stateAnimator = ValueAnimator.ofInt(clipLayer.alpha, newAlpha).apply {
+ addUpdateListener {
+ val updatedAlpha = it.animatedValue as Int
+ val updatedClipColor = ColorUtils.blendARGB(oldClipColor, newClipColor,
+ it.animatedFraction)
+ val updatedBaseColor = ColorUtils.blendARGB(oldBaseColor, newBaseColor,
+ it.animatedFraction)
+ val updatedLayoutAlpha = MathUtils.lerp(oldAlpha, 1f, it.animatedFraction)
+ applyBackgroundChange(
+ clipDrawable,
+ updatedAlpha,
+ updatedClipColor,
+ updatedBaseColor,
+ updatedLayoutAlpha
+ )
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ stateAnimator = null
+ }
+ })
+ duration = STATE_ANIMATION_DURATION
+ interpolator = Interpolators.CONTROL_STATE
+ start()
+ }
+ }
+
+ /**
+ * Applies a change in background.
+ *
+ * Updates both alpha and background colors. Only updates colors for GradientDrawables and not
+ * static images as used for the ThumbnailTemplate.
+ */
+ private fun applyBackgroundChange(
+ clipDrawable: Drawable,
+ newAlpha: Int,
+ @ColorInt newClipColor: Int,
+ @ColorInt newBaseColor: Int,
+ newLayoutAlpha: Float
+ ) {
+ clipDrawable.alpha = newAlpha
+ if (clipDrawable is GradientDrawable) {
+ clipDrawable.setColor(newClipColor)
+ }
+ baseLayer.setColor(newBaseColor)
+ layout.alpha = newLayoutAlpha
+ }
+
private fun animateStatusChange(animated: Boolean, statusRowUpdater: () -> Unit) {
statusAnimator?.cancel()
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 26be987..c322f45 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -217,22 +217,8 @@
}
private fun showInitialSetupView(items: List<SelectionItem>) {
- val inflater = LayoutInflater.from(context)
- inflater.inflate(R.layout.controls_no_favorites, parent, true)
-
- val viewGroup = parent.requireViewById(R.id.controls_no_favorites_group) as ViewGroup
- viewGroup.setOnClickListener { _: View -> startProviderSelectorActivity() }
-
- val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
- subtitle.setText(context.resources.getString(R.string.quick_controls_subtitle))
-
- val iconRowGroup = parent.requireViewById(R.id.controls_icon_row) as ViewGroup
- items.forEach {
- val imageView = inflater.inflate(R.layout.controls_icon, viewGroup, false) as ImageView
- imageView.setContentDescription(it.getTitle())
- imageView.setImageDrawable(it.icon)
- iconRowGroup.addView(imageView)
- }
+ startProviderSelectorActivity()
+ onDismiss.run()
}
private fun startFavoritingActivity(si: StructureInfo) {
@@ -261,7 +247,9 @@
}
private fun startProviderSelectorActivity() {
- startActivity(Intent(activityContext, ControlsProviderSelectorActivity::class.java))
+ val i = Intent(activityContext, ControlsProviderSelectorActivity::class.java)
+ i.putExtra(ControlsProviderSelectorActivity.BACK_SHOULD_EXIT, true)
+ startActivity(i)
}
private fun startActivity(intent: Intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index fd80d50..26db33d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -29,6 +29,7 @@
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.app.role.RoleManager;
+import android.app.smartspace.SmartspaceManager;
import android.app.trust.TrustManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -400,4 +401,10 @@
static PermissionManager providePermissionManager(Context context) {
return context.getSystemService(PermissionManager.class);
}
+
+ @Provides
+ @Singleton
+ static SmartspaceManager provideSmartspaceManager(Context context) {
+ return context.getSystemService(SmartspaceManager.class);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7fa48d4..1396099 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -29,6 +29,7 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
+import com.android.systemui.biometrics.UdfpsHbmCallback;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
@@ -160,6 +161,9 @@
@BindsOptionalOf
abstract StatusBar optionalStatusBar();
+ @BindsOptionalOf
+ abstract UdfpsHbmCallback optionalUdfpsHbmCallback();
+
@SysUISingleton
@Binds
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index a83b13c..3873c25 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -226,7 +226,7 @@
ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
this::getWalletViewController, mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter);
+ mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger());
if (shouldShowLockMessage(dialog)) {
dialog.showLockMessage();
@@ -294,11 +294,11 @@
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter) {
+ MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
adapter, overflowAdapter, depthController, sysuiColorExtractor,
statusBarService, notificationShadeWindowController, sysuiState,
- onRotateCallback, keyguardShowing, powerAdapter);
+ onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger);
mWalletFactory = walletFactory;
// Update window attributes
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 2eb3762..f9bb35fc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -249,7 +249,43 @@
GA_SCREENSHOT_PRESS(347),
@UiEvent(doc = "The global actions screenshot button was long pressed.")
- GA_SCREENSHOT_LONG_PRESS(348);
+ GA_SCREENSHOT_LONG_PRESS(348),
+
+ @UiEvent(doc = "The global actions power off button was pressed.")
+ GA_SHUTDOWN_PRESS(802),
+
+ @UiEvent(doc = "The global actions power off button was long pressed.")
+ GA_SHUTDOWN_LONG_PRESS(803),
+
+ @UiEvent(doc = "The global actions reboot button was pressed.")
+ GA_REBOOT_PRESS(349),
+
+ @UiEvent(doc = "The global actions reboot button was long pressed.")
+ GA_REBOOT_LONG_PRESS(804),
+
+ @UiEvent(doc = "The global actions lockdown button was pressed.")
+ GA_LOCKDOWN_PRESS(354), // already created by cwren apparently
+
+ @UiEvent(doc = "Power menu was opened via quick settings button.")
+ GA_OPEN_QS(805),
+
+ @UiEvent(doc = "Power menu was opened via power + volume up.")
+ GA_OPEN_POWER_VOLUP(806),
+
+ @UiEvent(doc = "Power menu was opened via long press on power.")
+ GA_OPEN_LONG_PRESS_POWER(807),
+
+ @UiEvent(doc = "Power menu was closed via long press on power.")
+ GA_CLOSE_LONG_PRESS_POWER(808),
+
+ @UiEvent(doc = "Power menu was dismissed by back gesture.")
+ GA_CLOSE_BACK(809),
+
+ @UiEvent(doc = "Power menu was dismissed by tapping outside dialog.")
+ GA_CLOSE_TAP_OUTSIDE(810),
+
+ @UiEvent(doc = "Power menu was closed via power + volume up.")
+ GA_CLOSE_POWER_VOLUP(811);
private final int mId;
@@ -349,6 +385,10 @@
return mContext;
}
+ protected UiEventLogger getEventLogger() {
+ return mUiEventLogger;
+ }
+
/**
* Show the global actions dialog (creating if necessary)
*
@@ -581,7 +621,7 @@
mAdapter, mOverflowAdapter,
mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter);
+ mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -679,13 +719,14 @@
@VisibleForTesting
final class ShutDownAction extends SinglePressAction implements LongPressAction {
- private ShutDownAction() {
+ ShutDownAction() {
super(R.drawable.ic_lock_power_off,
R.string.global_action_power_off);
}
@Override
public boolean onLongPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.reboot(true);
return true;
@@ -705,6 +746,7 @@
@Override
public void onPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_PRESS);
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown();
}
@@ -807,12 +849,13 @@
@VisibleForTesting
final class RestartAction extends SinglePressAction implements LongPressAction {
- private RestartAction() {
+ RestartAction() {
super(R.drawable.ic_restart, R.string.global_action_restart);
}
@Override
public boolean onLongPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.reboot(true);
return true;
@@ -832,6 +875,7 @@
@Override
public void onPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_PRESS);
mWindowManagerFuncs.reboot(false);
}
}
@@ -1062,6 +1106,7 @@
public void onPress() {
mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
UserHandle.USER_ALL);
+ mUiEventLogger.log(GlobalActionsEvent.GA_LOCKDOWN_PRESS);
try {
mIWindowManager.lockNow(null);
// Lock profiles (if any) on the background thread.
@@ -2049,6 +2094,7 @@
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRotateCallback;
+ private UiEventLogger mUiEventLogger;
protected ViewGroup mContainer;
@@ -2058,7 +2104,7 @@
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter) {
+ MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
super(context, themeRes);
mContext = context;
mAdapter = adapter;
@@ -2071,6 +2117,7 @@
mSysUiState = sysuiState;
mOnRotateCallback = onRotateCallback;
mKeyguardShowing = keyguardShowing;
+ mUiEventLogger = uiEventLogger;
// Window initialization
Window window = getWindow();
@@ -2141,7 +2188,7 @@
mGlobalActionsLayout.setAdapter(mAdapter);
mContainer = findViewById(com.android.systemui.R.id.global_actions_container);
mContainer.setOnClickListener(v -> {
- // TODO: add logging (b/182830510)
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
cancel();
});
@@ -2218,6 +2265,12 @@
}
@Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_BACK);
+ }
+
+ @Override
public void show() {
super.show();
// split this up so we can override but still call Dialog.show
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index b668e88..241c2a9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -64,6 +64,12 @@
mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN)
}
+ /**
+ * Is the media player visible?
+ */
+ var visible = false
+ private set
+
var visibilityChangedListener: ((Boolean) -> Unit)? = null
/**
@@ -106,11 +112,11 @@
val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD ||
statusBarStateController.state == StatusBarState.FULLSCREEN_USER_SWITCHER)
// mediaHost.visible required for proper animations handling
- val shouldBeVisible = mediaHost.visible &&
+ visible = mediaHost.visible &&
!bypassController.bypassEnabled &&
keyguardOrUserSwitcher &&
notifLockscreenUserManager.shouldShowLockscreenNotifications()
- if (shouldBeVisible) {
+ if (visible) {
showMediaPlayer()
} else {
hideMediaPlayer()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 644876c..a1c06fc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -3,6 +3,7 @@
import android.app.smartspace.SmartspaceTarget
import android.content.Context
import android.content.Intent
+import android.content.res.ColorStateList
import android.content.res.Configuration
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
import android.util.Log
@@ -115,7 +116,7 @@
private var needsReordering: Boolean = false
private var keysNeedRemoval = mutableSetOf<String>()
private var bgColor = getBackgroundColor()
- private var shouldScrollToActivePlayer: Boolean = false
+ protected var shouldScrollToActivePlayer: Boolean = false
private var isRtl: Boolean = false
set(value) {
if (value != field) {
@@ -184,7 +185,14 @@
true /* persistent */)
mediaManager.addListener(object : MediaDataManager.Listener {
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- addOrUpdatePlayer(key, oldKey, data)
+ if (addOrUpdatePlayer(key, oldKey, data)) {
+ MediaPlayerData.getMediaPlayer(key, null)?.let {
+ logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
+ it.mInstanceId,
+ /* isRecommendationCard */ false,
+ it.surfaceForSmartspaceLogging)
+ }
+ }
val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
if (canRemove && !Utils.useMediaResumption(context)) {
// This view isn't playing, let's remove this! This happens e.g when
@@ -200,9 +208,19 @@
}
}
- override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
+ override fun onSmartspaceMediaDataLoaded(
+ key: String,
+ data: SmartspaceTarget,
+ shouldPrioritize: Boolean
+ ) {
Log.d(TAG, "My Smartspace media update is here")
- addSmartspaceMediaRecommendations(key, data)
+ addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
+ MediaPlayerData.getMediaPlayer(key, null)?.let {
+ logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
+ it.mInstanceId,
+ /* isRecommendationCard */ true,
+ it.surfaceForSmartspaceLogging)
+ }
if (mediaCarouselScrollHandler.visibleToUser) {
logSmartspaceImpression()
}
@@ -276,7 +294,8 @@
}
}
- private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) {
+ // Returns true if new player is added
+ private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData): Boolean {
val dataCopy = data.copy(backgroundColor = bgColor)
val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey)
if (existingPlayer == null) {
@@ -295,7 +314,7 @@
} else {
existingPlayer.bindPlayer(dataCopy, key)
MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer)
- if (visualStabilityManager.isReorderingAllowed) {
+ if (visualStabilityManager.isReorderingAllowed || shouldScrollToActivePlayer) {
reorderAllPlayers()
} else {
needsReordering = true
@@ -309,9 +328,14 @@
if (MediaPlayerData.players().size != mediaContent.childCount) {
Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync")
}
+ return existingPlayer == null
}
- private fun addSmartspaceMediaRecommendations(key: String, data: SmartspaceTarget) {
+ private fun addSmartspaceMediaRecommendations(
+ key: String,
+ data: SmartspaceTarget,
+ shouldPrioritize: Boolean
+ ) {
Log.d(TAG, "Updating smartspace target in carousel")
if (MediaPlayerData.getMediaPlayer(key, null) != null) {
Log.w(TAG, "Skip adding smartspace target in carousel")
@@ -325,8 +349,8 @@
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp)
- newRecs.bindRecommendation(data, bgColor, { v -> shouldScrollToActivePlayer = true })
- MediaPlayerData.addMediaRecommendation(key, newRecs)
+ newRecs.bindRecommendation(data, bgColor)
+ MediaPlayerData.addMediaRecommendation(key, newRecs, shouldPrioritize)
updatePlayerToState(newRecs, noAnimation = true)
reorderAllPlayers()
updatePageIndicator()
@@ -365,6 +389,7 @@
private fun recreatePlayers() {
bgColor = getBackgroundColor()
+ pageIndicator.tintList = ColorStateList.valueOf(getForegroundColor())
MediaPlayerData.mediaData().forEach { (key, data) ->
removePlayer(key, dismissMediaData = false)
@@ -376,6 +401,10 @@
return context.getColor(android.R.color.system_accent2_50)
}
+ private fun getForegroundColor(): Int {
+ return context.getColor(android.R.color.system_accent2_900)
+ }
+
private fun updatePageIndicator() {
val numPages = mediaContent.getChildCount()
pageIndicator.setNumPages(numPages)
@@ -520,12 +549,20 @@
this.desiredLocation = desiredLocation
this.desiredHostState = it
currentlyExpanded = it.expansion > 0
+
+ val shouldCloseGuts = !currentlyExpanded && !mediaManager.hasActiveMedia() &&
+ desiredHostState.showsOnlyActiveMedia
+
for (mediaPlayer in MediaPlayerData.players()) {
if (animate) {
mediaPlayer.mediaViewController.animatePendingStateChange(
duration = duration,
delay = startDelay)
}
+ if (shouldCloseGuts && mediaPlayer.mediaViewController.isGutsVisible) {
+ mediaPlayer.closeGuts(!animate)
+ }
+
mediaPlayer.mediaViewController.onLocationPreChange(desiredLocation)
}
mediaCarouselScrollHandler.showsSettingsButton = !it.showsOnlyActiveMedia
@@ -541,9 +578,9 @@
}
}
- fun closeGuts() {
+ fun closeGuts(immediate: Boolean = true) {
MediaPlayerData.players().forEach {
- it.closeGuts(true)
+ it.closeGuts(immediate)
}
}
@@ -641,18 +678,20 @@
@VisibleForTesting
internal object MediaPlayerData {
private val EMPTY = MediaData(-1, false, 0, null, null, null, null, null,
- emptyList(), emptyList(), "INVALID", null, null, null, false, null)
+ emptyList(), emptyList(), "INVALID", null, null, null, true, null)
+ // Whether should prioritize Smartspace card.
+ private var shouldPrioritizeSs: Boolean = false
data class MediaSortKey(
- // Is Smartspace media recommendation. When the Smartspace media is present, it should
- // always be the first card in carousel.
+ // Whether the item represents a Smartspace media recommendation.
val isSsMediaRec: Boolean,
val data: MediaData,
val updateTime: Long = 0
)
private val comparator =
- compareByDescending<MediaSortKey> { it.isSsMediaRec }
+ compareByDescending<MediaSortKey>
+ { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec }
.thenByDescending { it.data.isPlaying }
.thenByDescending { it.data.isLocalSession }
.thenByDescending { !it.data.resumption }
@@ -668,7 +707,8 @@
mediaPlayers.put(sortKey, player)
}
- fun addMediaRecommendation(key: String, player: MediaControlPanel) {
+ fun addMediaRecommendation(key: String, player: MediaControlPanel, shouldPrioritize: Boolean) {
+ shouldPrioritizeSs = shouldPrioritize
removeMediaPlayer(key)
val sortKey = MediaSortKey(isSsMediaRec = true, EMPTY, System.currentTimeMillis())
mediaData.put(key, sortKey)
@@ -694,7 +734,7 @@
/** Returns the index of the first non-timeout media. */
fun getActiveMediaIndex(): Int {
mediaPlayers.entries.forEachIndexed { index, e ->
- if (e.key.data.active) {
+ if (!e.key.isSsMediaRec && e.key.data.active) {
return index
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index e5a6271..c806bcf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -59,7 +59,7 @@
private val mainExecutor: DelayableExecutor,
private val dismissCallback: () -> Unit,
private var translationChangedListener: () -> Unit,
- private val closeGuts: () -> Unit,
+ private val closeGuts: (immediate: Boolean) -> Unit,
private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
private val logSmartspaceImpression: () -> Unit
@@ -473,7 +473,7 @@
if (oldIndex != visibleMediaIndex && visibleToUser) {
logSmartspaceImpression()
}
- closeGuts()
+ closeGuts(false)
updatePlayerVisibilities()
}
val relativeLocation = visibleMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index fe3463f..e213dfe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -25,7 +25,8 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -44,6 +45,7 @@
import androidx.annotation.UiThread;
import androidx.constraintlayout.widget.ConstraintSet;
+import com.android.settingslib.Utils;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -212,7 +214,8 @@
mMediaViewController.openGuts();
return true;
} else {
- return false;
+ closeGuts();
+ return true;
}
});
mPlayerViewHolder.getCancel().setOnClickListener(v -> {
@@ -276,7 +279,7 @@
if (mMediaViewController.isGutsVisible()) return;
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- false);
+ /* isRecommendationCard */ false);
mActivityStarter.postStartActivityDismissingKeyguard(clickIntent,
buildLaunchAnimatorController(mPlayerViewHolder.getPlayer()));
});
@@ -301,11 +304,23 @@
}
// App icon
- ImageView appIcon = mPlayerViewHolder.getAppIcon();
- if (data.getAppIcon() != null) {
- appIcon.setImageIcon(data.getAppIcon());
+ ImageView appIconView = mPlayerViewHolder.getAppIcon();
+ appIconView.clearColorFilter();
+ if (data.getAppIcon() != null && !data.getResumption()) {
+ appIconView.setImageIcon(data.getAppIcon());
+ int color = Utils.getColorAttrDefaultColor(mContext,
+ com.android.internal.R.attr.colorAccentTertiary);
+ appIconView.setColorFilter(color);
} else {
- appIcon.setImageResource(R.drawable.ic_music_note);
+ appIconView.setColorFilter(getGrayscaleFilter());
+ try {
+ Drawable icon = mContext.getPackageManager().getApplicationIcon(
+ data.getPackageName());
+ appIconView.setImageDrawable(icon);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
+ appIconView.setImageResource(R.drawable.ic_music_note);
+ }
}
// Song name
@@ -384,7 +399,7 @@
button.setEnabled(true);
button.setOnClickListener(v -> {
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- false);
+ /* isRecommendationCard */ false);
action.run();
});
}
@@ -398,7 +413,7 @@
setVisibleAndAlpha(collapsedSet, ACTION_IDS[i], false /*visible */);
setVisibleAndAlpha(expandedSet, ACTION_IDS[i], false /* visible */);
}
- // If no expanded buttons, set the first view as INVISIBLE so z remains constant
+ // If no actions, set the first view as INVISIBLE so expanded height remains constant
if (actionIcons.size() == 0) {
expandedSet.setVisibility(ACTION_IDS[0], ConstraintSet.INVISIBLE);
}
@@ -418,7 +433,7 @@
mPlayerViewHolder.getDismiss().setEnabled(isDismissible);
mPlayerViewHolder.getDismiss().setOnClickListener(v -> {
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
- false);
+ /* isRecommendationCard */ false);
if (mKey != null) {
closeGuts();
@@ -426,7 +441,7 @@
mMediaDataManagerLazy.get().dismissMediaData(mKey,
MediaViewController.GUTS_ANIMATION_DURATION + 100);
return true;
- }, /* requiresShadeOpen */ true);
+ }, /* requiresShadeOpen */ true, false);
} else {
Log.w(TAG, "Dismiss media with null notification. Token uid="
+ data.getToken().getUid());
@@ -472,10 +487,7 @@
}
/** Bind this recommendation view based on the data given. */
- public void bindRecommendation(
- @NonNull SmartspaceTarget target,
- @NonNull int backgroundColor,
- @Nullable View.OnClickListener callback) {
+ public void bindRecommendation(@NonNull SmartspaceTarget target, @NonNull int backgroundColor) {
if (mRecommendationViewHolder == null) {
return;
}
@@ -517,6 +529,7 @@
try {
icon = mContext.getPackageManager().getApplicationIcon(
packageName);
+ icon.setColorFilter(getGrayscaleFilter());
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "No media source icon can be fetched via package name", e);
}
@@ -528,18 +541,13 @@
// Set up media source app's logo.
ImageView mediaSourceLogoImageView = mediaLogoItems.get(uiComponentIndex);
mediaSourceLogoImageView.setImageDrawable(icon);
- // TODO(b/186699032): Tint the app logo using the accent color.
- mediaSourceLogoImageView.setColorFilter(backgroundColor, PorterDuff.Mode.XOR);
// Set up media item cover.
ImageView mediaCoverImageView = mediaCoverItems.get(uiComponentIndex);
mediaCoverImageView.setImageIcon(recommendation.getIcon());
// Set up the click listener if applicable.
- setSmartspaceRecItemOnClickListener(
- mediaCoverImageView,
- recommendation,
- callback);
+ setSmartspaceRecItemOnClickListener(mediaCoverImageView, recommendation);
if (uiComponentIndex < MEDIA_RECOMMENDATION_ITEMS_PER_ROW) {
setVisibleAndAlpha(collapsedSet,
@@ -563,13 +571,13 @@
// Set up long press to show guts setting panel.
mRecommendationViewHolder.getDismiss().setOnClickListener(v -> {
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
- true);
+ /* isRecommendationCard */ true);
closeGuts();
mKeyguardDismissUtil.executeWhenUnlocked(() -> {
mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
MediaViewController.GUTS_ANIMATION_DURATION + 100L);
return true;
- }, true /* requiresShadeOpen */);
+ }, true /* requiresShadeOpen */, false);
});
mController = null;
@@ -651,6 +659,12 @@
return (state.getState() == PlaybackState.STATE_PLAYING);
}
+ private ColorMatrixColorFilter getGrayscaleFilter() {
+ ColorMatrix matrix = new ColorMatrix();
+ matrix.setSaturation(0);
+ return new ColorMatrixColorFilter(matrix);
+ }
+
private void setVisibleAndAlpha(ConstraintSet set, int actionId, boolean visible) {
set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : ConstraintSet.GONE);
set.setAlpha(actionId, visible ? 1.0f : 0.0f);
@@ -658,8 +672,7 @@
private void setSmartspaceRecItemOnClickListener(
@NonNull View view,
- @NonNull SmartspaceAction action,
- @Nullable View.OnClickListener callback) {
+ @NonNull SmartspaceAction action) {
if (view == null || action == null || action.getIntent() == null) {
Log.e(TAG, "No tap action can be set up");
return;
@@ -668,7 +681,7 @@
view.setOnClickListener(v -> {
// When media recommendation card is shown, it will always be the top card.
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- true);
+ /* isRecommendationCard */ true);
if (shouldSmartspaceRecItemOpenInForeground(action)) {
// Request to unlock the device if the activity needs to be opened in foreground.
@@ -682,9 +695,8 @@
view.getContext().startActivity(action.getIntent());
}
- if (callback != null) {
- callback.onClick(v);
- }
+ // Automatically scroll to the active player once the media is loaded.
+ mMediaCarouselController.setShouldScrollToActivePlayer(true);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index 2c094b8..87af9e0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -38,7 +38,11 @@
}
}
- override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
+ override fun onSmartspaceMediaDataLoaded(
+ key: String,
+ data: SmartspaceTarget,
+ shouldPrioritize: Boolean
+ ) {
listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index e5c15107..3deb5d1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -26,9 +26,11 @@
import com.android.systemui.settings.CurrentUserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.util.time.SystemClock
+import java.util.SortedMap
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import javax.inject.Inject
+import kotlin.collections.LinkedHashMap
private const val TAG = "MediaDataFilter"
private const val DEBUG = true
@@ -39,7 +41,7 @@
*/
@VisibleForTesting
internal val SMARTSPACE_MAX_AGE = SystemProperties
- .getLong("debug.sysui.smartspace_max_age", TimeUnit.HOURS.toMillis(3))
+ .getLong("debug.sysui.smartspace_max_age", TimeUnit.MINUTES.toMillis(30))
/**
* Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user
@@ -65,7 +67,8 @@
private val allEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
// The filtered userEntries, which will be a subset of all userEntries in MediaDataManager
private val userEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
- private var hasSmartspace: Boolean = false
+ var hasSmartspace: Boolean = false
+ private set
private var reactivatedKey: String? = null
init {
@@ -99,39 +102,39 @@
}
}
- override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
+ override fun onSmartspaceMediaDataLoaded(
+ key: String,
+ data: SmartspaceTarget,
+ shouldPrioritize: Boolean
+ ) {
+ var shouldPrioritizeMutable = shouldPrioritize
hasSmartspace = true
// Before forwarding the smartspace target, first check if we have recently inactive media
- val now = systemClock.elapsedRealtime()
val sorted = userEntries.toSortedMap(compareBy {
userEntries.get(it)?.lastActive ?: -1
})
- if (sorted.size > 0) {
+ val timeSinceActive = timeSinceActiveForMostRecentMedia(sorted)
+ if (timeSinceActive < SMARTSPACE_MAX_AGE) {
val lastActiveKey = sorted.lastKey() // most recently active
- val timeSinceActive = sorted.get(lastActiveKey)?.let {
- now - it.lastActive
- } ?: Long.MAX_VALUE
- if (timeSinceActive < SMARTSPACE_MAX_AGE) {
- // Notify listeners to consider this media active
- Log.d(TAG, "reactivating $lastActiveKey instead of smartspace")
- reactivatedKey = lastActiveKey
- val mediaData = sorted.get(lastActiveKey)!!.copy(active = true)
- listeners.forEach {
- it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData)
- }
- return
+ // Notify listeners to consider this media active
+ Log.d(TAG, "reactivating $lastActiveKey instead of smartspace")
+ reactivatedKey = lastActiveKey
+ val mediaData = sorted.get(lastActiveKey)!!.copy(active = true)
+ listeners.forEach {
+ it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData)
}
+ } else {
+ // Mark to prioritize Smartspace card if no recent media.
+ shouldPrioritizeMutable = true
}
- // If no recent media, continue with smartspace update
+ // Only proceed with the Smartspace update if the recommendation is not empty.
if (isMediaRecommendationEmpty(data)) {
Log.d(TAG, "Empty media recommendations. Skip showing the card")
return
}
-
- // Proceed only if the Smartspace recommendation is not empty.
- listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data) }
+ listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
}
override fun onMediaDataRemoved(key: String) {
@@ -158,7 +161,6 @@
it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData)
}
}
- return
}
listeners.forEach { it.onSmartspaceMediaDataRemoved(key) }
@@ -230,4 +232,26 @@
val mediaRecommendationList: List<SmartspaceAction> = data.getIconGrid()
return mediaRecommendationList == null || mediaRecommendationList.isEmpty()
}
+
+ /**
+ * Return the time since last active for the most-recent media.
+ *
+ * @param sortedEntries userEntries sorted from the earliest to the most-recent.
+ *
+ * @return The duration in milliseconds from the most-recent media's last active timestamp to
+ * the present. MAX_VALUE will be returned if there is no media.
+ */
+ private fun timeSinceActiveForMostRecentMedia(
+ sortedEntries: SortedMap<String, MediaData>
+ ): Long {
+ if (sortedEntries.isEmpty()) {
+ return Long.MAX_VALUE
+ }
+
+ val now = systemClock.elapsedRealtime()
+ val lastActiveKey = sortedEntries.lastKey() // most recently active
+ return sortedEntries.get(lastActiveKey)?.let {
+ now - it.lastActive
+ } ?: Long.MAX_VALUE
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index a45bd33..ffcec29 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -817,8 +817,17 @@
*/
fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {}
- /** Called whenever there's new Smartspace media data loaded. */
- fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {}
+ /**
+ * Called whenever there's new Smartspace media data loaded.
+ *
+ * shouldPrioritize indicates the sorting priority of the Smartspace card. If true, it will
+ * be prioritized as the first card. Otherwise, it will show up as the last card as default.
+ */
+ fun onSmartspaceMediaDataLoaded(
+ key: String,
+ data: SmartspaceTarget,
+ shouldPrioritize: Boolean = false
+ ) {}
/**
* Called whenever a previously existing Media notification was removed
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 3c28f6e..73dfe5e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -26,6 +26,7 @@
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
+import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -35,6 +36,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import javax.inject.Inject
@@ -74,6 +76,7 @@
private val bypassController: KeyguardBypassController,
private val mediaCarouselController: MediaCarouselController,
private val notifLockscreenUserManager: NotificationLockscreenUserManager,
+ configurationController: ConfigurationController,
wakefulnessLifecycle: WakefulnessLifecycle,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager
) {
@@ -183,6 +186,58 @@
}
/**
+ * distance that the full shade transition takes in order for media to fully transition to the
+ * shade
+ */
+ private var distanceForFullShadeTransition = 0
+
+ /**
+ * Delay after which the media will start transitioning to the full shade on the lockscreen.
+ */
+ private var fullShadeTransitionDelay = 0
+
+ /**
+ * The amount of progress we are currently in if we're transitioning to the full shade.
+ * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+ * shade.
+ */
+ private var fullShadeTransitionProgress = 0f
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ if (bypassController.bypassEnabled) {
+ return
+ }
+ updateDesiredLocation()
+ if (value >= 0) {
+ updateTargetState()
+ applyTargetStateIfNotAnimating()
+ }
+ }
+
+ private val isTransitioningToFullShade: Boolean
+ get() = fullShadeTransitionProgress != 0f && !bypassController.bypassEnabled
+
+ /**
+ * Set the amount of pixels we have currently dragged down if we're transitioning to the full
+ * shade. 0.0f means we're not transitioning yet.
+ */
+ fun setTransitionToFullShadeAmount(value: Float) {
+ // If we're transitioning starting on the shade_locked, we don't want any delay and rather
+ // have it aligned with the rest of the animation
+ val delay = if (statusbarState == StatusBarState.KEYGUARD) {
+ fullShadeTransitionDelay
+ } else {
+ 0
+ }
+ val progress = MathUtils.saturate((value - delay) /
+ (distanceForFullShadeTransition - delay))
+ fullShadeTransitionProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(progress)
+ }
+
+ /**
* Is the shade currently collapsing from the expanded qs? If we're on the lockscreen and in qs,
* we wouldn't want to transition in that case.
*/
@@ -242,6 +297,12 @@
}
init {
+ updateConfiguration()
+ configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
+ override fun onDensityOrFontScaleChanged() {
+ updateConfiguration()
+ }
+ })
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
override fun onStatePreChange(oldState: Int, newState: Int) {
// We're updating the location before the state change happens, since we want the
@@ -273,6 +334,7 @@
} else {
updateDesiredLocation()
qsExpanded = false
+ closeGuts()
}
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
}
@@ -311,6 +373,13 @@
})
}
+ private fun updateConfiguration() {
+ distanceForFullShadeTransition = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_qs_transition_distance)
+ fullShadeTransitionDelay = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_media_transition_start_delay)
+ }
+
/**
* Register a media host and create a view can be attached to a view hierarchy
* and where the players will be placed in when the host is the currently desired state.
@@ -545,6 +614,9 @@
if (progress >= 0) {
return progress
}
+ if (isTransitioningToFullShade) {
+ return fullShadeTransitionProgress
+ }
return -1.0f
}
@@ -642,6 +714,7 @@
val location = when {
qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
+ onLockscreen && isTransitioningToFullShade -> LOCATION_QQS
onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 28e4640..7402107 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -56,7 +56,11 @@
updateViewVisibility()
}
- override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
+ override fun onSmartspaceMediaDataLoaded(
+ key: String,
+ data: SmartspaceTarget,
+ shouldPrioritize: Boolean
+ ) {
updateViewVisibility()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
index d973478..3e5e8248 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
@@ -135,7 +135,11 @@
}
}
- override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
+ override fun onSmartspaceMediaDataLoaded(
+ key: String,
+ data: SmartspaceTarget,
+ shouldPrioritize: Boolean
+ ) {
backgroundExecutor.execute {
dispatchSmartspaceMediaDataLoaded(key, data)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index c6373f5..bcef43c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -24,6 +24,7 @@
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -41,6 +42,7 @@
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.media.MediaDevice;
+import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
@@ -116,6 +118,7 @@
final View mDivider;
final View mBottomDivider;
final CheckBox mCheckBox;
+ private String mDeviceId;
MediaDeviceBaseViewHolder(View view) {
super(view);
@@ -134,8 +137,17 @@
}
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
- mTitleIcon.setImageIcon(mController.getDeviceIconCompat(device).toIcon(mContext));
- setMargin(topMargin, bottomMargin);
+ mDeviceId = device.getId();
+ ThreadUtils.postOnBackgroundThread(() -> {
+ Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
+ ThreadUtils.postOnMainThread(() -> {
+ if (!TextUtils.equals(mDeviceId, device.getId())) {
+ return;
+ }
+ mTitleIcon.setImageIcon(icon);
+ setMargin(topMargin, bottomMargin);
+ });
+ });
}
void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 4e41d75..04f5199 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -364,6 +364,11 @@
}
@Override
+ public void onHomeRotationEnabled(boolean enabled) {
+ mNavigationBarView.getRotationButtonController().setHomeRotationEnabled(enabled);
+ }
+
+ @Override
public void onOverviewShown(boolean fromHome) {
// If the overview has fixed orientation that may change display to natural rotation,
// we don't want the user rotation to be reset. So after user returns to application,
@@ -951,6 +956,7 @@
if (running) {
mNavbarOverlayController.setButtonState(/* visible */false, /* force */true);
}
+ mNavigationBarView.getRotationButtonController().setRecentsAnimationRunning(running);
}
/** Restores the appearance and the transient saved state to {@link NavigationBar}. */
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 9c3cb2e..ae9b598 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -184,6 +184,7 @@
mNavMode = mNavigationModeController.addListener(this);
mNavigationModeController.addListener(this);
mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService);
+ mIsTablet = isTablet(mContext.getResources().getConfiguration());
}
@Override
@@ -297,6 +298,10 @@
*/
public void createNavigationBars(final boolean includeDefaultDisplay,
RegisterStatusBarResult result) {
+ if (updateNavbarForTaskbar()) {
+ return;
+ }
+
Display[] displays = mDisplayManager.getDisplays();
for (Display display : displays) {
if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
index 2d0d5cd..40f908b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
@@ -16,7 +16,6 @@
package com.android.systemui.navigationbar;
-import android.annotation.ColorInt;
import android.content.Context;
import android.view.View;
@@ -46,10 +45,9 @@
}
/**
- * Initialize the controller with visibility change callback and light/dark icon color.
+ * Initialize the controller with visibility change callback.
*/
- public void init(Consumer<Boolean> visibilityChangeCallback, @ColorInt int lightIconColor,
- @ColorInt int darkIconColor) {}
+ public void init(Consumer<Boolean> visibilityChangeCallback) {}
/**
* Set whether the view can be shown.
@@ -72,11 +70,6 @@
public void unregisterListeners() {}
/**
- * Set the dark intensity for all drawables.
- */
- public void setDarkIntensity(float darkIntensity) {}
-
- /**
* Return the current view.
*/
public View getCurrentView() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index fbc7c92..b4f8c10 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -198,7 +198,6 @@
}
mView.getRotationButtonController().setDarkIntensity(darkIntensity);
- Dependency.get(NavigationBarOverlayController.class).setDarkIntensity(darkIntensity);
for (DarkIntensityListener listener : mDarkIntensityListeners) {
listener.onDarkIntensity(darkIntensity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index f82d265d..fcbd5965 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -61,7 +61,6 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
@@ -326,8 +325,7 @@
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
- mNavBarOverlayController.init(
- mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor);
+ mNavBarOverlayController.init(mNavbarOverlayVisibilityChangeCallback);
}
mConfiguration = new Configuration();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index ddf089b..649ac87 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -71,6 +71,8 @@
private final ViewRippler mViewRippler = new ViewRippler();
private RotationButton mRotationButton;
+ private boolean mIsRecentsAnimationRunning;
+ private boolean mHomeRotationEnabled;
private int mLastRotationSuggestion;
private boolean mPendingRotationSuggestion;
private boolean mHoveringRotationSuggestion;
@@ -92,7 +94,6 @@
() -> mPendingRotationSuggestion = false;
private Animator mRotateHideAnimator;
-
private final Stub mRotationWatcher = new Stub() {
@Override
public void onRotationChanged(final int rotation) throws RemoteException {
@@ -105,7 +106,7 @@
if (shouldOverrideUserLockPrefs(rotation)) {
setRotationLockedAtAngle(rotation);
}
- setRotateSuggestionButtonState(false /* visible */, true /* forced */);
+ setRotateSuggestionButtonState(false /* visible */, true /* hideImmediately */);
}
if (mRotWatcherListener != null) {
@@ -192,10 +193,14 @@
}
void setRotateSuggestionButtonState(boolean visible) {
- setRotateSuggestionButtonState(visible, false /* force */);
+ setRotateSuggestionButtonState(visible, false /* hideImmediately */);
}
- void setRotateSuggestionButtonState(final boolean visible, final boolean force) {
+ /**
+ * Change the visibility of rotate suggestion button. If {@code hideImmediately} is true,
+ * it doesn't wait until the completion of the running animation.
+ */
+ void setRotateSuggestionButtonState(final boolean visible, final boolean hideImmediately) {
// At any point the the button can become invisible because an a11y service became active.
// Similarly, a call to make the button visible may be rejected because an a11y service is
// active. Must account for this.
@@ -236,7 +241,7 @@
} else { // Hide
mViewRippler.stop(); // Prevent any pending ripples, force hide or not
- if (force) {
+ if (hideImmediately) {
// If a hide animator is running stop it and make invisible
if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
mRotateHideAnimator.pause();
@@ -263,12 +268,29 @@
}
}
+ void setRecentsAnimationRunning(boolean running) {
+ mIsRecentsAnimationRunning = running;
+ updateRotationButtonStateInOverview();
+ }
+
+ void setHomeRotationEnabled(boolean enabled) {
+ mHomeRotationEnabled = enabled;
+ updateRotationButtonStateInOverview();
+ }
+
+ private void updateRotationButtonStateInOverview() {
+ if (mIsRecentsAnimationRunning && !mHomeRotationEnabled) {
+ setRotateSuggestionButtonState(false, true /* hideImmediately */ );
+ }
+ }
+
void setDarkIntensity(float darkIntensity) {
mRotationButton.setDarkIntensity(darkIntensity);
}
void onRotationProposal(int rotation, int windowRotation, boolean isValid) {
- if (!mRotationButton.acceptRotationProposal()) {
+ if (!mRotationButton.acceptRotationProposal() || (!mHomeRotationEnabled
+ && mIsRecentsAnimationRunning)) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
index 0f66456..b55d86e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
@@ -18,7 +18,9 @@
import android.content.ContentProvider;
import android.content.ContentValues;
+import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
@@ -27,16 +29,19 @@
import android.util.Log;
import android.widget.RemoteViews;
+import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.shared.system.PeopleProviderUtils;
import javax.inject.Inject;
/** API that returns a People Tile preview. */
-public class PeopleProvider extends ContentProvider {
+public class PeopleProvider extends ContentProvider implements
+ SystemUIAppComponentFactory.ContextInitializer {
private static final String TAG = "PeopleProvider";
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
private static final String EMPTY_STRING = "";
+ private SystemUIAppComponentFactory.ContextAvailableCallback mCallback;
@Inject
PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
@@ -82,8 +87,8 @@
Log.e(TAG, "Could not initialize people widget manager");
return null;
}
- RemoteViews view =
- mPeopleSpaceWidgetManager.getPreview(shortcutId, userHandle, packageName, extras);
+ RemoteViews view = mPeopleSpaceWidgetManager.getPreview(shortcutId, userHandle, packageName,
+ extras);
if (view == null) {
if (DEBUG) Log.d(TAG, "No preview available for shortcutId: " + shortcutId);
return null;
@@ -130,5 +135,17 @@
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new IllegalArgumentException("Invalid method");
}
+
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ mCallback.onContextAvailable(context);
+ super.attachInfo(context, info);
+ }
+
+ @Override
+ public void setContextAvailableCallback(
+ SystemUIAppComponentFactory.ContextAvailableCallback callback) {
+ mCallback = callback;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 29685a4..7b5ab0d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -416,9 +416,8 @@
&& birthdayString == null;
boolean addBirthdayStatus = !hasBirthdayStatus(storedTile, context)
&& birthdayString != null;
- boolean shouldUpdate =
- storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus
- || addBirthdayStatus;
+ boolean shouldUpdate = storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus
+ || addBirthdayStatus;
if (shouldUpdate) {
if (DEBUG) Log.d(TAG, "Update " + storedTile.getUserName() + " from contacts");
manager.updateAppWidgetOptionsAndView(appWidgetId,
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 06f8a60..9fc9cad 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -41,8 +41,6 @@
import android.app.people.PeopleSpaceTile;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
@@ -57,13 +55,13 @@
import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.Pair;
+import android.view.Gravity;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -195,16 +193,10 @@
*/
private RemoteViews getViewForTile() {
if (DEBUG) Log.d(TAG, "Creating view for tile key: " + mKey.toString());
- if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()) {
- if (DEBUG) Log.d(TAG, "Create empty view: " + mTile);
- return createEmptyView();
- }
-
- boolean dndBlockingTileData = isDndBlockingTileData(mTile);
- if (dndBlockingTileData) {
- if (DEBUG) Log.d(TAG, "Create DND view: " + mTile.getNotificationPolicyState());
- // TODO: Create DND view.
- return createEmptyView();
+ if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()
+ || isDndBlockingTileData(mTile)) {
+ if (DEBUG) Log.d(TAG, "Create suppressed view: " + mTile);
+ return createSuppressedView();
}
if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
@@ -265,34 +257,27 @@
return !tile.canBypassDnd();
}
- private RemoteViews createEmptyView() {
- RemoteViews views = new RemoteViews(mContext.getPackageName(),
- R.layout.people_tile_empty_layout);
- Drawable appIcon = getAppBadge(mKey.getPackageName(), mKey.getUserId());
+ private RemoteViews createSuppressedView() {
+ RemoteViews views;
+ if (mTile.isUserQuieted()) {
+ views = new RemoteViews(mContext.getPackageName(),
+ R.layout.people_tile_work_profile_quiet_layout);
+ } else {
+ views = new RemoteViews(mContext.getPackageName(),
+ R.layout.people_tile_suppressed_layout);
+ }
+ Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon);
Bitmap appIconAsBitmap = convertDrawableToBitmap(appIcon);
- FastBitmapDrawable drawable = new FastBitmapDrawable(
- appIconAsBitmap);
+ FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap);
drawable.setIsDisabled(true);
Bitmap convertedBitmap = convertDrawableToBitmap(drawable);
- views.setImageViewBitmap(R.id.item, convertedBitmap);
+ views.setImageViewBitmap(R.id.icon, convertedBitmap);
return views;
}
- private Drawable getAppBadge(String packageName, int userId) {
- Drawable badge = null;
- try {
- final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
- packageName, PackageManager.GET_META_DATA, userId);
- badge = Utils.getBadgedIcon(mContext, appInfo);
- } catch (PackageManager.NameNotFoundException e) {
- badge = mContext.getPackageManager().getDefaultActivityIcon();
- }
- return badge;
- }
-
private void setMaxLines(RemoteViews views, boolean showSender) {
int textSize = mLayoutSize == LAYOUT_LARGE ? getSizeInDp(
- R.dimen.content_text_size_for_medium)
+ R.dimen.content_text_size_for_large)
: getSizeInDp(R.dimen.content_text_size_for_medium);
int lineHeight = getLineHeight(textSize);
int notificationContentHeight = getContentHeightForLayout(lineHeight);
@@ -422,9 +407,6 @@
views.setViewVisibility(R.id.availability, View.GONE);
}
- if (mTile.getUserName() != null) {
- views.setTextViewText(R.id.name, mTile.getUserName().toString());
- }
views.setBoolean(R.id.image, "setClipToOutline", true);
views.setImageViewBitmap(R.id.person_icon,
getPersonIconBitmap(mContext, mTile, maxAvatarSize));
@@ -537,25 +519,31 @@
statusText = getStatusTextByType(status.getActivity());
}
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
- views.setViewVisibility(R.id.messages_count, View.GONE);
- setMaxLines(views, false);
- // Secondary text color for statuses.
- views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorSecondary);
views.setTextViewText(R.id.text_content, statusText);
+ if (mLayoutSize == LAYOUT_LARGE) {
+ views.setInt(R.id.content, "setGravity", Gravity.BOTTOM);
+ }
Icon statusIcon = status.getIcon();
if (statusIcon != null) {
- // No multi-line text with status images on medium layout.
- views.setViewVisibility(R.id.text_content, View.GONE);
+ // No text content styled text on medium or large.
+ views.setViewVisibility(R.id.scrim_layout, View.VISIBLE);
+ views.setImageViewIcon(R.id.status_icon, statusIcon);
// Show 1-line subtext on large layout with status images.
if (mLayoutSize == LAYOUT_LARGE) {
- views.setViewVisibility(R.id.subtext, View.VISIBLE);
- views.setTextViewText(R.id.subtext, statusText);
+ if (DEBUG) Log.d(TAG, "Remove name for large");
+ views.setViewVisibility(R.id.name, View.GONE);
+ views.setColorAttr(R.id.text_content, "setTextColor",
+ android.R.attr.textColorPrimary);
+ } else if (mLayoutSize == LAYOUT_MEDIUM) {
+ views.setViewVisibility(R.id.text_content, View.GONE);
+ views.setTextViewText(R.id.name, statusText);
}
- views.setViewVisibility(R.id.image, View.VISIBLE);
- views.setImageViewIcon(R.id.image, statusIcon);
} else {
- views.setViewVisibility(R.id.image, View.GONE);
+ // Secondary text color for statuses without icons.
+ views.setColorAttr(R.id.text_content, "setTextColor",
+ android.R.attr.textColorSecondary);
+ setMaxLines(views, false);
}
// TODO: Set status pre-defined icons
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
@@ -741,16 +729,23 @@
views.setViewVisibility(R.id.name, View.VISIBLE);
views.setViewVisibility(R.id.text_content, View.VISIBLE);
views.setViewVisibility(R.id.subtext, View.GONE);
+ views.setViewVisibility(R.id.image, View.GONE);
+ views.setViewVisibility(R.id.scrim_layout, View.GONE);
}
if (mLayoutSize == LAYOUT_MEDIUM) {
if (DEBUG) Log.d(TAG, "Set vertical padding: " + mMediumVerticalPadding);
int horizontalPadding = (int) Math.floor(MAX_MEDIUM_PADDING * mDensity);
int verticalPadding = (int) Math.floor(mMediumVerticalPadding * mDensity);
- views.setViewPadding(R.id.item, horizontalPadding, verticalPadding, horizontalPadding,
+ views.setViewPadding(R.id.content, horizontalPadding, verticalPadding,
+ horizontalPadding,
verticalPadding);
}
views.setViewVisibility(R.id.messages_count, View.GONE);
+ if (mTile.getUserName() != null) {
+ views.setTextViewText(R.id.name, mTile.getUserName());
+ }
+
return views;
}
@@ -761,6 +756,9 @@
views.setViewVisibility(R.id.predefined_icon, View.GONE);
views.setViewVisibility(R.id.messages_count, View.GONE);
}
+ if (mTile.getUserName() != null) {
+ views.setTextViewText(R.id.name, mTile.getUserName());
+ }
String status = getLastInteractionString(mContext,
mTile.getLastInteractionTimestamp());
if (status != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
index eec69f98..1d2e747 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -28,7 +28,7 @@
appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
.toList()
.sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
- { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest)
+ { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
types = itemsList.map { it.privacyType }.distinct().sorted()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index dd1a4af..3a3f3f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -129,6 +129,12 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mNavBarInset = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
+ mQSPanelContainer.setPaddingRelative(
+ mQSPanelContainer.getPaddingStart(),
+ mQSPanelContainer.getPaddingTop(),
+ mQSPanelContainer.getPaddingEnd(),
+ mNavBarInset
+ );
return super.onApplyWindowInsets(insets);
}
@@ -138,8 +144,7 @@
// bottom and footer are inside the screen.
MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
- int availableScreenHeight = getDisplayHeight() - mNavBarInset;
- int maxQs = availableScreenHeight - layoutParams.topMargin - layoutParams.bottomMargin
+ int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
- getPaddingBottom();
int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
+ layoutParams.rightMargin;
@@ -148,10 +153,8 @@
mQSPanelContainer.measure(qsPanelWidthSpec,
MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
int width = mQSPanelContainer.getMeasuredWidth() + padding;
- int height = layoutParams.topMargin + layoutParams.bottomMargin
- + mQSPanelContainer.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(availableScreenHeight, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY));
// QSCustomizer will always be the height of the screen, but do this after
// other measuring to avoid changing the height of the QS.
mQSCustomizer.measure(widthMeasureSpec,
@@ -200,12 +203,6 @@
layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
mQSPanelContainer.setLayoutParams(layoutParams);
- mQSPanelContainer.setPaddingRelative(
- mQSPanelContainer.getPaddingStart(),
- mQSPanelContainer.getPaddingTop(),
- mQSPanelContainer.getPaddingEnd(),
- mContext.getResources().getDimensionPixelSize(R.dimen.qs_container_bottom_padding)
- );
int sideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
int padding = getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 05197e4..2d0d87d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -30,12 +30,14 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dependency;
@@ -56,7 +58,8 @@
private ViewGroup mDetailContent;
protected TextView mDetailSettingsButton;
protected TextView mDetailDoneButton;
- private QSDetailClipper mClipper;
+ @VisibleForTesting
+ QSDetailClipper mClipper;
private DetailAdapter mDetailAdapter;
private QSPanelController mQsPanelController;
@@ -153,11 +156,18 @@
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
lp.topMargin = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- lp.bottomMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_container_bottom_padding);
setLayoutParams(lp);
}
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ int bottomNavBar = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
+ MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
+ lp.bottomMargin = bottomNavBar;
+ setLayoutParams(lp);
+ return super.onApplyWindowInsets(insets);
+ }
+
public boolean isClosingDetail() {
return mClosingDetail;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 1fa9260..f6d9389 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -29,6 +29,7 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
@@ -73,6 +74,7 @@
private final View mPowerMenuLite;
private final boolean mShowPMLiteButton;
private GlobalActionsDialogLite mGlobalActionsDialog;
+ private final UiEventLogger mUiEventLogger;
private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
new UserInfoController.OnUserInfoChangedListener() {
@@ -122,6 +124,7 @@
startSettingsActivity();
}
} else if (v == mPowerMenuLite) {
+ mUiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
mGlobalActionsDialog.showOrHideDialog(false, true);
}
}
@@ -139,7 +142,7 @@
QuickQSPanelController quickQSPanelController,
TunerService tunerService, MetricsLogger metricsLogger, FalsingManager falsingManager,
@Named(PM_LITE_ENABLED) boolean showPMLiteButton,
- GlobalActionsDialogLite globalActionsDialog) {
+ GlobalActionsDialogLite globalActionsDialog, UiEventLogger uiEventLogger) {
super(view);
mUserManager = userManager;
mUserInfoController = userInfoController;
@@ -161,6 +164,7 @@
mPowerMenuLite = mView.findViewById(R.id.pm_lite);
mShowPMLiteButton = showPMLiteButton;
mGlobalActionsDialog = globalActionsDialog;
+ mUiEventLogger = uiEventLogger;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 53b4d5f..34c654c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -112,6 +112,17 @@
* otherwise.
*/
private boolean mTranslateWhileExpanding;
+ private boolean mPulseExpanding;
+
+ /**
+ * Are we currently transitioning from lockscreen to the full shade?
+ */
+ private boolean mTransitioningToFullShade;
+
+ /**
+ * Whether the next Quick settings
+ */
+ private boolean mAnimateNextQsUpdate;
@Inject
public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
@@ -265,6 +276,11 @@
}
}
+ @Override
+ public boolean isFullyCollapsed() {
+ return mLastQSExpansion == 0.0f || mLastQSExpansion == -1;
+ }
+
private void setEditLocation(View view) {
View edit = view.findViewById(android.R.id.edit);
int[] loc = edit.getLocationOnScreen();
@@ -335,14 +351,22 @@
}
@Override
- public void setShowCollapsedOnKeyguard(boolean showCollapsedOnKeyguard) {
- if (showCollapsedOnKeyguard != mShowCollapsedOnKeyguard) {
- mShowCollapsedOnKeyguard = showCollapsedOnKeyguard;
+ public void setPulseExpanding(boolean pulseExpanding) {
+ if (pulseExpanding != mPulseExpanding) {
+ mPulseExpanding = pulseExpanding;
+ updateShowCollapsedOnKeyguard();
+ }
+ }
+
+ private void updateShowCollapsedOnKeyguard() {
+ boolean showCollapsed = mPulseExpanding || mTransitioningToFullShade;
+ if (showCollapsed != mShowCollapsedOnKeyguard) {
+ mShowCollapsedOnKeyguard = showCollapsed;
updateQsState();
if (mQSAnimator != null) {
- mQSAnimator.setShowCollapsedOnKeyguard(showCollapsedOnKeyguard);
+ mQSAnimator.setShowCollapsedOnKeyguard(showCollapsed);
}
- if (!showCollapsedOnKeyguard && isKeyguardShowing()) {
+ if (!showCollapsed && isKeyguardShowing()) {
setQsExpansion(mLastQSExpansion, 0);
}
}
@@ -411,13 +435,24 @@
}
@Override
- public void setQsExpansion(float expansion, float headerTranslation) {
- if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
+ public void setTransitionToFullShadeAmount(float pxAmount, boolean animated) {
+ boolean isTransitioningToFullShade = pxAmount > 0;
+ if (isTransitioningToFullShade != mTransitioningToFullShade) {
+ mTransitioningToFullShade = isTransitioningToFullShade;
+ updateShowCollapsedOnKeyguard();
+ setQsExpansion(mLastQSExpansion, mLastHeaderTranslation);
+ }
+ }
+ @Override
+ public void setQsExpansion(float expansion, float proposedTranslation) {
+ if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + proposedTranslation);
+ float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
if (mQSAnimator != null) {
final boolean showQSOnLockscreen = expansion > 0;
final boolean showQSUnlocked = headerTranslation == 0 || !mTranslateWhileExpanding;
- mQSAnimator.startAlphaAnimation(showQSOnLockscreen || showQSUnlocked);
+ mQSAnimator.startAlphaAnimation(showQSOnLockscreen || showQSUnlocked
+ || mTransitioningToFullShade);
}
mContainer.setExpansion(expansion);
final float translationScaleY = (mTranslateWhileExpanding
@@ -542,18 +577,6 @@
}
@Override
- public void animateHeaderSlidingIn(long delay) {
- if (DEBUG) Log.d(TAG, "animateHeaderSlidingIn");
- // If the QS is already expanded we don't need to slide in the header as it's already
- // visible.
- if (!mQsExpanded && getView().getTranslationY() != 0) {
- mHeaderAnimating = true;
- mDelay = delay;
- getView().getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
- }
- }
-
- @Override
public void animateHeaderSlidingOut() {
if (DEBUG) Log.d(TAG, "animateHeaderSlidingOut");
if (getView().getY() == -mHeader.getHeight()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 73a6b34..81b5318 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -23,7 +23,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.MathUtils;
+import android.util.FeatureFlagUtils;
import android.util.Pair;
import android.view.DisplayCutout;
import android.view.View;
@@ -36,9 +36,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.qs.QSDetail.Callback;
-import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.StatusIconContainer;
@@ -67,7 +65,11 @@
private View mQSCarriers;
private Clock mClockView;
- private Space mSpace;
+ private Space mDatePrivacySeparator;
+ private View mClockIconsSeparator;
+ private boolean mShowClockIconsSeparator;
+ private ViewGroup mRightLayout;
+
private BatteryMeterView mBatteryRemainingIcon;
private StatusIconContainer mIconContainer;
private View mPrivacyChip;
@@ -80,19 +82,27 @@
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
- private float mClockIconsAlpha = 1.0f;
+ private float mViewAlpha = 1.0f;
private float mKeyguardExpansionFraction;
private int mTextColorPrimary = Color.TRANSPARENT;
private int mTopViewMeasureHeight;
private final String mMobileSlotName;
+ private final String mNoCallingSlotName;
private final String mCallStrengthSlotName;
+ private final boolean mProviderModel;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
- mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_no_calling);
+ mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile);
+ mNoCallingSlotName = context.getString(com.android.internal.R.string.status_bar_no_calling);
mCallStrengthSlotName =
context.getString(com.android.internal.R.string.status_bar_call_strength);
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ mProviderModel = true;
+ } else {
+ mProviderModel = false;
+ }
}
/**
@@ -117,9 +127,11 @@
mPrivacyChip = findViewById(R.id.privacy_chip);
mDateView = findViewById(R.id.date);
mSecurityHeaderView = findViewById(R.id.header_text_container);
+ mClockIconsSeparator = findViewById(R.id.separator);
+ mRightLayout = findViewById(R.id.rightLayout);
mClockView = findViewById(R.id.clock);
- mSpace = findViewById(R.id.space);
+ mDatePrivacySeparator = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
@@ -230,36 +242,50 @@
}
private void updateAlphaAnimator() {
- StatusBarIconView noCallingIcon =
- ((StatusBarIconView) mIconContainer.getViewForSlot(mMobileSlotName));
- StatusBarIconView callStrengthIcon =
- ((StatusBarIconView) mIconContainer.getViewForSlot(mCallStrengthSlotName));
TouchAnimator.Builder builder = new TouchAnimator.Builder()
// The following two views have to be hidden manually, so as not to hide the
// Privacy chip in QQS
.addFloat(mDateView, "alpha", 0, 1)
.addFloat(mSecurityHeaderView, "alpha", 0, 1)
- .addFloat(mQSCarriers, "alpha", 0, 1);
- builder.setListener(new TouchAnimator.ListenerAdapter() {
- @Override
- public void onAnimationAtEnd() {
- mIconContainer.addIgnoredSlot(mMobileSlotName);
- mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
- }
+ .addFloat(mQSCarriers, "alpha", 0, 1)
+ .setListener(new TouchAnimator.ListenerAdapter() {
+ @Override
+ public void onAnimationAtEnd() {
+ // TODO(b/185580157): Remove the mProviderModel if the mobile slot can be
+ // hidden in Provider model.
+ if (mProviderModel) {
+ mIconContainer.addIgnoredSlot(mNoCallingSlotName);
+ mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
+ } else {
+ mIconContainer.addIgnoredSlot(mMobileSlotName);
+ }
+ }
- @Override
- public void onAnimationStarted() {
- mIconContainer.addIgnoredSlot(mMobileSlotName);
- mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
- }
+ @Override
+ public void onAnimationStarted() {
+ if (mProviderModel) {
+ mIconContainer.addIgnoredSlot(mNoCallingSlotName);
+ mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
+ } else {
+ mIconContainer.addIgnoredSlot(mMobileSlotName);
+ }
- @Override
- public void onAnimationAtStart() {
- super.onAnimationAtStart();
- mIconContainer.removeIgnoredSlot(mMobileSlotName);
- mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
- }
- });
+ setSeparatorVisibility(false);
+ }
+
+ @Override
+ public void onAnimationAtStart() {
+ super.onAnimationAtStart();
+ if (mProviderModel) {
+ mIconContainer.removeIgnoredSlot(mNoCallingSlotName);
+ mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
+ } else {
+ mIconContainer.removeIgnoredSlot(mMobileSlotName);
+ }
+
+ setSeparatorVisibility(mShowClockIconsSeparator);
+ }
+ });
mAlphaAnimator = builder.build();
}
@@ -337,20 +363,30 @@
cutout, cornerCutoutPadding, -1);
mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
+ LinearLayout.LayoutParams datePrivacySeparatorLayoutParams =
+ (LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
+ LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
+ (LinearLayout.LayoutParams) mClockIconsSeparator.getLayoutParams();
boolean cornerCutout = cornerCutoutPadding != null
&& (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
if (cutout != null) {
Rect topCutout = cutout.getBoundingRectTop();
if (topCutout.isEmpty() || cornerCutout) {
- lp.width = 0;
- mSpace.setVisibility(View.GONE);
+ datePrivacySeparatorLayoutParams.width = 0;
+ mDatePrivacySeparator.setVisibility(View.GONE);
+ mClockIconsSeparatorLayoutParams.width = 0;
+ setSeparatorVisibility(false);
+ mShowClockIconsSeparator = false;
} else {
- lp.width = topCutout.width();
- mSpace.setVisibility(View.VISIBLE);
+ datePrivacySeparatorLayoutParams.width = topCutout.width();
+ mDatePrivacySeparator.setVisibility(View.VISIBLE);
+ mClockIconsSeparatorLayoutParams.width = topCutout.width();
+ mShowClockIconsSeparator = true;
+ setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
}
}
- mSpace.setLayoutParams(lp);
+ mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
+ mClockIconsSeparator.setLayoutParams(mClockIconsSeparatorLayoutParams);
mCutOutPaddingLeft = padding.first;
mCutOutPaddingRight = padding.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
@@ -358,6 +394,32 @@
return super.onApplyWindowInsets(insets);
}
+ /**
+ * Sets the visibility of the separator between clock and icons.
+ *
+ * This separator is "visible" when there is a center cutout, to block that space. In that
+ * case, the clock and the layout on the right (containing the icons and the battery meter) are
+ * set to weight 1 to take the available space.
+ * @param visible whether the separator between clock and icons should be visible.
+ */
+ private void setSeparatorVisibility(boolean visible) {
+ int newVisibility = visible ? View.VISIBLE : View.GONE;
+ if (mClockIconsSeparator.getVisibility() == newVisibility) return;
+
+ mClockIconsSeparator.setVisibility(visible ? View.VISIBLE : View.GONE);
+ mQSCarriers.setVisibility(visible ? View.GONE : View.VISIBLE);
+
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mClockView.getLayoutParams();
+ lp.width = visible ? 0 : WRAP_CONTENT;
+ lp.weight = visible ? 1f : 0f;
+ mClockView.setLayoutParams(lp);
+
+ lp = (LinearLayout.LayoutParams) mRightLayout.getLayoutParams();
+ lp.width = visible ? 0 : WRAP_CONTENT;
+ lp.weight = visible ? 1f : 0f;
+ mRightLayout.setLayoutParams(lp);
+ }
+
private void updateHeadersPadding() {
setContentMargins(mDatePrivacyView, 0, 0);
setContentMargins(mClockIconsView, 0, 0);
@@ -408,35 +470,12 @@
}
/**
- * When QS is scrolling, mClockIconsAlpha should scroll away and fade out.
- *
- * For a given scroll level, this method does the following:
- * <ol>
- * <li>Determine the alpha that {@code mClockIconsView} should have when the panel is fully
- * expanded.</li>
- * <li>Set the scroll of {@code mClockIconsView} to the same of {@code QSPanel}.</li>
- * <li>Set the alpha of {@code mClockIconsView} to that determined by the expansion of
- * the panel, interpolated between 1 (no expansion) and {@code mClockIconsAlpha} (fully
- * expanded), matching the animator.</li>
- * </ol>
+ * Scroll the headers away.
*
* @param scrollY the scroll of the QSPanel container
*/
public void setExpandedScrollAmount(int scrollY) {
- // The scrolling of the expanded qs has changed. Since the header text isn't part of it,
- // but would overlap content, we're fading it out.
- float newAlpha = 1.0f;
- if (mClockIconsView.getHeight() > 0) {
- newAlpha = MathUtils.map(0, mClockIconsView.getHeight() / 2.0f, 1.0f, 0.0f,
- scrollY);
- newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha);
- }
mClockIconsView.setScrollY(scrollY);
- if (newAlpha != mClockIconsAlpha) {
- mClockIconsAlpha = newAlpha;
- mClockIconsView.setAlpha(MathUtils.lerp(1.0f, mClockIconsAlpha,
- mKeyguardExpansionFraction));
- updateAlphaAnimator();
- }
+ mDatePrivacyView.setScrollY(scrollY);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
index 7055760..2bac298 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
@@ -45,13 +45,15 @@
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
if (ignoreLastView && childCount > 0) {
val lastView = getChildAt(childCount - 1)
- val lp = lastView.layoutParams as MarginLayoutParams
- if (orientation == VERTICAL) {
- val height = lastView.measuredHeight + lp.bottomMargin + lp.topMargin
- setMeasuredDimension(measuredWidth, measuredHeight - height)
- } else {
- val width = lastView.measuredWidth + lp.leftMargin + lp.rightMargin
- setMeasuredDimension(measuredWidth - width, measuredHeight)
+ if (lastView.visibility != GONE) {
+ val lp = lastView.layoutParams as MarginLayoutParams
+ if (orientation == VERTICAL) {
+ val height = lastView.measuredHeight + lp.bottomMargin + lp.topMargin
+ setMeasuredDimension(measuredWidth, measuredHeight - height)
+ } else {
+ val width = lastView.measuredWidth + lp.leftMargin + lp.rightMargin
+ setMeasuredDimension(measuredWidth - width, measuredHeight)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 8280f98..2e771d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -244,11 +244,13 @@
public static int getIconColorForState(Context context, int state) {
switch (state) {
case Tile.STATE_UNAVAILABLE:
- return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorTertiary);
+ return Utils.applyAlpha(QSTileViewImpl.UNAVAILABLE_ALPHA,
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary));
case Tile.STATE_INACTIVE:
return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
case Tile.STATE_ACTIVE:
- return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
+ return Utils.getColorAttrDefaultColor(context,
+ android.R.attr.textColorPrimaryInverse);
default:
Log.e("QSIconView", "Invalid state " + state);
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 2d777a5..cd76b4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tileimpl
+import android.animation.ArgbEvaluator
+import android.animation.PropertyValuesHolder
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.ColorStateList
@@ -43,6 +45,7 @@
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTile.BooleanState
import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
import java.util.Objects
private const val TAG = "QSTileViewImpl"
@@ -54,6 +57,11 @@
companion object {
private const val INVALID = -1
+ private const val BACKGROUND_NAME = "background"
+ private const val LABEL_NAME = "label"
+ private const val SECONDARY_LABEL_NAME = "secondaryLabel"
+ private const val CHEVRON_NAME = "chevron"
+ const val UNAVAILABLE_ALPHA = 0.3f
}
override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
@@ -61,15 +69,20 @@
private val colorActive = Utils.getColorAttrDefaultColor(context,
com.android.internal.R.attr.colorAccentPrimary)
private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.offStateColor)
- private val colorUnavailable =
- Utils.getColorAttrDefaultColor(context, android.R.attr.colorBackground)
+ private val colorUnavailable = Utils.applyAlpha(UNAVAILABLE_ALPHA, colorInactive)
private val colorLabelActive =
Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse)
private val colorLabelInactive =
Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
- private val colorLabelUnavailable =
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorTertiary)
+ private val colorLabelUnavailable = Utils.applyAlpha(UNAVAILABLE_ALPHA, colorLabelInactive)
+
+ private val colorSecondaryLabelActive =
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondaryInverse)
+ private val colorSecondaryLabelInactive =
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary)
+ private val colorSecondaryLabelUnavailable =
+ Utils.applyAlpha(UNAVAILABLE_ALPHA, colorSecondaryLabelInactive)
private lateinit var label: TextView
protected lateinit var secondaryLabel: TextView
@@ -83,9 +96,19 @@
private lateinit var ripple: RippleDrawable
private lateinit var colorBackgroundDrawable: Drawable
private var paintColor: Int = 0
- private var paintAnimator: ValueAnimator? = null
- private var labelAnimator: ValueAnimator? = null
- private var secondaryLabelAnimator: ValueAnimator? = null
+ private val singleAnimator: ValueAnimator = ValueAnimator().apply {
+ setDuration(QS_ANIM_LENGTH)
+ addUpdateListener { animation ->
+ setAllColors(
+ // These casts will throw an exception if some property is missing. We should
+ // always have all properties.
+ animation.getAnimatedValue(BACKGROUND_NAME) as Int,
+ animation.getAnimatedValue(LABEL_NAME) as Int,
+ animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int,
+ animation.getAnimatedValue(CHEVRON_NAME) as Int
+ )
+ }
+ }
private var accessibilityClass: String? = null
private var stateDescriptionDeltas: CharSequence? = null
@@ -104,8 +127,7 @@
clipToPadding = false
isFocusable = true
background = createTileBackground()
- paintColor = getCircleColor(QSTile.State.DEFAULT_STATE)
- colorBackgroundDrawable.setTint(paintColor)
+ setColor(getBackgroundColorForState(QSTile.State.DEFAULT_STATE))
val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
@@ -166,8 +188,8 @@
labelContainer.ignoreLastView = true
secondaryLabel.alpha = 0f
}
- label.setTextColor(getLabelColor(QSTile.State.DEFAULT_STATE))
- secondaryLabel.setTextColor(getSecondaryLabelColor(QSTile.State.DEFAULT_STATE))
+ setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE))
+ setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE))
addView(labelContainer)
}
@@ -176,6 +198,7 @@
.inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup
customDrawableView = sideView.requireViewById(R.id.customDrawable)
chevronView = sideView.requireViewById(R.id.chevron)
+ setChevronColor(getChevronColorForState(QSTile.State.DEFAULT_STATE))
addView(sideView)
}
@@ -322,19 +345,6 @@
icon.setIcon(state, allowAnimations)
contentDescription = state.contentDescription
- // Background color animation
- val newColor = getCircleColor(state.state)
- if (allowAnimations) {
- animateBackground(newColor)
- } else {
- clearBackgroundAnimator()
- colorBackgroundDrawable.setTintList(ColorStateList.valueOf(newColor)).also {
- paintColor = newColor
- }
- paintColor = newColor
- }
- //
-
// State handling and description
val stateDescription = StringBuilder()
val stateText = getStateText(state)
@@ -383,23 +393,80 @@
}
}
- if (allowAnimations) {
- animateLabelColor(getLabelColor(state.state))
- animateSecondaryLabelColor(getSecondaryLabelColor(state.state))
- } else {
- label.setTextColor(getLabelColor(state.state))
- secondaryLabel.setTextColor(getSecondaryLabelColor(state.state))
+ // Colors
+ if (state.state != lastState) {
+ singleAnimator.cancel()
+ if (allowAnimations) {
+ singleAnimator.setValues(
+ colorValuesHolder(
+ BACKGROUND_NAME,
+ paintColor,
+ getBackgroundColorForState(state.state)
+ ),
+ colorValuesHolder(
+ LABEL_NAME,
+ label.currentTextColor,
+ getLabelColorForState(state.state)
+ ),
+ colorValuesHolder(
+ SECONDARY_LABEL_NAME,
+ secondaryLabel.currentTextColor,
+ getSecondaryLabelColorForState(state.state)
+ ),
+ colorValuesHolder(
+ CHEVRON_NAME,
+ chevronView.imageTintList?.defaultColor ?: 0,
+ getChevronColorForState(state.state)
+ )
+ )
+ singleAnimator.start()
+ } else {
+ setAllColors(
+ getBackgroundColorForState(state.state),
+ getLabelColorForState(state.state),
+ getSecondaryLabelColorForState(state.state),
+ getChevronColorForState(state.state)
+ )
+ }
}
// Right side icon
loadSideViewDrawableIfNecessary(state)
- chevronView.imageTintList = ColorStateList.valueOf(getSecondaryLabelColor(state.state))
label.isEnabled = !state.disabledByPolicy
lastState = state.state
}
+ private fun setAllColors(
+ backgroundColor: Int,
+ labelColor: Int,
+ secondaryLabelColor: Int,
+ chevronColor: Int
+ ) {
+ setColor(backgroundColor)
+ setLabelColor(labelColor)
+ setSecondaryLabelColor(secondaryLabelColor)
+ setChevronColor(chevronColor)
+ }
+
+ private fun setColor(color: Int) {
+ colorBackgroundDrawable.setTint(color)
+ paintColor = color
+ }
+
+ private fun setLabelColor(color: Int) {
+ label.setTextColor(color)
+ }
+
+ private fun setSecondaryLabelColor(color: Int) {
+ secondaryLabel.setTextColor(color)
+ }
+
+ private fun setChevronColor(color: Int) {
+ chevronView.imageTintList = ColorStateList.valueOf(color)
+ }
+
private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
if (state.sideViewCustomDrawable != null) {
customDrawableView.setImageDrawable(state.sideViewCustomDrawable)
@@ -446,63 +513,7 @@
return locInScreen.get(1) >= -height
}
- private fun animateBackground(newBackgroundColor: Int) {
- if (newBackgroundColor != paintColor) {
- clearBackgroundAnimator()
- paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener { animation: ValueAnimator ->
- val c = animation.animatedValue as Int
- colorBackgroundDrawable.setTintList(ColorStateList.valueOf(c)).also {
- paintColor = c
- }
- }
- start()
- }
- }
- }
-
- private fun animateLabelColor(color: Int) {
- val currentColor = label.textColors.defaultColor
- if (currentColor != color) {
- clearLabelAnimator()
- labelAnimator = ValueAnimator.ofArgb(currentColor, color)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener {
- label.setTextColor(it.animatedValue as Int)
- }
- start()
- }
- }
- }
-
- private fun animateSecondaryLabelColor(color: Int) {
- val currentColor = secondaryLabel.textColors.defaultColor
- if (currentColor != color) {
- clearSecondaryLabelAnimator()
- secondaryLabelAnimator = ValueAnimator.ofArgb(currentColor, color)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener {
- secondaryLabel.setTextColor(it.animatedValue as Int)
- }
- start()
- }
- }
- }
-
- private fun clearBackgroundAnimator() {
- paintAnimator?.cancel()?.also { paintAnimator = null }
- }
-
- private fun clearLabelAnimator() {
- labelAnimator?.cancel()?.also { labelAnimator = null }
- }
-
- private fun clearSecondaryLabelAnimator() {
- secondaryLabelAnimator?.cancel()?.also { secondaryLabelAnimator = null }
- }
-
- private fun getCircleColor(state: Int): Int {
+ private fun getBackgroundColorForState(state: Int): Int {
return when (state) {
Tile.STATE_ACTIVE -> colorActive
Tile.STATE_INACTIVE -> colorInactive
@@ -514,7 +525,7 @@
}
}
- private fun getLabelColor(state: Int): Int {
+ private fun getLabelColorForState(state: Int): Int {
return when (state) {
Tile.STATE_ACTIVE -> colorLabelActive
Tile.STATE_INACTIVE -> colorLabelInactive
@@ -526,14 +537,23 @@
}
}
- private fun getSecondaryLabelColor(state: Int): Int {
+ private fun getSecondaryLabelColorForState(state: Int): Int {
return when (state) {
- Tile.STATE_ACTIVE -> colorLabelActive
- Tile.STATE_INACTIVE, Tile.STATE_UNAVAILABLE -> colorLabelUnavailable
+ Tile.STATE_ACTIVE -> colorSecondaryLabelActive
+ Tile.STATE_INACTIVE -> colorSecondaryLabelInactive
+ Tile.STATE_UNAVAILABLE -> colorSecondaryLabelUnavailable
else -> {
Log.e(TAG, "Invalid state $state")
0
}
}
}
+
+ private fun getChevronColorForState(state: Int): Int = getSecondaryLabelColorForState(state)
+}
+
+private fun colorValuesHolder(name: String, vararg values: Int): PropertyValuesHolder {
+ return PropertyValuesHolder.ofInt(name, *values).apply {
+ setEvaluator(ArgbEvaluator.getInstance())
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 71f42d8..11b0e01 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -40,6 +40,7 @@
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.KeyguardStateController
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
@@ -52,7 +53,8 @@
statusBarStateController: StatusBarStateController,
activityStarter: ActivityStarter,
qsLogger: QSLogger,
- private val controlsComponent: ControlsComponent
+ private val controlsComponent: ControlsComponent,
+ private val keyguardStateController: KeyguardStateController
) : QSTileImpl<QSTile.State>(
host,
backgroundLooper,
@@ -101,11 +103,15 @@
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(ControlsUiController.EXTRA_ANIMATE, true)
}
-
- val animationController = view?.let {
- ActivityLaunchAnimator.Controller.fromView(it)
+ if (keyguardStateController.isUnlocked()) {
+ val animationController = view?.let {
+ ActivityLaunchAnimator.Controller.fromView(it)
+ }
+ mActivityStarter.startActivity(i, true /* dismissShade */, animationController)
+ } else {
+ mHost.collapsePanels()
+ mContext.startActivity(i)
}
- mActivityStarter.startActivity(i, true /* dismissShade */, animationController)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 85b835a..e4679255 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -152,7 +152,8 @@
@Override
protected void handleUpdateState(State state, Object arg) {
- state.label = mLabel;
+ CharSequence label = mQuickAccessWalletClient.getServiceLabel();
+ state.label = label == null ? mLabel : label;
state.contentDescription = state.label;
state.icon = ResourceIcon.get(R.drawable.ic_wallet_lockscreen);
boolean isDeviceLocked = !mKeyguardStateController.isUnlocked();
@@ -197,7 +198,8 @@
@Override
public CharSequence getTileLabel() {
- return mLabel;
+ CharSequence label = mQuickAccessWalletClient.getServiceLabel();
+ return label == null ? mLabel : label;
}
private void queryWalletCards() {
@@ -227,7 +229,9 @@
}
int selectedIndex = response.getSelectedIndex();
if (selectedIndex >= cards.size()) {
- Log.d(TAG, "Selected card index out of bounds.");
+ Log.w(TAG, "Error retrieving cards: Invalid selected card index.");
+ mSelectedCard = null;
+ mCardViewDrawable = null;
return;
}
mSelectedCard = cards.get(selectedIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index a4148ee..32a6c6c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -142,7 +142,7 @@
mHost.getUserContext().startActivity(intent);
return false;
};
- mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false);
+ mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false, false);
}
private void cancelCountdown() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 7bde64b..8df8c63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -125,25 +125,14 @@
} else {
state.slash.isSlashed = true;
}
- state.label = mContext.getString(R.string.quick_settings_work_mode_label);
+ state.label = getTileLabel();
state.contentDescription = state.label;
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
- state.secondaryLabel = state.value ? "" :
- mContext.getString(R.string.quick_settings_work_mode_paused);
}
@Override
public int getMetricsCategory() {
return MetricsEvent.QS_WORKMODE;
}
-
- @Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return mContext.getString(R.string.accessibility_quick_settings_work_mode_changed_on);
- } else {
- return mContext.getString(R.string.accessibility_quick_settings_work_mode_changed_off);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 63adbd0..20c3e81 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -259,6 +259,21 @@
}
}
+ @Override
+ public void setHomeRotationEnabled(boolean enabled) {
+ if (!verifyCaller("setHomeRotationEnabled")) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mHandler.post(() -> {
+ mHandler.post(() -> notifyHomeRotationEnabled(enabled));
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean sendEvent(int action, int code) {
long when = SystemClock.uptimeMillis();
final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
@@ -847,6 +862,12 @@
}
}
+ private void notifyHomeRotationEnabled(boolean enabled) {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onHomeRotationEnabled(enabled);
+ }
+ }
+
private void notifyConnectionChanged() {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
@@ -994,6 +1015,7 @@
default void onToggleRecentApps() {}
/** Notify changes in the nav bar button alpha */
default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {}
+ default void onHomeRotationEnabled(boolean enabled) {}
default void onSystemUiStateChanged(int sysuiStateFlags) {}
default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
default void onAssistantGestureCompletion(float velocity) {}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 4300d37..2f0bbdb8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -189,7 +189,7 @@
// Remove notification
mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
return false;
- }, false);
+ }, false, false);
// Close quick shade
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index fa28754..30c9b44 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -22,7 +22,6 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
-import android.graphics.Matrix;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
@@ -35,7 +34,6 @@
import android.util.Log;
import android.view.ScrollCaptureResponse;
import android.view.View;
-import android.view.Window;
import android.widget.ImageView;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -68,8 +66,6 @@
public static final String EXTRA_CAPTURE_RESPONSE = "capture-response";
private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
- private static final boolean USE_SHARED_ELEMENT = false;
-
private final UiEventLogger mUiEventLogger;
private final Executor mUiExecutor;
private final Executor mBackgroundExecutor;
@@ -89,6 +85,7 @@
private ListenableFuture<File> mCacheSaveFuture;
private ListenableFuture<ImageLoader.Result> mCacheLoadFuture;
+ private Bitmap mOutputBitmap;
private LongScreenshot mLongScreenshot;
private boolean mTransitionStarted;
@@ -114,7 +111,7 @@
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
super.onCreate(savedInstanceState);
- getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
+
setContentView(R.layout.long_screenshot);
mPreview = requireViewById(R.id.preview);
@@ -173,7 +170,6 @@
}
}, mUiExecutor);
mCacheLoadFuture = null;
- return;
} else {
LongScreenshot longScreenshot = mLongScreenshotHolder.takeLongScreenshot();
if (longScreenshot != null) {
@@ -189,7 +185,6 @@
Log.d(TAG, "onLongScreenshotReceived(longScreenshot=" + longScreenshot + ")");
mLongScreenshot = longScreenshot;
mPreview.setImageDrawable(mLongScreenshot.getDrawable());
- mTransitionView.setImageDrawable(mLongScreenshot.getDrawable());
updateImageDimensions();
mCropView.setVisibility(View.VISIBLE);
mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
@@ -310,19 +305,15 @@
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- if (USE_SHARED_ELEMENT) {
- updateImageDimensions();
- mTransitionView.setVisibility(View.VISIBLE);
- // TODO: listen for transition completing instead of finishing onStop
- mTransitionStarted = true;
- startActivity(intent,
- ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
- } else {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- startActivityAsUser(intent, UserHandle.CURRENT);
- finishAndRemoveTask();
- }
+ mTransitionView.setImageBitmap(mOutputBitmap);
+ mTransitionView.setVisibility(View.VISIBLE);
+ mTransitionView.setTransitionName(
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
+ // TODO: listen for transition completing instead of finishing onStop
+ mTransitionStarted = true;
+ startActivity(intent,
+ ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
}
private void doShare(Uri uri) {
@@ -368,9 +359,11 @@
return;
}
- Bitmap output = renderBitmap(mPreview.getDrawable(), bounds);
+ updateImageDimensions();
+
+ mOutputBitmap = renderBitmap(drawable, bounds);
ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
- mBackgroundExecutor, UUID.randomUUID(), output, ZonedDateTime.now());
+ mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now());
exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor);
}
@@ -419,7 +412,6 @@
// The image width and height on screen
int imageHeight = previewHeight;
int imageWidth = previewWidth;
- float scale;
if (imageRatio > viewRatio) {
// Image is full width and height is constrained, compute extra padding to inform
// CropView
@@ -428,15 +420,13 @@
mCropView.setExtraPadding(extraPadding + mPreview.getPaddingTop(),
extraPadding + mPreview.getPaddingBottom());
imageTop += (previewHeight - imageHeight) / 2;
- scale = imageHeight / bounds.height();
mCropView.setExtraPadding(extraPadding, extraPadding);
mCropView.setImageWidth(previewWidth);
} else {
imageWidth = (int) (previewWidth * imageRatio / viewRatio);
imageLeft += (previewWidth - imageWidth) / 2;
- scale = imageWidth / (float) bounds.width();
// Image is full height
- mCropView.setExtraPadding(mPreview.getPaddingTop(), mPreview.getPaddingBottom());
+ mCropView.setExtraPadding(mPreview.getPaddingTop(), mPreview.getPaddingBottom());
mCropView.setImageWidth((int) (previewHeight * imageRatio));
}
@@ -449,10 +439,5 @@
params.width = boundaries.width();
params.height = boundaries.height();
mTransitionView.setLayoutParams(params);
-
- Matrix matrix = new Matrix();
- matrix.postScale(scale, scale, 0, 0);
- matrix.postTranslate(-boundaries.left, -boundaries.top);
- mTransitionView.setImageMatrix(matrix);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 1a5c9ee..7530681 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -294,7 +294,6 @@
/**
* Make bottom edge concave so overlap between layers is not visible for alphas between 0 and 1
- * @return height of concavity
*/
public void enableBottomEdgeConcave(boolean clipScrim) {
if (mDrawable instanceof ScrimDrawable) {
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 7c0496b..06c1c6f 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -181,7 +181,7 @@
keyguardDismissUtil.executeWhenUnlocked({
disableSensorPrivacy()
false
- }, false)
+ }, false, false)
} else {
disableSensorPrivacy()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
deleted file mode 100644
index 0378123..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar;
-
-import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import com.android.systemui.ExpandHelper;
-import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
-import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-
-/**
- * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand
- * the notification where the drag started.
- */
-public class DragDownHelper implements Gefingerpoken {
-
- private static final float RUBBERBAND_FACTOR_EXPANDABLE = 0.5f;
- private static final float RUBBERBAND_FACTOR_STATIC = 0.15f;
-
- private static final int SPRING_BACK_ANIMATION_LENGTH_MS = 375;
-
- private int mMinDragDistance;
- private final FalsingManager mFalsingManager;
- private ExpandHelper.Callback mCallback;
- private float mInitialTouchX;
- private float mInitialTouchY;
- private boolean mDraggingDown;
- private final float mTouchSlop;
- private final float mSlopMultiplier;
- private DragDownCallback mDragDownCallback;
- private View mHost;
- private final int[] mTemp2 = new int[2];
- private boolean mDraggedFarEnough;
- private ExpandableView mStartingChild;
- private float mLastHeight;
- private FalsingCollector mFalsingCollector;
-
- public DragDownHelper(Context context, View host, ExpandHelper.Callback callback,
- DragDownCallback dragDownCallback, FalsingManager falsingManager,
- FalsingCollector falsingCollector) {
- mMinDragDistance = context.getResources().getDimensionPixelSize(
- R.dimen.keyguard_drag_down_min_distance);
- mFalsingManager = falsingManager;
- final ViewConfiguration configuration = ViewConfiguration.get(context);
- mTouchSlop = configuration.getScaledTouchSlop();
- mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
- mCallback = callback;
- mDragDownCallback = dragDownCallback;
- mHost = host;
- mFalsingCollector = falsingCollector;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- final float x = event.getX();
- final float y = event.getY();
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mDraggedFarEnough = false;
- mDraggingDown = false;
- mStartingChild = null;
- mInitialTouchY = y;
- mInitialTouchX = x;
- break;
-
- case MotionEvent.ACTION_MOVE:
- final float h = y - mInitialTouchY;
- // Adjust the touch slop if another gesture may be being performed.
- final float touchSlop =
- event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
- ? mTouchSlop * mSlopMultiplier
- : mTouchSlop;
- if (h > touchSlop && h > Math.abs(x - mInitialTouchX)) {
- mFalsingCollector.onNotificationStartDraggingDown();
- mDraggingDown = true;
- captureStartingChild(mInitialTouchX, mInitialTouchY);
- mInitialTouchY = y;
- mInitialTouchX = x;
- mDragDownCallback.onTouchSlopExceeded();
- return mStartingChild != null || mDragDownCallback.isDragDownAnywhereEnabled();
- }
- break;
- }
- return false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (!mDraggingDown) {
- return false;
- }
- final float x = event.getX();
- final float y = event.getY();
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_MOVE:
- mLastHeight = y - mInitialTouchY;
- captureStartingChild(mInitialTouchX, mInitialTouchY);
- if (mStartingChild != null) {
- handleExpansion(mLastHeight, mStartingChild);
- } else {
- mDragDownCallback.setEmptyDragAmount(mLastHeight);
- }
- if (mLastHeight > mMinDragDistance) {
- if (!mDraggedFarEnough) {
- mDraggedFarEnough = true;
- mDragDownCallback.onCrossedThreshold(true);
- }
- } else {
- if (mDraggedFarEnough) {
- mDraggedFarEnough = false;
- mDragDownCallback.onCrossedThreshold(false);
- }
- }
- return true;
- case MotionEvent.ACTION_UP:
- if (!mFalsingManager.isUnlockingDisabled() && mDragDownCallback.canDragDown()
- && !isFalseTouch()) {
- mDragDownCallback.onDraggedDown(mStartingChild, (int) (y - mInitialTouchY));
- if (mStartingChild == null) {
- cancelExpansion();
- } else {
- mCallback.setUserLockedChild(mStartingChild, false);
- mStartingChild = null;
- }
- mDraggingDown = false;
- } else {
- stopDragging();
- return false;
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- stopDragging();
- return false;
- }
- return false;
- }
-
- private boolean isFalseTouch() {
- if (!mDragDownCallback.isFalsingCheckNeeded()) {
- return false;
- }
- return mFalsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) || !mDraggedFarEnough;
- }
-
- private void captureStartingChild(float x, float y) {
- if (mStartingChild == null) {
- mStartingChild = findView(x, y);
- if (mStartingChild != null) {
- if (mDragDownCallback.isDragDownEnabledForView(mStartingChild)) {
- mCallback.setUserLockedChild(mStartingChild, true);
- } else {
- mStartingChild = null;
- }
- }
- }
- }
-
- private void handleExpansion(float heightDelta, ExpandableView child) {
- if (heightDelta < 0) {
- heightDelta = 0;
- }
- boolean expandable = child.isContentExpandable();
- float rubberbandFactor = expandable
- ? RUBBERBAND_FACTOR_EXPANDABLE
- : RUBBERBAND_FACTOR_STATIC;
- float rubberband = heightDelta * rubberbandFactor;
- if (expandable
- && (rubberband + child.getCollapsedHeight()) > child.getMaxContentHeight()) {
- float overshoot =
- (rubberband + child.getCollapsedHeight()) - child.getMaxContentHeight();
- overshoot *= (1 - RUBBERBAND_FACTOR_STATIC);
- rubberband -= overshoot;
- }
- child.setActualHeight((int) (child.getCollapsedHeight() + rubberband));
- }
-
- private void cancelExpansion(final ExpandableView child) {
- if (child.getActualHeight() == child.getCollapsedHeight()) {
- mCallback.setUserLockedChild(child, false);
- return;
- }
- ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight",
- child.getActualHeight(), child.getCollapsedHeight());
- anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mCallback.setUserLockedChild(child, false);
- }
- });
- anim.start();
- }
-
- private void cancelExpansion() {
- ValueAnimator anim = ValueAnimator.ofFloat(mLastHeight, 0);
- anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
- anim.addUpdateListener(animation -> {
- mDragDownCallback.setEmptyDragAmount((Float) animation.getAnimatedValue());
- });
- anim.start();
- }
-
- private void stopDragging() {
- mFalsingCollector.onNotificationStopDraggingDown();
- if (mStartingChild != null) {
- cancelExpansion(mStartingChild);
- mStartingChild = null;
- } else {
- cancelExpansion();
- }
- mDraggingDown = false;
- mDragDownCallback.onDragDownReset();
- }
-
- private ExpandableView findView(float x, float y) {
- mHost.getLocationOnScreen(mTemp2);
- x += mTemp2[0];
- y += mTemp2[1];
- return mCallback.getChildAtRawPosition(x, y);
- }
-
- public boolean isDraggingDown() {
- return mDraggingDown;
- }
-
- public boolean isDragDownEnabled() {
- return mDragDownCallback.isDragDownEnabledForView(null);
- }
-
- public interface DragDownCallback {
-
- /**
- * @return true if the interaction is accepted, false if it should be cancelled
- */
- boolean canDragDown();
-
- /** Call when a view has been dragged. */
- void onDraggedDown(View startingChild, int dragLengthY);
- void onDragDownReset();
-
- /**
- * The user has dragged either above or below the threshold
- * @param above whether he dragged above it
- */
- void onCrossedThreshold(boolean above);
- void onTouchSlopExceeded();
- void setEmptyDragAmount(float amount);
- boolean isFalsingCheckNeeded();
-
- /**
- * Is dragging down enabled on a given view
- * @param view The view to check or {@code null} to check if it's enabled at all
- */
- boolean isDragDownEnabledForView(ExpandableView view);
-
- /**
- * @return if drag down is enabled anywhere, not just on selected views.
- */
- boolean isDragDownAnywhereEnabled();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index d96e1ba..ac6d9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -92,4 +92,8 @@
public boolean isSmartspaceEnabled() {
return mFlagReader.isEnabled(R.bool.flag_smartspace);
}
+
+ public boolean isSmartspaceDedupingEnabled() {
+ return isSmartspaceEnabled() && mFlagReader.isEnabled(R.bool.flag_smartspace_deduping);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 5daee6c..91a0e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -33,7 +33,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -147,6 +146,7 @@
private long mChargingTimeRemaining;
private String mMessageToShowOnScreenOn;
protected int mLockScreenMode;
+ private boolean mInited;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -174,7 +174,9 @@
IBatteryStats iBatteryStats,
UserManager userManager,
@Main DelayableExecutor executor,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager,
+ LockPatternUtils lockPatternUtils,
+ IActivityManager iActivityManager) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mDevicePolicyManager = devicePolicyManager;
@@ -182,17 +184,26 @@
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDockManager = dockManager;
- mDockManager.addAlignmentStateListener(
- alignState -> mHandler.post(() -> handleAlignStateChanged(alignState)));
mWakeLock = new SettableWakeLock(
wakeLockBuilder.setTag("Doze:KeyguardIndication").build(), TAG);
mBatteryInfo = iBatteryStats;
mUserManager = userManager;
mExecutor = executor;
- mLockPatternUtils = new LockPatternUtils(context);
- mIActivityManager = ActivityManager.getService();
+ mLockPatternUtils = lockPatternUtils;
+ mIActivityManager = iActivityManager;
mFalsingManager = falsingManager;
+ }
+
+ /** Call this after construction to finish setting up the instance. */
+ public void init() {
+ if (mInited) {
+ return;
+ }
+ mInited = true;
+
+ mDockManager.addAlignmentStateListener(
+ alignState -> mHandler.post(() -> handleAlignStateChanged(alignState)));
mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback());
mKeyguardUpdateMonitor.registerCallback(mTickReceiver);
mStatusBarStateController.addCallback(mStatusBarStateListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
new file mode 100644
index 0000000..12f569c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -0,0 +1,667 @@
+package com.android.systemui.statusbar
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.Configuration
+import android.os.SystemClock
+import android.util.DisplayMetrics
+import android.util.MathUtils
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewConfiguration
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent
+import com.android.systemui.ExpandHelper
+import com.android.systemui.Gefingerpoken
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.classifier.Classifier
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.MediaHierarchyManager
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.stack.AmbientState
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.Utils
+import javax.inject.Inject
+
+private const val SPRING_BACK_ANIMATION_LENGTH_MS = 375L
+private const val RUBBERBAND_FACTOR_STATIC = 0.15f
+private const val RUBBERBAND_FACTOR_EXPANDABLE = 0.5f
+
+/**
+ * A class that controls the lockscreen to shade transition
+ */
+@SysUISingleton
+class LockscreenShadeTransitionController @Inject constructor(
+ private val statusBarStateController: SysuiStatusBarStateController,
+ private val lockscreenGestureLogger: LockscreenGestureLogger,
+ private val keyguardBypassController: KeyguardBypassController,
+ private val lockScreenUserManager: NotificationLockscreenUserManager,
+ private val falsingCollector: FalsingCollector,
+ private val ambientState: AmbientState,
+ private val displayMetrics: DisplayMetrics,
+ private val mediaHierarchyManager: MediaHierarchyManager,
+ private val scrimController: ScrimController,
+ private val featureFlags: FeatureFlags,
+ private val context: Context,
+ configurationController: ConfigurationController,
+ falsingManager: FalsingManager
+) {
+ private var useSplitShade: Boolean = false
+ private lateinit var nsslController: NotificationStackScrollLayoutController
+ lateinit var notificationPanelController: NotificationPanelViewController
+ lateinit var statusbar: StatusBar
+ lateinit var qS: QS
+
+ /**
+ * A handler that handles the next keyguard dismiss animation.
+ */
+ private var animationHandlerOnKeyguardDismiss: ((Long) -> Unit)? = null
+
+ /**
+ * The entry that was just dragged down on.
+ */
+ private var draggedDownEntry: NotificationEntry? = null
+
+ /**
+ * The current animator if any
+ */
+ @VisibleForTesting
+ internal var dragDownAnimator: ValueAnimator? = null
+
+ /**
+ * Distance that the full shade transition takes in order for scrim to fully transition to
+ * the shade (in alpha)
+ */
+ private var scrimTransitionDistance = 0
+
+ /**
+ * Distance that the full transition takes in order for us to fully transition to the shade
+ */
+ private var fullTransitionDistance = 0
+
+ /**
+ * Flag to make sure that the dragDownAmount is applied to the listeners even when in the
+ * locked down shade.
+ */
+ private var forceApplyAmount = false
+
+ /**
+ * A flag to suppress the default animation when unlocking in the locked down shade.
+ */
+ private var nextHideKeyguardNeedsNoAnimation = false
+
+ /**
+ * The touch helper responsible for the drag down animation.
+ */
+ val touchHelper = DragDownHelper(falsingManager, falsingCollector, this, context)
+
+ init {
+ updateResources()
+ configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ touchHelper.updateResources(context)
+ }
+ })
+ }
+
+ private fun updateResources() {
+ scrimTransitionDistance = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_scrim_transition_distance)
+ fullTransitionDistance = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_qs_transition_distance)
+ useSplitShade = Utils.shouldUseSplitNotificationShade(featureFlags, context.resources)
+ }
+
+ fun setStackScroller(nsslController: NotificationStackScrollLayoutController) {
+ this.nsslController = nsslController
+ touchHelper.host = nsslController.view
+ touchHelper.expandCallback = nsslController.expandHelperCallback
+ }
+
+ /**
+ * Initialize the shelf controller such that clicks on it will expand the shade
+ */
+ fun bindController(notificationShelfController: NotificationShelfController) {
+ // Bind the click listener of the shelf to go to the full shade
+ notificationShelfController.setOnClickListener {
+ if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ statusbar.wakeUpIfDozing(SystemClock.uptimeMillis(), it, "SHADE_CLICK")
+ goToLockedShade(it)
+ }
+ }
+ }
+
+ /**
+ * @return true if the interaction is accepted, false if it should be cancelled
+ */
+ internal fun canDragDown(): Boolean {
+ return (statusBarStateController.state == StatusBarState.KEYGUARD ||
+ nsslController.isInLockedDownShade()) &&
+ qS.isFullyCollapsed
+ }
+
+ /**
+ * Called by the touch helper when when a gesture has completed all the way and released.
+ */
+ internal fun onDraggedDown(startingChild: View?, dragLengthY: Int) {
+ if (canDragDown()) {
+ if (nsslController.isInLockedDownShade()) {
+ statusBarStateController.setLeaveOpenOnKeyguardHide(true)
+ statusbar.dismissKeyguardThenExecute(OnDismissAction {
+ nextHideKeyguardNeedsNoAnimation = true
+ false
+ },
+ null /* cancelRunnable */, false /* afterKeyguardGone */)
+ } else {
+ lockscreenGestureLogger.write(
+ MetricsEvent.ACTION_LS_SHADE,
+ (dragLengthY / displayMetrics.density).toInt(),
+ 0 /* velocityDp */)
+ lockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN)
+ if (!ambientState.isDozing() || startingChild != null) {
+ // go to locked shade while animating the drag down amount from its current
+ // value
+ val animationHandler = { delay: Long ->
+ if (startingChild is ExpandableNotificationRow) {
+ startingChild.onExpandedByGesture(
+ true /* drag down is always an open */)
+ }
+ notificationPanelController.animateToFullShade(delay)
+ notificationPanelController.setTransitionToFullShadeAmount(0f,
+ true /* animated */, delay)
+
+ // Let's reset ourselves, ready for the next animation
+
+ // changing to shade locked will make isInLockDownShade true, so let's
+ // override that
+ forceApplyAmount = true
+ // Reset the behavior. At this point the animation is already started
+ dragDownAmount = 0f
+ forceApplyAmount = false
+ }
+ val cancelRunnable = Runnable { setDragDownAmountAnimated(0f) }
+ goToLockedShadeInternal(startingChild, animationHandler, cancelRunnable)
+ }
+ }
+ } else {
+ setDragDownAmountAnimated(0f)
+ }
+ }
+
+ /**
+ * Called by the touch helper when the drag down was aborted and should be reset.
+ */
+ internal fun onDragDownReset() {
+ nsslController.setDimmed(true /* dimmed */, true /* animated */)
+ nsslController.resetScrollPosition()
+ nsslController.resetCheckSnoozeLeavebehind()
+ setDragDownAmountAnimated(0f)
+ }
+
+ /**
+ * The user has dragged either above or below the threshold which changes the dimmed state.
+ * @param above whether they dragged above it
+ */
+ internal fun onCrossedThreshold(above: Boolean) {
+ nsslController.setDimmed(!above /* dimmed */, true /* animate */)
+ }
+
+ /**
+ * Called by the touch helper when the drag down was started
+ */
+ internal fun onDragDownStarted() {
+ nsslController.cancelLongPress()
+ nsslController.checkSnoozeLeavebehind()
+ dragDownAnimator?.cancel()
+ }
+
+ /**
+ * Do we need a falsing check currently?
+ */
+ internal val isFalsingCheckNeeded: Boolean
+ get() = statusBarStateController.state == StatusBarState.KEYGUARD
+
+ /**
+ * Is dragging down enabled on a given view
+ * @param view The view to check or `null` to check if it's enabled at all
+ */
+ internal fun isDragDownEnabledForView(view: ExpandableView?): Boolean {
+ if (isDragDownAnywhereEnabled) {
+ return true
+ }
+ if (nsslController.isInLockedDownShade()) {
+ if (view == null) {
+ // Dragging down is allowed in general
+ return true
+ }
+ if (view is ExpandableNotificationRow) {
+ // Only drag down on sensitive views, otherwise the ExpandHelper will take this
+ return view.entry.isSensitive
+ }
+ }
+ return false
+ }
+
+ /**
+ * @return if drag down is enabled anywhere, not just on selected views.
+ */
+ internal val isDragDownAnywhereEnabled: Boolean
+ get() = (statusBarStateController.getState() == StatusBarState.KEYGUARD &&
+ !keyguardBypassController.bypassEnabled &&
+ qS.isFullyCollapsed)
+
+ /**
+ * The amount in pixels that the user has dragged down.
+ */
+ internal var dragDownAmount = 0f
+ set(value) {
+ if (field != value || forceApplyAmount) {
+ field = value
+ if (!nsslController.isInLockedDownShade() || forceApplyAmount) {
+ nsslController.setTransitionToFullShadeAmount(field)
+ notificationPanelController.setTransitionToFullShadeAmount(field,
+ false /* animate */, 0 /* delay */)
+ mediaHierarchyManager.setTransitionToFullShadeAmount(field)
+ val scrimProgress = MathUtils.saturate(field / scrimTransitionDistance)
+ scrimController.setTransitionToFullShadeProgress(scrimProgress)
+ // TODO: appear qs also in split shade
+ val qsAmount = if (useSplitShade) 0f else field
+ qS.setTransitionToFullShadeAmount(qsAmount, false /* animate */)
+ }
+ }
+ }
+
+ private fun setDragDownAmountAnimated(
+ target: Float,
+ delay: Long = 0,
+ endlistener: (() -> Unit)? = null
+ ) {
+ val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target)
+ dragDownAnimator.interpolator = Interpolators.FAST_OUT_SLOW_IN
+ dragDownAnimator.duration = SPRING_BACK_ANIMATION_LENGTH_MS
+ dragDownAnimator.addUpdateListener { animation: ValueAnimator ->
+ dragDownAmount = animation.animatedValue as Float
+ }
+ if (delay > 0) {
+ dragDownAnimator.startDelay = delay
+ }
+ if (endlistener != null) {
+ dragDownAnimator.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ endlistener.invoke()
+ }
+ })
+ }
+ dragDownAnimator.start()
+ this.dragDownAnimator = dragDownAnimator
+ }
+
+ /**
+ * Animate appear the drag down amount.
+ */
+ private fun animateAppear(delay: Long = 0) {
+ // changing to shade locked will make isInLockDownShade true, so let's override
+ // that
+ forceApplyAmount = true
+
+ // we set the value initially to 1 pixel, since that will make sure we're
+ // transitioning to the full shade. this is important to avoid flickering,
+ // as the below animation only starts once the shade is unlocked, which can
+ // be a couple of frames later. if we're setting it to 0, it will use the
+ // default inset and therefore flicker
+ dragDownAmount = 1f
+ setDragDownAmountAnimated(fullTransitionDistance.toFloat(), delay = delay) {
+ // End listener:
+ // Reset
+ dragDownAmount = 0f
+ forceApplyAmount = false
+ }
+ }
+
+ /**
+ * Ask this controller to go to the locked shade, changing the state change and doing
+ * an animation, where the qs appears from 0 from the top
+ *
+ * If secure with redaction: Show bouncer, go to unlocked shade.
+ * If secure without redaction or no security: Go to [StatusBarState.SHADE_LOCKED].
+ *
+ * @param expandView The view to expand after going to the shade
+ * @param needsQSAnimation if this needs the quick settings to slide in from the top or if
+ * that's already handled separately
+ */
+ @JvmOverloads
+ fun goToLockedShade(expandedView: View?, needsQSAnimation: Boolean = true) {
+ if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ val animationHandler: ((Long) -> Unit)?
+ if (needsQSAnimation) {
+ // Let's use the default animation
+ animationHandler = null
+ } else {
+ // Let's only animate notifications
+ animationHandler = { delay: Long ->
+ notificationPanelController.animateToFullShade(delay)
+ }
+ }
+ goToLockedShadeInternal(expandedView, animationHandler,
+ cancelAction = null)
+ }
+ }
+
+ /**
+ * If secure with redaction: Show bouncer, go to unlocked shade.
+ *
+ * If secure without redaction or no security: Go to [StatusBarState.SHADE_LOCKED].
+ *
+ * @param expandView The view to expand after going to the shade.
+ * @param animationHandler The handler which performs the go to full shade animation. If null,
+ * the default handler will do the animation, otherwise the caller is
+ * responsible for the animation. The input value is a Long for the
+ * delay for the animation.
+ * @param cancelAction The runnable to invoke when the transition is aborted. This happens if
+ * the user goes to the bouncer and goes back.
+ */
+ private fun goToLockedShadeInternal(
+ expandView: View?,
+ animationHandler: ((Long) -> Unit)? = null,
+ cancelAction: Runnable? = null
+ ) {
+ if (statusbar.isShadeDisabled) {
+ cancelAction?.run()
+ return
+ }
+ var userId: Int = lockScreenUserManager.getCurrentUserId()
+ var entry: NotificationEntry? = null
+ if (expandView is ExpandableNotificationRow) {
+ entry = expandView.entry
+ entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */)
+ // Indicate that the group expansion is changing at this time -- this way the group
+ // and children backgrounds / divider animations will look correct.
+ entry.setGroupExpansionChanging(true)
+ userId = entry.sbn.userId
+ }
+ var fullShadeNeedsBouncer = (!lockScreenUserManager.userAllowsPrivateNotificationsInPublic(
+ lockScreenUserManager.getCurrentUserId()) ||
+ !lockScreenUserManager.shouldShowLockscreenNotifications() ||
+ falsingCollector.shouldEnforceBouncer())
+ if (keyguardBypassController.bypassEnabled) {
+ fullShadeNeedsBouncer = false
+ }
+ if (lockScreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
+ statusBarStateController.setLeaveOpenOnKeyguardHide(true)
+ var onDismissAction: OnDismissAction? = null
+ if (animationHandler != null) {
+ onDismissAction = OnDismissAction {
+ // We're waiting on keyguard to hide before triggering the action,
+ // as that will make the animation work properly
+ animationHandlerOnKeyguardDismiss = animationHandler
+ false
+ }
+ }
+ val cancelHandler = Runnable {
+ draggedDownEntry?.apply {
+ setUserLocked(false)
+ notifyHeightChanged(false /* needsAnimation */)
+ draggedDownEntry = null
+ }
+ cancelAction?.run()
+ }
+ statusbar.showBouncerWithDimissAndCancelIfKeyguard(onDismissAction, cancelHandler)
+ draggedDownEntry = entry
+ } else {
+ statusBarStateController.setState(StatusBarState.SHADE_LOCKED)
+ // This call needs to be after updating the shade state since otherwise
+ // the scrimstate resets too early
+ if (animationHandler != null) {
+ animationHandler.invoke(0 /* delay */)
+ } else {
+ performDefaultGoToFullShadeAnimation(0)
+ }
+ }
+ }
+
+ /**
+ * Notify this handler that the keyguard was just dismissed and that a animation to
+ * the full shade should happen.
+ */
+ fun onHideKeyguard(delay: Long) {
+ if (animationHandlerOnKeyguardDismiss != null) {
+ animationHandlerOnKeyguardDismiss!!.invoke(delay)
+ animationHandlerOnKeyguardDismiss = null
+ } else {
+ if (nextHideKeyguardNeedsNoAnimation) {
+ nextHideKeyguardNeedsNoAnimation = false
+ } else {
+ performDefaultGoToFullShadeAnimation(delay)
+ }
+ }
+ draggedDownEntry?.apply {
+ setUserLocked(false)
+ draggedDownEntry = null
+ }
+ }
+
+ /**
+ * Perform the default appear animation when going to the full shade. This is called when
+ * not triggered by gestures, e.g. when clicking on the shelf or expand button.
+ */
+ private fun performDefaultGoToFullShadeAnimation(delay: Long) {
+ notificationPanelController.animateToFullShade(delay)
+ animateAppear(delay)
+ }
+}
+
+/**
+ * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand
+ * the notification where the drag started.
+ */
+class DragDownHelper(
+ private val falsingManager: FalsingManager,
+ private val falsingCollector: FalsingCollector,
+ private val dragDownCallback: LockscreenShadeTransitionController,
+ context: Context
+) : Gefingerpoken {
+
+ private var dragDownAmountOnStart = 0.0f
+ lateinit var expandCallback: ExpandHelper.Callback
+ lateinit var host: View
+
+ private var minDragDistance = 0
+ private var initialTouchX = 0f
+ private var initialTouchY = 0f
+ private var touchSlop = 0f
+ private var slopMultiplier = 0f
+ private val temp2 = IntArray(2)
+ private var draggedFarEnough = false
+ private var startingChild: ExpandableView? = null
+ private var lastHeight = 0f
+ var isDraggingDown = false
+ private set
+
+ private val isFalseTouch: Boolean
+ get() {
+ return if (!dragDownCallback.isFalsingCheckNeeded) {
+ false
+ } else {
+ falsingManager.isFalseTouch(Classifier.NOTIFICATION_DRAG_DOWN) || !draggedFarEnough
+ }
+ }
+
+ val isDragDownEnabled: Boolean
+ get() = dragDownCallback.isDragDownEnabledForView(null)
+
+ init {
+ updateResources(context)
+ }
+
+ fun updateResources(context: Context) {
+ minDragDistance = context.resources.getDimensionPixelSize(
+ R.dimen.keyguard_drag_down_min_distance)
+ val configuration = ViewConfiguration.get(context)
+ touchSlop = configuration.scaledTouchSlop.toFloat()
+ slopMultiplier = configuration.scaledAmbiguousGestureMultiplier
+ }
+
+ override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
+ val x = event.x
+ val y = event.y
+ when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ draggedFarEnough = false
+ isDraggingDown = false
+ startingChild = null
+ initialTouchY = y
+ initialTouchX = x
+ }
+ MotionEvent.ACTION_MOVE -> {
+ val h = y - initialTouchY
+ // Adjust the touch slop if another gesture may be being performed.
+ val touchSlop = if (event.classification
+ == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE)
+ touchSlop * slopMultiplier
+ else
+ touchSlop
+ if (h > touchSlop && h > Math.abs(x - initialTouchX)) {
+ falsingCollector.onNotificationStartDraggingDown()
+ isDraggingDown = true
+ captureStartingChild(initialTouchX, initialTouchY)
+ initialTouchY = y
+ initialTouchX = x
+ dragDownCallback.onDragDownStarted()
+ dragDownAmountOnStart = dragDownCallback.dragDownAmount
+ return startingChild != null || dragDownCallback.isDragDownAnywhereEnabled
+ }
+ }
+ }
+ return false
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ if (!isDraggingDown) {
+ return false
+ }
+ val x = event.x
+ val y = event.y
+ when (event.actionMasked) {
+ MotionEvent.ACTION_MOVE -> {
+ lastHeight = y - initialTouchY
+ captureStartingChild(initialTouchX, initialTouchY)
+ dragDownCallback.dragDownAmount = lastHeight + dragDownAmountOnStart
+ if (startingChild != null) {
+ handleExpansion(lastHeight, startingChild!!)
+ }
+ if (lastHeight > minDragDistance) {
+ if (!draggedFarEnough) {
+ draggedFarEnough = true
+ dragDownCallback.onCrossedThreshold(true)
+ }
+ } else {
+ if (draggedFarEnough) {
+ draggedFarEnough = false
+ dragDownCallback.onCrossedThreshold(false)
+ }
+ }
+ return true
+ }
+ MotionEvent.ACTION_UP -> if (!falsingManager.isUnlockingDisabled && !isFalseTouch &&
+ dragDownCallback.canDragDown()) {
+ dragDownCallback.onDraggedDown(startingChild, (y - initialTouchY).toInt())
+ if (startingChild != null) {
+ expandCallback.setUserLockedChild(startingChild, false)
+ startingChild = null
+ }
+ isDraggingDown = false
+ } else {
+ stopDragging()
+ return false
+ }
+ MotionEvent.ACTION_CANCEL -> {
+ stopDragging()
+ return false
+ }
+ }
+ return false
+ }
+
+ private fun captureStartingChild(x: Float, y: Float) {
+ if (startingChild == null) {
+ startingChild = findView(x, y)
+ if (startingChild != null) {
+ if (dragDownCallback.isDragDownEnabledForView(startingChild)) {
+ expandCallback.setUserLockedChild(startingChild, true)
+ } else {
+ startingChild = null
+ }
+ }
+ }
+ }
+
+ private fun handleExpansion(heightDelta: Float, child: ExpandableView) {
+ var hDelta = heightDelta
+ if (hDelta < 0) {
+ hDelta = 0f
+ }
+ val expandable = child.isContentExpandable
+ val rubberbandFactor = if (expandable) {
+ RUBBERBAND_FACTOR_EXPANDABLE
+ } else {
+ RUBBERBAND_FACTOR_STATIC
+ }
+ var rubberband = hDelta * rubberbandFactor
+ if (expandable && rubberband + child.collapsedHeight > child.maxContentHeight) {
+ var overshoot = rubberband + child.collapsedHeight - child.maxContentHeight
+ overshoot *= 1 - RUBBERBAND_FACTOR_STATIC
+ rubberband -= overshoot
+ }
+ child.actualHeight = (child.collapsedHeight + rubberband).toInt()
+ }
+
+ private fun cancelChildExpansion(child: ExpandableView) {
+ if (child.actualHeight == child.collapsedHeight) {
+ expandCallback.setUserLockedChild(child, false)
+ return
+ }
+ val anim = ObjectAnimator.ofInt(child, "actualHeight",
+ child.actualHeight, child.collapsedHeight)
+ anim.interpolator = Interpolators.FAST_OUT_SLOW_IN
+ anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS
+ anim.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ expandCallback.setUserLockedChild(child, false)
+ }
+ })
+ anim.start()
+ }
+
+ private fun stopDragging() {
+ falsingCollector.onNotificationStopDraggingDown()
+ if (startingChild != null) {
+ cancelChildExpansion(startingChild!!)
+ startingChild = null
+ }
+ isDraggingDown = false
+ dragDownCallback.onDragDownReset()
+ }
+
+ private fun findView(x: Float, y: Float): ExpandableView? {
+ host.getLocationOnScreen(temp2)
+ return expandCallback.getChildAtRawPosition(x + temp2[0], y + temp2[1])
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 1c5df41..5b69497 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -430,7 +430,6 @@
private static class LeftIconApplicator implements ResultApplicator {
public static final int[] MARGIN_ADJUSTED_VIEWS = {
- R.id.notification_headerless_view_column,
R.id.text,
R.id.big_text,
R.id.title,
@@ -438,22 +437,31 @@
R.id.notification_header};
@Override
- public void apply(View parent, View child, boolean apply, boolean reset) {
- ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon);
+ public void apply(View parent, View child, boolean showLeftIcon, boolean reset) {
ImageView leftIcon = child.findViewById(com.android.internal.R.id.left_icon);
- if (rightIcon == null || leftIcon == null) {
+ if (leftIcon == null) {
return;
}
- Drawable iconDrawable = rightIcon.getDrawable();
- if (iconDrawable == null) {
- return;
+ ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon);
+ boolean keepRightIcon = rightIcon != null && Integer.valueOf(1).equals(
+ rightIcon.getTag(R.id.tag_keep_when_showing_left_icon));
+ boolean leftIconUsesRightIconDrawable = Integer.valueOf(1).equals(
+ leftIcon.getTag(R.id.tag_uses_right_icon_drawable));
+ if (leftIconUsesRightIconDrawable) {
+ // Use the right drawable when showing the left, unless the right is being kept
+ Drawable rightDrawable = rightIcon == null ? null : rightIcon.getDrawable();
+ leftIcon.setImageDrawable(showLeftIcon && !keepRightIcon ? rightDrawable : null);
}
- rightIcon.setVisibility(apply ? View.GONE : View.VISIBLE);
- leftIcon.setVisibility(apply ? View.VISIBLE : View.GONE);
- leftIcon.setImageDrawable(apply ? iconDrawable : null);
+ leftIcon.setVisibility(showLeftIcon ? View.VISIBLE : View.GONE);
- for (int viewId : MARGIN_ADJUSTED_VIEWS) {
- adjustMargins(!apply, child.findViewById(viewId));
+ // update the right icon as well
+ if (rightIcon != null) {
+ boolean showRightIcon = (keepRightIcon || !showLeftIcon)
+ && rightIcon.getDrawable() != null;
+ rightIcon.setVisibility(showRightIcon ? View.VISIBLE : View.GONE);
+ for (int viewId : MARGIN_ADJUSTED_VIEWS) {
+ adjustMargins(showRightIcon, child.findViewById(viewId));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 7f31fdd..5437ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -18,7 +18,6 @@
import static com.android.systemui.statusbar.RemoteInputController.processForRemoteInput;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -35,6 +34,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
import java.util.List;
@@ -46,6 +46,7 @@
@SuppressLint("OverrideAbstract")
public class NotificationListener extends NotificationListenerWithPlugins {
private static final String TAG = "NotificationListener";
+ private static final boolean DEBUG = StatusBar.DEBUG;
private final Context mContext;
private final NotificationManager mNotificationManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index ebf7c2d..9a1a144 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -50,6 +50,12 @@
void addUserChangedListener(UserChangedListener listener);
/**
+ * Registers a [KeyguardNotificationSuppressor] that will be consulted during
+ * {@link #shouldShowOnKeyguard(NotificationEntry)}
+ */
+ void addKeyguardNotificationSuppressor(KeyguardNotificationSuppressor suppressor);
+
+ /**
* Removes a listener previously registered with
* {@link #addUserChangedListener(UserChangedListener)}
*/
@@ -88,4 +94,9 @@
default void onUserChanged(int userId) {}
default void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {}
}
+
+ /** Used to hide notifications on the lockscreen */
+ interface KeyguardNotificationSuppressor {
+ boolean shouldSuppressOnKeyguard(NotificationEntry entry);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 7eb921b..e7e9404 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -98,6 +98,7 @@
private LockPatternUtils mLockPatternUtils;
protected KeyguardManager mKeyguardManager;
private int mState = StatusBarState.SHADE;
+ private List<KeyguardNotificationSuppressor> mKeyguardSuppressors = new ArrayList<>();
protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
@Override
@@ -343,6 +344,11 @@
Log.wtf(TAG, "mEntryManager was null!", new Throwable());
return false;
}
+ for (int i = 0; i < mKeyguardSuppressors.size(); i++) {
+ if (mKeyguardSuppressors.get(i).shouldSuppressOnKeyguard(entry)) {
+ return false;
+ }
+ }
boolean exceedsPriorityThreshold;
if (hideSilentNotificationsOnLockscreen()) {
exceedsPriorityThreshold =
@@ -621,6 +627,11 @@
}
@Override
+ public void addKeyguardNotificationSuppressor(KeyguardNotificationSuppressor suppressor) {
+ mKeyguardSuppressors.add(suppressor);
+ }
+
+ @Override
public void removeUserChangedListener(UserChangedListener listener) {
mListeners.remove(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 2726488..f99436f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -250,7 +250,8 @@
@Override
public void onSmartspaceMediaDataLoaded(@NonNull String key,
- @NonNull SmartspaceTarget data) {
+ @NonNull SmartspaceTarget data, boolean shouldPrioritize) {
+
}
@Override
@@ -323,7 +324,8 @@
@Override
public void onSmartspaceMediaDataLoaded(@NonNull String key,
- @NonNull SmartspaceTarget data) {
+ @NonNull SmartspaceTarget data, boolean shouldPrioritize) {
+
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 70b3a7b..ca81a7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -169,7 +169,7 @@
Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
boolean started = RemoteViews.startPendingIntent(view, pendingIntent, options);
- if (started) releaseNotificationIfKeptForRemoteInputHistory(entry.getKey());
+ if (started) releaseNotificationIfKeptForRemoteInputHistory(entry);
return started;
});
}
@@ -608,7 +608,11 @@
* (after unlock, if applicable), and will then wait a short time to allow the app to update the
* notification in response to the action.
*/
- private void releaseNotificationIfKeptForRemoteInputHistory(String key) {
+ private void releaseNotificationIfKeptForRemoteInputHistory(NotificationEntry entry) {
+ if (entry == null) {
+ return;
+ }
+ final String key = entry.getKey();
if (isNotificationKeptForRemoteInputHistory(key)) {
mMainHandler.postDelayed(() -> {
if (isNotificationKeptForRemoteInputHistory(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 84465a8..9765ace 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -42,7 +42,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.ShadeController
import javax.inject.Inject
import kotlin.math.max
@@ -59,6 +58,7 @@
private val roundnessManager: NotificationRoundnessManager,
private val statusBarStateController: StatusBarStateController,
private val falsingManager: FalsingManager,
+ private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
private val falsingCollector: FalsingCollector
) : Gefingerpoken {
companion object {
@@ -66,7 +66,6 @@
private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
}
private val mPowerManager: PowerManager?
- private lateinit var shadeController: ShadeController
private val mMinDragDistance: Int
private var mInitialTouchX: Float = 0.0f
@@ -95,7 +94,7 @@
var leavingLockscreen: Boolean = false
private set
private val mTouchSlop: Float
- private lateinit var expansionCallback: ExpansionCallback
+ private lateinit var overStretchHandler: OverStretchHandler
private lateinit var stackScrollerController: NotificationStackScrollLayoutController
private val mTemp2 = IntArray(2)
private var mDraggedFarEnough: Boolean = false
@@ -103,7 +102,7 @@
private var mPulsing: Boolean = false
var isWakingToShadeLocked: Boolean = false
private set
- private var mEmptyDragAmount: Float = 0.0f
+ private var overStretchAmount: Float = 0.0f
private var mWakeUpHeight: Float = 0.0f
private var mReachedWakeUpHeight: Boolean = false
private var velocityTracker: VelocityTracker? = null
@@ -215,6 +214,7 @@
private fun finishExpansion() {
resetClock()
+ val startingChild = mStartingChild
if (mStartingChild != null) {
setUserLocked(mStartingChild!!, false)
mStartingChild = null
@@ -225,7 +225,8 @@
mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE,
"com.android.systemui:PULSEDRAG")
}
- shadeController.goToLockedShade(mStartingChild)
+ lockscreenShadeTransitionController.goToLockedShade(startingChild,
+ needsQSAnimation = false)
leavingLockscreen = true
isExpanding = false
if (mStartingChild is ExpandableNotificationRow) {
@@ -252,8 +253,8 @@
true /* increaseSpeed */)
expansionHeight = max(mWakeUpHeight, expansionHeight)
}
- val emptyDragAmount = wakeUpCoordinator.setPulseHeight(expansionHeight)
- setEmptyDragAmount(emptyDragAmount * RUBBERBAND_FACTOR_STATIC)
+ val dragDownAmount = wakeUpCoordinator.setPulseHeight(expansionHeight)
+ setOverStretchAmount(dragDownAmount)
}
private fun captureStartingChild(x: Float, y: Float) {
@@ -265,9 +266,9 @@
}
}
- private fun setEmptyDragAmount(amount: Float) {
- mEmptyDragAmount = amount
- expansionCallback.setEmptyDragAmount(amount)
+ private fun setOverStretchAmount(amount: Float) {
+ overStretchAmount = amount
+ overStretchHandler.setOverStretchAmount(amount)
}
private fun reset(child: ExpandableView) {
@@ -294,10 +295,12 @@
}
private fun resetClock() {
- val anim = ValueAnimator.ofFloat(mEmptyDragAmount, 0f)
+ val anim = ValueAnimator.ofFloat(overStretchAmount, 0f)
anim.interpolator = Interpolators.FAST_OUT_SLOW_IN
anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
- anim.addUpdateListener { animation -> setEmptyDragAmount(animation.animatedValue as Float) }
+ anim.addUpdateListener {
+ animation -> setOverStretchAmount(animation.animatedValue as Float)
+ }
anim.start()
}
@@ -329,11 +332,9 @@
fun setUp(
stackScrollerController: NotificationStackScrollLayoutController,
- expansionCallback: ExpansionCallback,
- shadeController: ShadeController
+ overStrechHandler: OverStretchHandler
) {
- this.expansionCallback = expansionCallback
- this.shadeController = shadeController
+ this.overStretchHandler = overStrechHandler
this.stackScrollerController = stackScrollerController
}
@@ -345,7 +346,11 @@
isWakingToShadeLocked = false
}
- interface ExpansionCallback {
- fun setEmptyDragAmount(amount: Float)
+ interface OverStretchHandler {
+
+ /**
+ * Set the overstretch amount in pixels This will be rubberbanded later
+ */
+ fun setOverStretchAmount(amount: Float)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index eb7854e..4919593 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -61,6 +61,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.DeviceConfigProxy;
@@ -243,11 +244,12 @@
SystemClock systemClock,
ActivityStarter activityStarter,
@Main Executor mainExecutor,
- IActivityManager iActivityManager) {
+ IActivityManager iActivityManager,
+ OngoingCallLogger logger) {
OngoingCallController ongoingCallController =
new OngoingCallController(
notifCollection, featureFlags, systemClock, activityStarter, mainExecutor,
- iActivityManager);
+ iActivityManager, logger);
ongoingCallController.init();
return ongoingCallController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 2481ed4..5f10e55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -29,13 +29,11 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
import com.android.systemui.statusbar.phone.StatusBarMarginUpdatedListener
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
-import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
-import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
-import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import java.lang.IllegalStateException
import java.util.concurrent.Executor
@@ -50,7 +48,7 @@
* will have its gravity set towards the corner (i.e., top-right corner gets top|right gravity), and
* the contained ImageView will be set to center_vertical and away from the corner horizontally. The
* Views will match the status bar top padding and status bar height so that the dot can appear to
- * reside directly after the status bar system contents (basically to the right of the battery).
+ * reside directly after the status bar system contents (basically after the battery).
*
* NOTE: any operation that modifies views directly must run on the provided executor, because
* these views are owned by ScreenDecorations and it runs in its own thread
@@ -85,21 +83,27 @@
// Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
private var uiExecutor: DelayableExecutor? = null
- private var e: DelayableExecutor? = null
+
+ private val marginListener: StatusBarMarginUpdatedListener =
+ object : StatusBarMarginUpdatedListener {
+ override fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int) {
+ setStatusBarMargins(marginLeft, marginRight)
+ }
+ }
private val views: Sequence<View>
get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
init {
- locationPublisher.addCallback(object : StatusBarMarginUpdatedListener {
- override fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int) {
- setStatusBarMargins(marginLeft, marginRight)
- }
- })
+ locationPublisher.addCallback(marginListener)
stateController.addCallback(object : StatusBarStateController.StateListener {
override fun onExpandedChanged(isExpanded: Boolean) {
- setStatusBarExpanded(isExpanded)
+ updateStatusBarState()
+ }
+
+ override fun onStateChanged(newState: Int) {
+ updateStatusBarState()
}
})
}
@@ -108,6 +112,13 @@
uiExecutor = e
}
+ fun setQsExpanded(expanded: Boolean) {
+ dlog("setQsExpanded $expanded")
+ synchronized(lock) {
+ nextViewState = nextViewState.copy(qsExpanded = expanded)
+ }
+ }
+
@UiThread
fun setNewRotation(rot: Int) {
dlog("updateRotation: $rot")
@@ -125,8 +136,8 @@
val index = newCorner.cornerIndex()
val h = when (rot) {
- ROTATION_NONE, ROTATION_UPSIDE_DOWN -> sbHeightPortrait
- ROTATION_LANDSCAPE, ROTATION_SEASCAPE -> sbHeightLandscape
+ 0, 2 -> sbHeightPortrait
+ 1, 3 -> sbHeightLandscape
else -> 0
}
synchronized(lock) {
@@ -326,15 +337,22 @@
}
}
- /**
- * We won't show the dot when quick settings is showing
- */
- private fun setStatusBarExpanded(expanded: Boolean) {
+ private fun updateStatusBarState() {
synchronized(lock) {
- nextViewState = nextViewState.copy(hideDotForQuickSettings = expanded)
+ nextViewState = nextViewState.copy(shadeExpanded = isShadeInQs())
}
}
+ /**
+ * If we are unlocked with an expanded shade, QS is showing. On keyguard, the shade is always
+ * expanded so we use other signals from the panel view controller to know if QS is expanded
+ */
+ @GuardedBy("lock")
+ private fun isShadeInQs(): Boolean {
+ return (stateController.isExpanded && stateController.state == SHADE) ||
+ (stateController.state == SHADE_LOCKED)
+ }
+
private fun scheduleUpdate() {
dlog("scheduleUpdate: ")
@@ -431,13 +449,20 @@
}
}
+private fun vlog(s: String) {
+ if (DEBUG_VERBOSE) {
+ Log.d(TAG, s)
+ }
+}
+
const val TOP_LEFT = 0
const val TOP_RIGHT = 1
const val BOTTOM_RIGHT = 2
const val BOTTOM_LEFT = 3
private const val DURATION = 160L
private const val TAG = "PrivacyDotViewController"
-private const val DEBUG = false
+private const val DEBUG = true
+private const val DEBUG_VERBOSE = false
private fun Int.toGravity(): Int {
return when (this) {
@@ -460,10 +485,10 @@
}
private data class ViewState(
- // don't @ me with names
val systemPrivacyEventIsActive: Boolean = false,
- val hideDotForQuickSettings: Boolean = false,
- val statusBarExpanded: Boolean = false,
+ val shadeExpanded: Boolean = false,
+ val qsExpanded: Boolean = false,
+
val rotation: Int = 0,
val height: Int = 0,
val marginLeft: Int = 0,
@@ -472,7 +497,7 @@
val designatedCorner: View? = null
) {
fun shouldShowDot(): Boolean {
- return systemPrivacyEventIsActive && !hideDotForQuickSettings
+ return systemPrivacyEventIsActive && !shadeExpanded && !qsExpanded
}
fun needsLayout(other: ViewState): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
new file mode 100644
index 0000000..71546ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.lockscreen
+
+import android.app.PendingIntent
+import android.app.smartspace.SmartspaceConfig
+import android.app.smartspace.SmartspaceManager
+import android.app.smartspace.SmartspaceSession
+import android.app.smartspace.SmartspaceTarget
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.view.View
+import android.view.ViewGroup
+import com.android.settingslib.Utils
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.concurrency.Execution
+import com.android.systemui.util.settings.SecureSettings
+import java.lang.RuntimeException
+import java.util.Optional
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Controller for managing the smartspace view on the lockscreen
+ */
+@SysUISingleton
+class LockscreenSmartspaceController @Inject constructor(
+ private val context: Context,
+ private val featureFlags: FeatureFlags,
+ private val smartspaceManager: SmartspaceManager,
+ private val activityStarter: ActivityStarter,
+ private val falsingManager: FalsingManager,
+ private val secureSettings: SecureSettings,
+ private val userTracker: UserTracker,
+ private val contentResolver: ContentResolver,
+ private val configurationController: ConfigurationController,
+ private val statusBarStateController: StatusBarStateController,
+ private val execution: Execution,
+ @Main private val uiExecutor: Executor,
+ @Main private val handler: Handler,
+ optionalPlugin: Optional<BcSmartspaceDataPlugin>
+) {
+ private var session: SmartspaceSession? = null
+ private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
+ private lateinit var smartspaceView: SmartspaceView
+
+ lateinit var view: View
+ private set
+
+ private var showSensitiveContentForCurrentUser = false
+ private var showSensitiveContentForManagedUser = false
+ private var managedUserHandle: UserHandle? = null
+
+ fun isEnabled(): Boolean {
+ execution.assertIsMainThread()
+
+ return featureFlags.isSmartspaceEnabled && plugin != null
+ }
+
+ /**
+ * Constructs the smartspace view and connects it to the smartspace service. Subsequent calls
+ * are idempotent until [disconnect] is called.
+ */
+ fun buildAndConnectView(parent: ViewGroup): View {
+ execution.assertIsMainThread()
+
+ if (!isEnabled()) {
+ throw RuntimeException("Cannot build view when not enabled")
+ }
+
+ buildView(parent)
+ connectSession()
+
+ return view
+ }
+
+ fun requestSmartspaceUpdate() {
+ session?.requestSmartspaceUpdate()
+ }
+
+ private fun buildView(parent: ViewGroup) {
+ if (plugin == null) {
+ return
+ }
+ if (this::view.isInitialized) {
+ // Due to some oddities with a singleton smartspace view, allow reparenting
+ (view.getParent() as ViewGroup?)?.removeView(view)
+ return
+ }
+
+ val ssView = plugin.getView(parent)
+ ssView.registerDataProvider(plugin)
+ ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
+ override fun startIntent(v: View?, i: Intent?) {
+ activityStarter.startActivity(i, true /* dismissShade */)
+ }
+
+ override fun startPendingIntent(pi: PendingIntent?) {
+ activityStarter.startPendingIntentDismissingKeyguard(pi)
+ }
+ })
+ ssView.setFalsingManager(falsingManager)
+
+ this.smartspaceView = ssView
+ this.view = ssView as View
+
+ updateTextColorFromWallpaper()
+ statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount)
+ }
+
+ private fun connectSession() {
+ if (plugin == null || session != null) {
+ return
+ }
+ val session = smartspaceManager.createSmartspaceSession(
+ SmartspaceConfig.Builder(context, "lockscreen").build())
+ session.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+
+ userTracker.addCallback(userTrackerCallback, uiExecutor)
+ contentResolver.registerContentObserver(
+ secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ true,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
+ configurationController.addCallback(configChangeListener)
+ statusBarStateController.addCallback(statusBarStateListener)
+
+ this.session = session
+
+ reloadSmartspace()
+ }
+
+ /**
+ * Disconnects the smartspace view from the smartspace service and cleans up any resources.
+ * Calling [buildAndConnectView] again will cause the same view to be reconnected to the
+ * service.
+ */
+ fun disconnect() {
+ execution.assertIsMainThread()
+
+ if (session == null) {
+ return
+ }
+
+ session?.let {
+ it.removeOnTargetsAvailableListener(sessionListener)
+ it.close()
+ }
+ userTracker.removeCallback(userTrackerCallback)
+ contentResolver.unregisterContentObserver(settingsObserver)
+ configurationController.removeCallback(configChangeListener)
+ statusBarStateController.removeCallback(statusBarStateListener)
+ session = null
+
+ plugin?.onTargetsAvailable(emptyList())
+ }
+
+ fun addListener(listener: SmartspaceTargetListener) {
+ execution.assertIsMainThread()
+ plugin?.registerListener(listener)
+ }
+
+ fun removeListener(listener: SmartspaceTargetListener) {
+ execution.assertIsMainThread()
+ plugin?.unregisterListener(listener)
+ }
+
+ private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
+ execution.assertIsMainThread()
+ val filteredTargets = targets.filter(::filterSmartspaceTarget)
+ plugin?.onTargetsAvailable(filteredTargets)
+ }
+
+ private val userTrackerCallback = object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ execution.assertIsMainThread()
+ reloadSmartspace()
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ }
+ }
+
+ private val settingsObserver = object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ execution.assertIsMainThread()
+ reloadSmartspace()
+ }
+ }
+
+ private val configChangeListener = object : ConfigurationController.ConfigurationListener {
+ override fun onThemeChanged() {
+ execution.assertIsMainThread()
+ updateTextColorFromWallpaper()
+ }
+ }
+
+ private val statusBarStateListener = object : StatusBarStateController.StateListener {
+ override fun onDozeAmountChanged(linear: Float, eased: Float) {
+ execution.assertIsMainThread()
+ smartspaceView.setDozeAmount(eased)
+ }
+ }
+
+ private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
+ return when (t.userHandle) {
+ userTracker.userHandle -> {
+ !t.isSensitive || showSensitiveContentForCurrentUser
+ }
+ managedUserHandle -> {
+ // Really, this should be "if this managed profile is associated with the current
+ // active user", but we don't have a good way to check that, so instead we cheat:
+ // Only the primary user can have an associated managed profile, so only show
+ // content for the managed profile if the primary user is active
+ userTracker.userHandle.identifier == UserHandle.USER_SYSTEM &&
+ (!t.isSensitive || showSensitiveContentForManagedUser)
+ }
+ else -> {
+ false
+ }
+ }
+ }
+
+ private fun updateTextColorFromWallpaper() {
+ val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor)
+ smartspaceView.setPrimaryTextColor(wallpaperTextColor)
+ }
+
+ private fun reloadSmartspace() {
+ val setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
+
+ showSensitiveContentForCurrentUser =
+ secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1
+
+ managedUserHandle = getWorkProfileUser()
+ val managedId = managedUserHandle?.identifier
+ if (managedId != null) {
+ showSensitiveContentForManagedUser =
+ secureSettings.getIntForUser(setting, 0, managedId) == 1
+ }
+
+ session?.requestSmartspaceUpdate()
+ }
+
+ private fun getWorkProfileUser(): UserHandle? {
+ for (userInfo in userTracker.userProfiles) {
+ if (userInfo.isManagedProfile) {
+ return userInfo.userHandle
+ }
+ }
+ return null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
index 9482c17..a0ccd57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
@@ -19,6 +19,8 @@
import android.annotation.Nullable;
import android.util.ArraySet;
+import androidx.annotation.VisibleForTesting;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -76,8 +78,9 @@
}
}
- private boolean isDynamicPrivacyEnabled() {
- return !mLockscreenUserManager.shouldHideNotifications(
+ @VisibleForTesting
+ boolean isDynamicPrivacyEnabled() {
+ return !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
mLockscreenUserManager.getCurrentUserId());
}
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 13a8661..1ab2a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -46,8 +46,9 @@
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker;
+import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRankerStub;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -138,12 +139,11 @@
private final LeakDetector mLeakDetector;
private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
- private final KeyguardEnvironment mKeyguardEnvironment;
private final NotificationGroupManagerLegacy mGroupManager;
- private final Lazy<NotificationRankingManager> mRankingManager;
private final FeatureFlags mFeatureFlags;
private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
+ private LegacyNotificationRanker mRanker = new LegacyNotificationRankerStub();
private NotificationPresenter mPresenter;
private RankingMap mLatestRankingMap;
@@ -200,8 +200,6 @@
public NotificationEntryManager(
NotificationEntryManagerLogger logger,
NotificationGroupManagerLegacy groupManager,
- Lazy<NotificationRankingManager> rankingManager,
- KeyguardEnvironment keyguardEnvironment,
FeatureFlags featureFlags,
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
@@ -211,8 +209,6 @@
) {
mLogger = logger;
mGroupManager = groupManager;
- mRankingManager = rankingManager;
- mKeyguardEnvironment = keyguardEnvironment;
mFeatureFlags = featureFlags;
mNotificationRowBinderLazy = notificationRowBinderLazy;
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
@@ -226,6 +222,10 @@
notificationListener.addNotificationHandler(mNotifListener);
}
+ public void setRanker(LegacyNotificationRanker ranker) {
+ mRanker = ranker;
+ }
+
/** Adds a {@link NotificationEntryListener}. */
public void addNotificationEntryListener(NotificationEntryListener listener) {
mNotificationEntryListeners.add(listener);
@@ -419,7 +419,7 @@
mActiveNotifications.put(entry.getKey(), entry);
mGroupManager.onEntryAdded(entry);
- updateRankingAndSort(mRankingManager.get().getRankingMap(), "addEntryInternalInternal");
+ updateRankingAndSort(mRanker.getRankingMap(), "addEntryInternalInternal");
}
/**
@@ -698,13 +698,6 @@
updateNotifications("updateNotificationInternal");
- if (DEBUG) {
- // Is this for you?
- boolean isForCurrentUser = mKeyguardEnvironment
- .isNotificationForCurrentProfiles(notification);
- Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
- }
-
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPostEntryUpdated(entry);
}
@@ -862,8 +855,7 @@
final int len = mActiveNotifications.size();
for (int i = 0; i < len; i++) {
NotificationEntry entry = mActiveNotifications.valueAt(i);
- final StatusBarNotification sbn = entry.getSbn();
- if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
+ if (!mRanker.isNotificationForCurrentProfiles(entry)) {
continue;
}
filtered.add(entry);
@@ -886,13 +878,13 @@
/** Resorts / filters the current notification set with the current RankingMap */
public void reapplyFilterAndSort(String reason) {
- updateRankingAndSort(mRankingManager.get().getRankingMap(), reason);
+ updateRankingAndSort(mRanker.getRankingMap(), reason);
}
/** Calls to NotificationRankingManager and updates mSortedAndFiltered */
private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
mSortedAndFiltered.clear();
- mSortedAndFiltered.addAll(mRankingManager.get().updateRanking(
+ mSortedAndFiltered.addAll(mRanker.updateRanking(
rankingMap, mActiveNotifications.values(), reason));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index f21771a..1940cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -27,14 +27,13 @@
import android.service.notification.StatusBarNotification;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.phone.ShadeController;
import javax.inject.Inject;
@@ -46,68 +45,43 @@
public class NotificationFilter {
private final StatusBarStateController mStatusBarStateController;
+ private final KeyguardEnvironment mKeyguardEnvironment;
+ private final ForegroundServiceController mForegroundServiceController;
+ private final NotificationLockscreenUserManager mUserManager;
private final Boolean mIsMediaFlagEnabled;
- private NotificationEntryManager.KeyguardEnvironment mEnvironment;
- private ShadeController mShadeController;
- private ForegroundServiceController mFsc;
- private NotificationLockscreenUserManager mUserManager;
-
@Inject
public NotificationFilter(
StatusBarStateController statusBarStateController,
+ KeyguardEnvironment keyguardEnvironment,
+ ForegroundServiceController foregroundServiceController,
+ NotificationLockscreenUserManager userManager,
MediaFeatureFlag mediaFeatureFlag) {
mStatusBarStateController = statusBarStateController;
+ mKeyguardEnvironment = keyguardEnvironment;
+ mForegroundServiceController = foregroundServiceController;
+ mUserManager = userManager;
mIsMediaFlagEnabled = mediaFeatureFlag.getEnabled();
}
- private NotificationEntryManager.KeyguardEnvironment getEnvironment() {
- if (mEnvironment == null) {
- mEnvironment = Dependency.get(NotificationEntryManager.KeyguardEnvironment.class);
- }
- return mEnvironment;
- }
-
- private ShadeController getShadeController() {
- if (mShadeController == null) {
- mShadeController = Dependency.get(ShadeController.class);
- }
- return mShadeController;
- }
-
- private ForegroundServiceController getFsc() {
- if (mFsc == null) {
- mFsc = Dependency.get(ForegroundServiceController.class);
- }
- return mFsc;
- }
-
- private NotificationLockscreenUserManager getUserManager() {
- if (mUserManager == null) {
- mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
- }
- return mUserManager;
- }
-
-
/**
* @return true if the provided notification should NOT be shown right now.
*/
public boolean shouldFilterOut(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
- if (!(getEnvironment().isDeviceProvisioned()
+ if (!(mKeyguardEnvironment.isDeviceProvisioned()
|| showNotificationEvenIfUnprovisioned(sbn))) {
return true;
}
- if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+ if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
return true;
}
- if (getUserManager().isLockscreenPublicMode(sbn.getUserId())
+ if (mUserManager.isLockscreenPublicMode(sbn.getUserId())
&& (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
- || getUserManager().shouldHideNotifications(sbn.getUserId())
- || getUserManager().shouldHideNotifications(sbn.getKey()))) {
+ || mUserManager.shouldHideNotifications(sbn.getUserId())
+ || mUserManager.shouldHideNotifications(sbn.getKey()))) {
return true;
}
@@ -123,8 +97,8 @@
return true;
}
- if (getFsc().isDisclosureNotification(sbn)
- && !getFsc().isDisclosureNeededForUser(sbn.getUserId())) {
+ if (mForegroundServiceController.isDisclosureNotification(sbn)
+ && !mForegroundServiceController.isDisclosureNeededForUser(sbn.getUserId())) {
// this is a foreground-service disclosure for a user that does not need to show one
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 6b96ee4..8ae31cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -185,6 +185,11 @@
mBuildListener = buildListener;
}
+ /** @see NotifPipeline#getEntry(String) () */
+ NotificationEntry getEntry(String key) {
+ return mNotificationSet.get(key);
+ }
+
/** @see NotifPipeline#getAllNotifs() */
Collection<NotificationEntry> getAllNotifs() {
Assert.isMainThread();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
index a1844ff..47939f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection;
+import androidx.annotation.Nullable;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
@@ -89,6 +91,7 @@
*
* The returned collection is read-only, unsorted, unfiltered, and ungrouped.
*/
+ @Override
public Collection<NotificationEntry> getAllNotifs() {
return mNotifCollection.getAllNotifs();
}
@@ -99,6 +102,14 @@
}
/**
+ * Returns the NotificationEntry associated with [key].
+ */
+ @Nullable
+ public NotificationEntry getEntry(String key) {
+ return mNotifCollection.getEntry(key);
+ }
+
+ /**
* Registers a lifetime extender. Lifetime extenders can cause notifications that have been
* dismissed or retracted by system server to be temporarily retained in the collection.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index fad0e49..2b620a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -23,9 +23,11 @@
import android.service.notification.NotificationListenerService.RankingMap
import android.service.notification.StatusBarNotification
import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
@@ -39,7 +41,6 @@
import dagger.Lazy
import java.util.Objects
import javax.inject.Inject
-import kotlin.Comparator
private const val TAG = "NotifRankingManager"
@@ -60,10 +61,11 @@
private val logger: NotificationEntryManagerLogger,
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
- private val highPriorityProvider: HighPriorityProvider
-) {
+ private val highPriorityProvider: HighPriorityProvider,
+ private val keyguardEnvironment: KeyguardEnvironment
+) : LegacyNotificationRanker {
- var rankingMap: RankingMap? = null
+ override var rankingMap: RankingMap? = null
protected set
private val mediaManager by lazy {
mediaManagerLazy.get()
@@ -115,7 +117,7 @@
}
}
- fun updateRanking(
+ override fun updateRanking(
newRankingMap: RankingMap?,
entries: Collection<NotificationEntry>,
reason: String
@@ -131,6 +133,12 @@
}
}
+ override fun isNotificationForCurrentProfiles(
+ entry: NotificationEntry
+ ): Boolean {
+ return keyguardEnvironment.isNotificationForCurrentProfiles(entry.sbn)
+ }
+
/** Uses the [rankingComparator] to sort notifications which aren't filtered */
private fun filterAndSortLocked(
entries: Collection<NotificationEntry>,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
index c1a11b2..2357072 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
+import androidx.annotation.NonNull;
+
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
@@ -27,5 +29,5 @@
* Called after the NewNotifPipeline is initialized.
* Coordinators should register their listeners and {@link Pluggable}s to the pipeline.
*/
- void attach(NotifPipeline pipeline);
+ void attach(@NonNull NotifPipeline pipeline);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index ded5e46..d80cc08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -60,6 +60,7 @@
ConversationCoordinator conversationCoordinator,
PreparationCoordinator preparationCoordinator,
MediaCoordinator mediaCoordinator,
+ SmartspaceDedupingCoordinator smartspaceDedupingCoordinator,
VisualStabilityCoordinator visualStabilityCoordinator) {
dumpManager.registerDumpable(TAG, this);
@@ -70,9 +71,14 @@
mCoordinators.add(appOpsCoordinator);
mCoordinators.add(deviceProvisionedCoordinator);
mCoordinators.add(bubbleCoordinator);
- mCoordinators.add(mediaCoordinator);
mCoordinators.add(conversationCoordinator);
+ mCoordinators.add(mediaCoordinator);
mCoordinators.add(visualStabilityCoordinator);
+
+ if (featureFlags.isSmartspaceDedupingEnabled()) {
+ mCoordinators.add(smartspaceDedupingCoordinator);
+ }
+
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
mCoordinators.add(headsUpCoordinator);
mCoordinators.add(preparationCoordinator);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt
new file mode 100644
index 0000000..442d9d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.app.smartspace.SmartspaceTarget
+import android.os.Parcelable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.time.SystemClock
+import java.util.concurrent.TimeUnit.SECONDS
+import javax.inject.Inject
+
+/**
+ * Hides notifications on the lockscreen if the content of those notifications is also visible
+ * in smartspace. This ONLY hides the notifications on the lockscreen: if the user pulls the shade
+ * down or unlocks the device, then the notifications are unhidden.
+ *
+ * In addition, notifications that have recently alerted aren't filtered. Tracking this in a way
+ * that involves the fewest pipeline invalidations requires some unfortunately complex logic.
+ */
+// This class is a singleton so that the same instance can be accessed by both the old and new
+// pipelines
+@SysUISingleton
+class SmartspaceDedupingCoordinator @Inject constructor(
+ private val statusBarStateController: SysuiStatusBarStateController,
+ private val smartspaceController: LockscreenSmartspaceController,
+ private val notificationEntryManager: NotificationEntryManager,
+ private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
+ private val notifPipeline: NotifPipeline,
+ @Main private val executor: DelayableExecutor,
+ private val clock: SystemClock
+) : Coordinator {
+ private var isOnLockscreen = false
+
+ private var trackedSmartspaceTargets = mutableMapOf<String, TrackedSmartspaceTarget>()
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addPreGroupFilter(filter)
+ pipeline.addCollectionListener(collectionListener)
+ statusBarStateController.addCallback(statusBarStateListener)
+ smartspaceController.addListener(this::onNewSmartspaceTargets)
+
+ // TODO (b/173126564): Remove this once the old pipeline is no longer necessary
+ notificationLockscreenUserManager.addKeyguardNotificationSuppressor { entry ->
+ isDupedWithSmartspaceContent(entry)
+ }
+
+ recordStatusBarState(statusBarStateController.state)
+ }
+
+ private fun isDupedWithSmartspaceContent(entry: NotificationEntry): Boolean {
+ return trackedSmartspaceTargets[entry.key]?.shouldFilter ?: false
+ }
+
+ private val filter = object : NotifFilter("SmartspaceDedupingFilter") {
+ override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean {
+ return isOnLockscreen && isDupedWithSmartspaceContent(entry)
+ }
+ }
+
+ private val collectionListener = object : NotifCollectionListener {
+ override fun onEntryAdded(entry: NotificationEntry) {
+ trackedSmartspaceTargets[entry.key]?.let {
+ updateFilterStatus(it)
+ }
+ }
+
+ override fun onEntryUpdated(entry: NotificationEntry) {
+ trackedSmartspaceTargets[entry.key]?.let {
+ updateFilterStatus(it)
+ }
+ }
+
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ trackedSmartspaceTargets[entry.key]?.let { trackedTarget ->
+ cancelExceptionTimeout(trackedTarget)
+ }
+ }
+ }
+
+ private val statusBarStateListener = object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ recordStatusBarState(newState)
+ }
+ }
+
+ private fun onNewSmartspaceTargets(targets: List<Parcelable>) {
+ var changed = false
+ val newMap = mutableMapOf<String, TrackedSmartspaceTarget>()
+ val oldMap = trackedSmartspaceTargets
+
+ for (target in targets) {
+ // For all targets that are SmartspaceTargets and have non-null sourceNotificationKeys
+ (target as? SmartspaceTarget)?.sourceNotificationKey?.let { key ->
+ val trackedTarget = oldMap.getOrElse(key) {
+ TrackedSmartspaceTarget(key)
+ }
+ newMap[key] = trackedTarget
+ changed = changed || updateFilterStatus(trackedTarget)
+ }
+ // Currently, only filter out the first target
+ break
+ }
+
+ for (prevKey in oldMap.keys) {
+ if (!newMap.containsKey(prevKey)) {
+ oldMap[prevKey]?.cancelTimeoutRunnable?.run()
+ changed = true
+ }
+ }
+
+ if (changed) {
+ filter.invalidateList()
+ notificationEntryManager.updateNotifications("Smartspace targets changed")
+ }
+
+ trackedSmartspaceTargets = newMap
+ }
+
+ /**
+ * Returns true if the target's alert exception status has changed
+ */
+ private fun updateFilterStatus(target: TrackedSmartspaceTarget): Boolean {
+ val prevShouldFilter = target.shouldFilter
+
+ val entry = notifPipeline.getEntry(target.key)
+ if (entry != null) {
+ updateAlertException(target, entry)
+
+ target.shouldFilter = !hasRecentlyAlerted(entry)
+ }
+
+ return target.shouldFilter != prevShouldFilter && isOnLockscreen
+ }
+
+ private fun updateAlertException(target: TrackedSmartspaceTarget, entry: NotificationEntry) {
+ val now = clock.currentTimeMillis()
+ val alertExceptionExpires = entry.ranking.lastAudiblyAlertedMillis + ALERT_WINDOW
+
+ if (alertExceptionExpires != target.alertExceptionExpires &&
+ alertExceptionExpires > now) {
+ // If we got here, the target is subject to a new alert exception window, so we
+ // should update our timeout to fire at the end of the new window
+
+ target.cancelTimeoutRunnable?.run()
+ target.alertExceptionExpires = alertExceptionExpires
+ target.cancelTimeoutRunnable = executor.executeDelayed({
+ target.cancelTimeoutRunnable = null
+ target.shouldFilter = true
+ filter.invalidateList()
+ notificationEntryManager.updateNotifications("deduping timeout expired")
+ }, alertExceptionExpires - now)
+ }
+ }
+
+ private fun cancelExceptionTimeout(target: TrackedSmartspaceTarget) {
+ target.cancelTimeoutRunnable?.run()
+ target.cancelTimeoutRunnable = null
+ target.alertExceptionExpires = 0
+ }
+
+ private fun recordStatusBarState(newState: Int) {
+ val wasOnLockscreen = isOnLockscreen
+ isOnLockscreen = newState == StatusBarState.KEYGUARD
+
+ if (isOnLockscreen != wasOnLockscreen) {
+ filter.invalidateList()
+ // No need to call notificationEntryManager.updateNotifications; something else already
+ // does it for us when the keyguard state changes
+ }
+ }
+
+ private fun hasRecentlyAlerted(entry: NotificationEntry): Boolean {
+ return clock.currentTimeMillis() - entry.ranking.lastAudiblyAlertedMillis <= ALERT_WINDOW
+ }
+}
+
+private class TrackedSmartspaceTarget(
+ val key: String
+) {
+ var cancelTimeoutRunnable: Runnable? = null
+ var alertExceptionExpires: Long = 0
+ var shouldFilter = false
+}
+
+private val ALERT_WINDOW = SECONDS.toMillis(30)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRanker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRanker.kt
new file mode 100644
index 0000000..49bc48e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRanker.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.legacy
+
+import android.service.notification.NotificationListenerService
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+interface LegacyNotificationRanker {
+ val rankingMap: NotificationListenerService.RankingMap?
+
+ fun updateRanking(
+ newRankingMap: NotificationListenerService.RankingMap?,
+ entries: Collection<NotificationEntry>,
+ reason: String
+ ): List<NotificationEntry>
+
+ fun isNotificationForCurrentProfiles(
+ entry: NotificationEntry
+ ): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRankerStub.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRankerStub.java
new file mode 100644
index 0000000..12353f8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRankerStub.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.legacy;
+
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Stub implementation that we use until we get passed the "real" one in the form of
+ * {@link com.android.systemui.statusbar.notification.collection.NotificationRankingManager}
+ */
+public class LegacyNotificationRankerStub implements LegacyNotificationRanker {
+ private RankingMap mRankingMap = new RankingMap(new Ranking[] {});
+
+ @NonNull
+ @Override
+ public List<NotificationEntry> updateRanking(
+ @Nullable RankingMap newRankingMap,
+ @NonNull Collection<NotificationEntry> entries,
+ @NonNull String reason) {
+ if (newRankingMap != null) {
+ mRankingMap = newRankingMap;
+ }
+ List<NotificationEntry> ranked = new ArrayList<>(entries);
+ ranked.sort(mEntryComparator);
+ return ranked;
+ }
+
+ @Nullable
+ @Override
+ public RankingMap getRankingMap() {
+ return mRankingMap;
+ }
+
+ private final Comparator<NotificationEntry> mEntryComparator = Comparator.comparingLong(
+ o -> o.getSbn().getNotification().when);
+
+ @Override
+ public boolean isNotificationForCurrentProfiles(@NonNull NotificationEntry entry) {
+ return true;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index d6356de..f40f24a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -16,7 +16,9 @@
package com.android.systemui.statusbar.notification.collection.legacy;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
@@ -31,6 +33,7 @@
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.wm.shell.bubbles.Bubbles;
@@ -39,10 +42,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.TreeSet;
import javax.inject.Inject;
@@ -58,13 +63,21 @@
public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, StateListener,
GroupMembershipManager, GroupExpansionManager, Dumpable {
- private static final String TAG = "NotificationGroupManager";
+ private static final String TAG = "NotifGroupManager";
+ private static final boolean DEBUG = StatusBar.DEBUG;
+ private static final boolean SPEW = StatusBar.SPEW;
+ /**
+ * The maximum amount of time (in ms) between the posting of notifications that can be
+ * considered part of the same update batch.
+ */
+ private static final long POST_BATCH_MAX_AGE = 5000;
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
private final ArraySet<OnGroupExpansionChangeListener> mExpansionChangeListeners =
new ArraySet<>();
private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
private final Optional<Bubbles> mBubblesOptional;
+ private final EventBuffer mEventBuffer = new EventBuffer();
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
@@ -134,8 +147,14 @@
* When we want to remove an entry from being tracked for grouping
*/
public void onEntryRemoved(NotificationEntry removed) {
+ if (SPEW) {
+ Log.d(TAG, "onEntryRemoved: entry=" + removed);
+ }
onEntryRemovedInternal(removed, removed.getSbn());
- mIsolatedEntries.remove(removed.getKey());
+ StatusBarNotification oldSbn = mIsolatedEntries.remove(removed.getKey());
+ if (oldSbn != null) {
+ updateSuppression(mGroupMap.get(oldSbn.getGroupKey()));
+ }
}
/**
@@ -162,6 +181,9 @@
// the close future. See b/23676310 for reference.
return;
}
+ if (SPEW) {
+ Log.d(TAG, "onEntryRemovedInternal: entry=" + removed + " group=" + group.groupKey);
+ }
if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
group.children.remove(removed.getKey());
} else {
@@ -182,6 +204,9 @@
* Notify the group manager that a new entry was added
*/
public void onEntryAdded(final NotificationEntry added) {
+ if (SPEW) {
+ Log.d(TAG, "onEntryAdded: entry=" + added);
+ }
updateIsolation(added);
onEntryAddedInternal(added);
}
@@ -195,13 +220,16 @@
String groupKey = getGroupKey(sbn);
NotificationGroup group = mGroupMap.get(groupKey);
if (group == null) {
- group = new NotificationGroup();
+ group = new NotificationGroup(groupKey);
mGroupMap.put(groupKey, group);
for (OnGroupChangeListener listener : mGroupChangeListeners) {
listener.onGroupCreated(group, groupKey);
}
}
+ if (SPEW) {
+ Log.d(TAG, "onEntryAddedInternal: entry=" + added + " group=" + group.groupKey);
+ }
if (isGroupChild) {
NotificationEntry existing = group.children.get(added.getKey());
if (existing != null && existing != added) {
@@ -213,9 +241,11 @@
+ " added removed" + added.isRowRemoved(), new Throwable());
}
group.children.put(added.getKey(), added);
+ addToPostBatchHistory(group, added);
updateSuppression(group);
} else {
group.summary = added;
+ addToPostBatchHistory(group, added);
group.expanded = added.areChildrenExpanded();
updateSuppression(group);
if (!group.children.isEmpty()) {
@@ -231,6 +261,27 @@
}
}
+ private void addToPostBatchHistory(NotificationGroup group, @Nullable NotificationEntry entry) {
+ if (entry == null) {
+ return;
+ }
+ boolean didAdd = group.postBatchHistory.add(new PostRecord(entry));
+ if (didAdd) {
+ trimPostBatchHistory(group.postBatchHistory);
+ }
+ }
+
+ /** remove all history that's too old to be in the batch. */
+ private void trimPostBatchHistory(@NonNull TreeSet<PostRecord> postBatchHistory) {
+ if (postBatchHistory.size() <= 1) {
+ return;
+ }
+ long batchStartTime = postBatchHistory.last().postTime - POST_BATCH_MAX_AGE;
+ while (!postBatchHistory.isEmpty() && postBatchHistory.first().postTime < batchStartTime) {
+ postBatchHistory.pollFirst();
+ }
+ }
+
private void onEntryBecomingChild(NotificationEntry entry) {
updateIsolation(entry);
}
@@ -239,6 +290,9 @@
if (group == null) {
return;
}
+ NotificationEntry prevAlertOverride = group.alertOverride;
+ group.alertOverride = getPriorityConversationAlertOverride(group);
+
int childCount = 0;
boolean hasBubbles = false;
for (NotificationEntry entry : group.children.values()) {
@@ -255,18 +309,150 @@
group.suppressed = group.summary != null && !group.expanded
&& (childCount == 1
|| (childCount == 0
- && group.summary.getSbn().getNotification().isGroupSummary()
- && (hasIsolatedChildren(group) || hasBubbles)));
- if (prevSuppressed != group.suppressed) {
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- if (!mIsUpdatingUnchangedGroup) {
- listener.onGroupSuppressionChanged(group, group.suppressed);
- listener.onGroupsChanged();
+ && group.summary.getSbn().getNotification().isGroupSummary()
+ && (hasIsolatedChildren(group) || hasBubbles)));
+
+ boolean alertOverrideChanged = prevAlertOverride != group.alertOverride;
+ boolean suppressionChanged = prevSuppressed != group.suppressed;
+ if (alertOverrideChanged || suppressionChanged) {
+ if (DEBUG && alertOverrideChanged) {
+ Log.d(TAG, "updateSuppression: alertOverride was=" + prevAlertOverride
+ + " now=" + group.alertOverride + " group:\n" + group);
+ }
+ if (DEBUG && suppressionChanged) {
+ Log.d(TAG,
+ "updateSuppression: suppressed changed to " + group.suppressed
+ + " group:\n" + group);
+ }
+ if (!mIsUpdatingUnchangedGroup) {
+ if (alertOverrideChanged) {
+ mEventBuffer.notifyAlertOverrideChanged(group, prevAlertOverride);
+ }
+ if (suppressionChanged) {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupSuppressionChanged(group, group.suppressed);
+ }
+ }
+ mEventBuffer.notifyGroupsChanged();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, group + " did not notify listeners of above change(s)");
}
}
}
}
+ /**
+ * Finds the isolated logical child of this group which is should be alerted instead.
+ *
+ * Notifications from priority conversations are isolated from their groups to make them more
+ * prominent, however apps may post these with a GroupAlertBehavior that has the group receiving
+ * the alert. This would lead to the group alerting even though the conversation that was
+ * updated was not actually a part of that group. This method finds the best priority
+ * conversation in this situation, if there is one, so they can be set as the alertOverride of
+ * the group.
+ *
+ * @param group the group to check
+ * @return the entry which should receive the alert instead of the group, if any.
+ */
+ @Nullable
+ private NotificationEntry getPriorityConversationAlertOverride(NotificationGroup group) {
+ // GOAL: if there is a priority child which wouldn't alert based on its groupAlertBehavior,
+ // but which should be alerting (because priority conversations are isolated), find it.
+ if (group == null || group.summary == null) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary");
+ }
+ return null;
+ }
+ if (isIsolated(group.summary.getKey())) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: isolated group");
+ }
+ return null;
+ }
+
+ // Precondiions:
+ // * Only necessary when all notifications in the group use GROUP_ALERT_SUMMARY
+ // * Only necessary when at least one notification in the group is on a priority channel
+ if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
+ != Notification.GROUP_ALERT_SUMMARY) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: summary != GROUP_ALERT_SUMMARY");
+ }
+ return null;
+ }
+
+ // Get the important children first, copy the keys for the final importance check,
+ // then add the non-isolated children to the map for unified lookup.
+ HashMap<String, NotificationEntry> children = getImportantConversations(group);
+ if (children == null || children.isEmpty()) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations");
+ }
+ return null;
+ }
+ HashSet<String> importantChildKeys = new HashSet<>(children.keySet());
+ children.putAll(group.children);
+
+ // Ensure all children have GROUP_ALERT_SUMMARY
+ for (NotificationEntry child : children.values()) {
+ if (child.getSbn().getNotification().getGroupAlertBehavior()
+ != Notification.GROUP_ALERT_SUMMARY) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: "
+ + "child != GROUP_ALERT_SUMMARY");
+ }
+ return null;
+ }
+ }
+
+ // Create a merged post history from all the children
+ TreeSet<PostRecord> combinedHistory = new TreeSet<>(group.postBatchHistory);
+ for (String importantChildKey : importantChildKeys) {
+ NotificationGroup importantChildGroup = mGroupMap.get(importantChildKey);
+ combinedHistory.addAll(importantChildGroup.postBatchHistory);
+ }
+ trimPostBatchHistory(combinedHistory);
+
+ // This is a streamlined implementation of the following idea:
+ // * From the subset of notifications in the latest 'batch' of updates. A batch is:
+ // * Notifs posted less than POST_BATCH_MAX_AGE before the most recently posted.
+ // * Only including notifs newer than the second-to-last post of any notification.
+ // * Find the newest child in the batch -- the with the largest 'when' value.
+ // * If the newest child is a priority conversation, set that as the override.
+ HashSet<String> batchKeys = new HashSet<>();
+ long newestChildWhen = -1;
+ NotificationEntry newestChild = null;
+ // Iterate backwards through the post history, tracking the child with the smallest sort key
+ for (PostRecord record : combinedHistory.descendingSet()) {
+ if (batchKeys.contains(record.key)) {
+ // Once you see a notification again, the batch has ended
+ break;
+ }
+ batchKeys.add(record.key);
+ NotificationEntry child = children.get(record.key);
+ if (child != null) {
+ long childWhen = child.getSbn().getNotification().when;
+ if (newestChild == null || childWhen > newestChildWhen) {
+ newestChildWhen = childWhen;
+ newestChild = child;
+ }
+ }
+ }
+ if (newestChild != null && importantChildKeys.contains(newestChild.getKey())) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: result=" + newestChild);
+ }
+ return newestChild;
+ }
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: result=null, newestChild="
+ + newestChild);
+ }
+ return null;
+ }
+
private boolean hasIsolatedChildren(NotificationGroup group) {
return getNumberOfIsolatedChildren(group.summary.getSbn().getGroupKey()) != 0;
}
@@ -281,12 +467,33 @@
return count;
}
+ @Nullable
+ private HashMap<String, NotificationEntry> getImportantConversations(NotificationGroup group) {
+ String groupKey = group.summary.getSbn().getGroupKey();
+ HashMap<String, NotificationEntry> result = null;
+ for (StatusBarNotification sbn : mIsolatedEntries.values()) {
+ if (sbn.getGroupKey().equals(groupKey)) {
+ NotificationEntry entry = mGroupMap.get(sbn.getKey()).summary;
+ if (isImportantConversation(entry)) {
+ if (result == null) {
+ result = new HashMap<>();
+ }
+ result.put(sbn.getKey(), entry);
+ }
+ }
+ }
+ return result;
+ }
+
/**
* Update an entry's group information
* @param entry notification entry to update
* @param oldNotification previous notification info before this update
*/
public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) {
+ if (SPEW) {
+ Log.d(TAG, "onEntryUpdated: entry=" + entry);
+ }
onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(),
oldNotification.getNotification().isGroupSummary());
}
@@ -325,7 +532,17 @@
* Whether the given notification is the summary of a group that is being suppressed
*/
public boolean isSummaryOfSuppressedGroup(StatusBarNotification sbn) {
- return isGroupSuppressed(getGroupKey(sbn)) && sbn.getNotification().isGroupSummary();
+ return sbn.getNotification().isGroupSummary() && isGroupSuppressed(getGroupKey(sbn));
+ }
+
+ /**
+ * If the given notification is a summary, get the group for it.
+ */
+ public NotificationGroup getGroupForSummary(StatusBarNotification sbn) {
+ if (sbn.getNotification().isGroupSummary()) {
+ return mGroupMap.get(getGroupKey(sbn));
+ }
+ return null;
}
private boolean isOnlyChild(StatusBarNotification sbn) {
@@ -545,9 +762,7 @@
if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
return false;
}
- int peopleNotificationType =
- mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
- if (peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON) {
+ if (isImportantConversation(entry)) {
return true;
}
if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.getKey())) {
@@ -560,18 +775,25 @@
|| isGroupNotFullyVisible(notificationGroup));
}
+ private boolean isImportantConversation(NotificationEntry entry) {
+ int peopleNotificationType =
+ mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
+ return peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON;
+ }
+
/**
* Isolate a notification from its group so that it visually shows as its own group.
*
* @param entry the notification to isolate
*/
private void isolateNotification(NotificationEntry entry) {
- StatusBarNotification sbn = entry.getSbn();
-
+ if (SPEW) {
+ Log.d(TAG, "isolateNotification: entry=" + entry);
+ }
// We will be isolated now, so lets update the groups
onEntryRemovedInternal(entry, entry.getSbn());
- mIsolatedEntries.put(sbn.getKey(), sbn);
+ mIsolatedEntries.put(entry.getKey(), entry.getSbn());
onEntryAddedInternal(entry);
// We also need to update the suppression of the old group, because this call comes
@@ -588,6 +810,14 @@
* Update the isolation of an entry, splitting it from the group.
*/
public void updateIsolation(NotificationEntry entry) {
+ // We need to buffer a few events because we do isolation changes in 3 steps:
+ // removeInternal, update mIsolatedEntries, addInternal. This means that often the
+ // alertOverride will update on the removal, however processing the event in that case can
+ // cause problems because the mIsolatedEntries map is not in its final state, so the event
+ // listener may be unable to correctly determine the true state of the group. By delaying
+ // the alertOverride change until after the add phase, we can ensure that listeners only
+ // have to handle a consistent state.
+ mEventBuffer.startBuffering();
boolean isIsolated = isIsolated(entry.getSbn().getKey());
if (shouldIsolate(entry)) {
if (!isIsolated) {
@@ -596,6 +826,7 @@
} else if (isIsolated) {
stopIsolatingNotification(entry);
}
+ mEventBuffer.flushAndStopBuffering();
}
/**
@@ -604,15 +835,15 @@
* @param entry the notification to un-isolate
*/
private void stopIsolatingNotification(NotificationEntry entry) {
- StatusBarNotification sbn = entry.getSbn();
- if (isIsolated(sbn.getKey())) {
- // not isolated anymore, we need to update the groups
- onEntryRemovedInternal(entry, entry.getSbn());
- mIsolatedEntries.remove(sbn.getKey());
- onEntryAddedInternal(entry);
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupsChanged();
- }
+ if (SPEW) {
+ Log.d(TAG, "stopIsolatingNotification: entry=" + entry);
+ }
+ // not isolated anymore, we need to update the groups
+ onEntryRemovedInternal(entry, entry.getSbn());
+ mIsolatedEntries.remove(entry.getKey());
+ onEntryAddedInternal(entry);
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupsChanged();
}
}
@@ -648,33 +879,154 @@
}
/**
+ * A record of a notification being posted, containing the time of the post and the key of the
+ * notification entry. These are stored in a TreeSet by the NotificationGroup and used to
+ * calculate a batch of notifications.
+ */
+ public static class PostRecord implements Comparable<PostRecord> {
+ public final long postTime;
+ public final String key;
+
+ /** constructs a record containing the post time and key from the notification entry */
+ public PostRecord(@NonNull NotificationEntry entry) {
+ this.postTime = entry.getSbn().getPostTime();
+ this.key = entry.getKey();
+ }
+
+ @Override
+ public int compareTo(PostRecord o) {
+ int postTimeComparison = Long.compare(this.postTime, o.postTime);
+ return postTimeComparison == 0
+ ? String.CASE_INSENSITIVE_ORDER.compare(this.key, o.key)
+ : postTimeComparison;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PostRecord that = (PostRecord) o;
+ return postTime == that.postTime && key.equals(that.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(postTime, key);
+ }
+ }
+
+ /**
* Represents a notification group in the notification shade.
*/
public static class NotificationGroup {
+ public final String groupKey;
public final HashMap<String, NotificationEntry> children = new HashMap<>();
+ public final TreeSet<PostRecord> postBatchHistory = new TreeSet<>();
public NotificationEntry summary;
public boolean expanded;
/**
* Is this notification group suppressed, i.e its summary is hidden
*/
public boolean suppressed;
+ /**
+ * The child (which is isolated from this group) to which the alert should be transferred,
+ * due to priority conversations.
+ */
+ public NotificationEntry alertOverride;
+
+ NotificationGroup(String groupKey) {
+ this.groupKey = groupKey;
+ }
@Override
public String toString() {
- String result = " summary:\n "
- + (summary != null ? summary.getSbn() : "null")
- + (summary != null && summary.getDebugThrowable() != null
- ? Log.getStackTraceString(summary.getDebugThrowable())
- : "");
- result += "\n children size: " + children.size();
+ StringBuilder sb = new StringBuilder();
+ sb.append(" groupKey: ").append(groupKey);
+ sb.append("\n summary:");
+ appendEntry(sb, summary);
+ sb.append("\n children size: ").append(children.size());
for (NotificationEntry child : children.values()) {
- result += "\n " + child.getSbn()
- + (child.getDebugThrowable() != null
- ? Log.getStackTraceString(child.getDebugThrowable())
- : "");
+ appendEntry(sb, child);
}
- result += "\n summary suppressed: " + suppressed;
- return result;
+ sb.append("\n alertOverride:");
+ appendEntry(sb, alertOverride);
+ sb.append("\n summary suppressed: ").append(suppressed);
+ return sb.toString();
+ }
+
+ private void appendEntry(StringBuilder sb, NotificationEntry entry) {
+ sb.append("\n ").append(entry != null ? entry.getSbn() : "null");
+ if (entry != null && entry.getDebugThrowable() != null) {
+ sb.append(Log.getStackTraceString(entry.getDebugThrowable()));
+ }
+ }
+ }
+
+ /**
+ * This class is a toggleable buffer for a subset of events of {@link OnGroupChangeListener}.
+ * When buffering, instead of notifying the listeners it will set internal state that will allow
+ * it to notify listeners of those events later
+ */
+ private class EventBuffer {
+ private final HashMap<String, NotificationEntry> mOldAlertOverrideByGroup = new HashMap<>();
+ private boolean mIsBuffering = false;
+ private boolean mDidGroupsChange = false;
+
+ void notifyAlertOverrideChanged(NotificationGroup group,
+ NotificationEntry oldAlertOverride) {
+ if (mIsBuffering) {
+ // The value in this map is the override before the event. If there is an entry
+ // already in the map, then we are effectively coalescing two events, which means
+ // we need to preserve the original initial value.
+ mOldAlertOverrideByGroup.putIfAbsent(group.groupKey, oldAlertOverride);
+ } else {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupAlertOverrideChanged(group, oldAlertOverride,
+ group.alertOverride);
+ }
+ }
+ }
+
+ void notifyGroupsChanged() {
+ if (mIsBuffering) {
+ mDidGroupsChange = true;
+ } else {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupsChanged();
+ }
+ }
+ }
+
+ void startBuffering() {
+ mIsBuffering = true;
+ }
+
+ void flushAndStopBuffering() {
+ // stop buffering so that we can call our own helpers
+ mIsBuffering = false;
+ // alert all group alert override changes for groups that were not removed
+ for (Map.Entry<String, NotificationEntry> entry : mOldAlertOverrideByGroup.entrySet()) {
+ NotificationGroup group = mGroupMap.get(entry.getKey());
+ if (group == null) {
+ // The group can be null if this alertOverride changed before the group was
+ // permanently removed, meaning that there's no guarantee that listeners will
+ // that field clear.
+ continue;
+ }
+ NotificationEntry oldAlertOverride = entry.getValue();
+ if (group.alertOverride == oldAlertOverride) {
+ // If the final alertOverride equals the initial, it means we coalesced two
+ // events which undid the change, so we can drop it entirely.
+ continue;
+ }
+ notifyAlertOverrideChanged(group, oldAlertOverride);
+ }
+ mOldAlertOverrideByGroup.clear();
+ // alert that groups changed
+ if (mDidGroupsChange) {
+ notifyGroupsChanged();
+ mDidGroupsChange = false;
+ }
}
}
@@ -714,6 +1066,18 @@
boolean suppressed) {}
/**
+ * The alert override of a group has changed.
+ *
+ * @param group the group that has changed
+ * @param oldAlertOverride the previous notification to which the group's alerts were sent
+ * @param newAlertOverride the notification to which the group's alerts should now be sent
+ */
+ default void onGroupAlertOverrideChanged(
+ NotificationGroup group,
+ @Nullable NotificationEntry oldAlertOverride,
+ @Nullable NotificationEntry newAlertOverride) {}
+
+ /**
* A group of children just received a summary notification and should therefore become
* children of it.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
index 9edb5fc..776c7d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+import androidx.annotation.NonNull;
+
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -45,5 +47,5 @@
* various entries against.
* @return True if the notif should be removed from the list
*/
- public abstract boolean shouldFilterOut(NotificationEntry entry, long now);
+ public abstract boolean shouldFilterOut(@NonNull NotificationEntry entry, long now);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 89bb652..a32b7e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -44,7 +44,6 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
@@ -100,8 +99,6 @@
static NotificationEntryManager provideNotificationEntryManager(
NotificationEntryManagerLogger logger,
NotificationGroupManagerLegacy groupManager,
- Lazy<NotificationRankingManager> rankingManager,
- NotificationEntryManager.KeyguardEnvironment keyguardEnvironment,
FeatureFlags featureFlags,
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
@@ -111,8 +108,6 @@
return new NotificationEntryManager(
logger,
groupManager,
- rankingManager,
- keyguardEnvironment,
featureFlags,
notificationRowBinderLazy,
notificationRemoteInputManagerLazy,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index fd5128a..88ca86b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.init
-import android.content.Context
import android.service.notification.StatusBarNotification
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.people.widget.PeopleSpaceWidgetManager
@@ -30,6 +29,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.NotificationListController
import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
@@ -59,10 +59,10 @@
*/
@SysUISingleton
class NotificationsControllerImpl @Inject constructor(
- private val context: Context,
private val featureFlags: FeatureFlags,
private val notificationListener: NotificationListener,
private val entryManager: NotificationEntryManager,
+ private val legacyRanker: NotificationRankingManager,
private val notifPipeline: Lazy<NotifPipeline>,
private val targetSdkResolver: TargetSdkResolver,
private val newNotifPipeline: Lazy<NotifPipelineInitializer>,
@@ -128,6 +128,7 @@
groupManagerLegacy.get().setHeadsUpManager(headsUpManager)
groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
+ entryManager.setRanker(legacyRanker)
entryManager.attach(notificationListener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 151840a..79f99b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -42,12 +42,17 @@
updateImageTag(row.getEntry().getSbn());
}
- private void updateImageTag(StatusBarNotification notification) {
- final Bundle extras = notification.getNotification().extras;
- Icon overriddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG);
- if (overriddenIcon != null) {
- mRightIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon);
- mLeftIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon);
+ private void updateImageTag(StatusBarNotification sbn) {
+ final Bundle extras = sbn.getNotification().extras;
+ boolean bigLargeIconSet = extras.containsKey(Notification.EXTRA_LARGE_ICON_BIG);
+ if (bigLargeIconSet) {
+ Icon bigLargeIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG);
+ mRightIcon.setTag(ImageTransformState.ICON_TAG, bigLargeIcon);
+ mLeftIcon.setTag(ImageTransformState.ICON_TAG, bigLargeIcon);
+ } else {
+ // Overwrite in case the superclass populated this tag with the promoted picture,
+ // which won't be right since this is the expanded state.
+ mRightIcon.setTag(ImageTransformState.ICON_TAG, getLargeIcon(sbn.getNotification()));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 0548611..a4f1172 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -20,10 +20,12 @@
import static com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.DEFAULT_HEADER_VISIBLE_AMOUNT;
+import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.graphics.drawable.Icon;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.view.View;
@@ -32,6 +34,8 @@
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.NotificationActionListLayout;
import com.android.systemui.Dependency;
@@ -143,16 +147,14 @@
com.android.internal.R.dimen.notification_content_margin_top);
}
- private void resolveTemplateViews(StatusBarNotification notification) {
+ private void resolveTemplateViews(StatusBarNotification sbn) {
mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
if (mRightIcon != null) {
- mRightIcon.setTag(ImageTransformState.ICON_TAG,
- notification.getNotification().getLargeIcon());
+ mRightIcon.setTag(ImageTransformState.ICON_TAG, getRightIcon(sbn.getNotification()));
}
mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon);
if (mLeftIcon != null) {
- mLeftIcon.setTag(ImageTransformState.ICON_TAG,
- notification.getNotification().getLargeIcon());
+ mLeftIcon.setTag(ImageTransformState.ICON_TAG, getLargeIcon(sbn.getNotification()));
}
mTitle = mView.findViewById(com.android.internal.R.id.title);
mText = mView.findViewById(com.android.internal.R.id.text);
@@ -171,6 +173,27 @@
updatePendingIntentCancellations();
}
+ @Nullable
+ protected final Icon getLargeIcon(Notification n) {
+ Icon modernLargeIcon = n.getLargeIcon();
+ if (modernLargeIcon == null && n.largeIcon != null) {
+ return Icon.createWithBitmap(n.largeIcon);
+ }
+ return modernLargeIcon;
+ }
+
+ @Nullable
+ protected final Icon getRightIcon(Notification n) {
+ if (n.extras.getBoolean(Notification.EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED)
+ && n.getNotificationStyle() == Notification.BigPictureStyle.class) {
+ Icon pictureIcon = Notification.BigPictureStyle.getPictureIcon(n.extras);
+ if (pictureIcon != null) {
+ return pictureIcon;
+ }
+ }
+ return getLargeIcon(n);
+ }
+
private void updatePendingIntentCancellations() {
if (mActions != null) {
int numActions = mActions.getChildCount();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index caf4720..66d2347 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -95,6 +95,7 @@
/** Height of the notifications panel without top padding when expansion completes. */
private float mStackEndHeight;
+ private float mTransitionToFullShadeAmount;
/**
* @return Height of the notifications panel without top padding when expansion completes.
@@ -595,6 +596,21 @@
}
/**
+ * Set the amount of pixels we have currently dragged down if we're transitioning to the full
+ * shade. 0.0f means we're not transitioning yet.
+ */
+ public void setTransitionToFullShadeAmount(float transitionToFullShadeAmount) {
+ mTransitionToFullShadeAmount = transitionToFullShadeAmount;
+ }
+
+ /**
+ * get
+ */
+ public float getTransitionToFullShadeAmount() {
+ return mTransitionToFullShadeAmount;
+ }
+
+ /**
* Returns the currently tracked heads up row, if there is one and it is currently above the
* shelf (still appearing).
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 120f973..a804ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -46,7 +46,6 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
@@ -71,17 +70,14 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardSliceView;
import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -89,7 +85,6 @@
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -108,9 +103,6 @@
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -151,7 +143,6 @@
*/
private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
- private final SysuiStatusBarStateController mStatusbarStateController;
private ExpandHelper mExpandHelper;
private NotificationSwipeHelper mSwipeHelper;
@@ -433,13 +424,9 @@
private ShadeController mShadeController;
private Runnable mOnStackYChanged;
- private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
- private final LockscreenGestureLogger mLockscreenGestureLogger =
- Dependency.get(LockscreenGestureLogger.class);
protected boolean mClearAllEnabled;
private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
- private NotificationPanelViewController mNotificationPanelController;
private final NotificationSectionsManager mSectionsManager;
private ForegroundServiceDungeonView mFgsSectionView;
@@ -449,6 +436,11 @@
private boolean mWillExpand;
private int mGapHeight;
+ /**
+ * The extra inset during the full shade transition
+ */
+ private float mExtraTopInsetForFullShadeTransition;
+
private int mWaterfallTopInset;
private NotificationStackScrollLayoutController mController;
@@ -496,7 +488,6 @@
NotificationSectionsManager notificationSectionsManager,
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
- SysuiStatusBarStateController statusbarStateController,
AmbientState ambientState,
FeatureFlags featureFlags) {
super(context, attrs, 0, 0);
@@ -535,7 +526,6 @@
mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
mGroupMembershipManager = groupMembershipManager;
mGroupExpansionManager = groupExpansionManager;
- mStatusbarStateController = statusbarStateController;
}
void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) {
@@ -1142,18 +1132,22 @@
*/
private void updateStackPosition() {
// Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
+ float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition;
final float fraction = mAmbientState.getExpansionFraction();
- final float stackY = MathUtils.lerp(0, mTopPadding, fraction);
+ final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
mAmbientState.setStackY(stackY);
if (mOnStackYChanged != null) {
mOnStackYChanged.run();
}
-
- final float stackEndHeight = getHeight() - getEmptyBottomMargin() - mTopPadding;
- mAmbientState.setStackEndHeight(stackEndHeight);
- mAmbientState.setStackHeight(
- MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION,
- stackEndHeight, fraction));
+ if (mQsExpansionFraction <= 0) {
+ final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mSidePaddings;
+ final float stackEndHeight = Math.max(0f,
+ getHeight() - getEmptyBottomMargin() - mTopPadding);
+ mAmbientState.setStackEndHeight(stackEndHeight);
+ mAmbientState.setStackHeight(
+ MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION,
+ stackEndHeight, fraction));
+ }
}
void setOnStackYChanged(Runnable onStackYChanged) {
@@ -2065,18 +2059,22 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateContentHeight() {
- int height = 0;
+ final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mSidePaddings;
+ int height = (int) scrimTopPadding;
float previousPaddingRequest = mPaddingBetweenElements;
int numShownItems = 0;
int numShownNotifs = 0;
boolean finish = false;
int maxDisplayedNotifications = mMaxDisplayedNotifications;
ExpandableView previousView = null;
+
for (int i = 0; i < getChildCount(); i++) {
ExpandableView expandableView = (ExpandableView) getChildAt(i);
boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
+
if (expandableView.getVisibility() != View.GONE
&& !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
+
boolean limitReached = maxDisplayedNotifications != -1
&& numShownNotifs >= maxDisplayedNotifications;
final float viewHeight;
@@ -4271,6 +4269,13 @@
+ mGapHeight;
}
+ /**
+ * @return the padding after the media header on the lockscreen
+ */
+ public int getPaddingAfterMedia() {
+ return mGapHeight + mPaddingBetweenElements;
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public int getEmptyShadeViewHeight() {
return mEmptyShadeView.getHeight();
@@ -4926,12 +4931,6 @@
getChildCount() - offsetFromEnd);
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setNotificationPanelController(
- NotificationPanelViewController notificationPanelViewController) {
- mNotificationPanelController = notificationPanelViewController;
- }
-
/**
* Set how far the wake up is when waking up from pulsing. This is a height and will adjust the
* notification positions accordingly.
@@ -5148,6 +5147,16 @@
}
/**
+ * Sets the extra top inset for the full shade transition. This is needed to compensate for
+ * media transitioning to quick settings
+ */
+ public void setExtraTopInsetForFullShadeTransition(float inset) {
+ mExtraTopInsetForFullShadeTransition = inset;
+ updateStackPosition();
+ requestChildrenUpdate();
+ }
+
+ /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5519,108 +5528,10 @@
}
}
- public void setKeyguardMediaControllorVisible(boolean keyguardMediaControllorVisible) {
- mKeyguardMediaControllorVisible = keyguardMediaControllorVisible;
- }
-
void resetCheckSnoozeLeavebehind() {
setCheckForLeaveBehind(true);
}
- // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private final DragDownCallback mDragDownCallback = new DragDownCallback() {
-
- @Override
- public boolean canDragDown() {
- return mStatusBarState == StatusBarState.KEYGUARD
- && (mController.hasActiveNotifications() || mKeyguardMediaControllorVisible)
- || mController.isInLockedDownShade();
- }
-
- /* Only ever called as a consequence of a lockscreen expansion gesture. */
- @Override
- public void onDraggedDown(View startingChild, int dragLengthY) {
- boolean canDragDown =
- mController.hasActiveNotifications() || mKeyguardMediaControllorVisible;
- if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) {
- mLockscreenGestureLogger.write(
- MetricsEvent.ACTION_LS_SHADE,
- (int) (dragLengthY / mDisplayMetrics.density),
- 0 /* velocityDp - N/A */);
- mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN);
-
- if (!mAmbientState.isDozing() || startingChild != null) {
- // We have notifications, go to locked shade.
- mShadeController.goToLockedShade(startingChild);
- if (startingChild instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
- row.onExpandedByGesture(true /* drag down is always an open */);
- }
- }
- } else if (mController.isInLockedDownShade()) {
- mStatusbarStateController.setLeaveOpenOnKeyguardHide(true);
- mStatusBar.dismissKeyguardThenExecute(() -> false /* dismissAction */,
- null /* cancelRunnable */, false /* afterKeyguardGone */);
- }
- }
-
- @Override
- public void onDragDownReset() {
- setDimmed(true /* dimmed */, true /* animated */);
- resetScrollPosition();
- resetCheckSnoozeLeavebehind();
- }
-
- @Override
- public void onCrossedThreshold(boolean above) {
- setDimmed(!above /* dimmed */, true /* animate */);
- }
-
- @Override
- public void onTouchSlopExceeded() {
- cancelLongPress();
- mController.checkSnoozeLeavebehind();
- }
-
- @Override
- public void setEmptyDragAmount(float amount) {
- mNotificationPanelController.setEmptyDragAmount(amount);
- }
-
- @Override
- public boolean isFalsingCheckNeeded() {
- return mStatusBarState == StatusBarState.KEYGUARD;
- }
-
- @Override
- public boolean isDragDownEnabledForView(ExpandableView view) {
- if (isDragDownAnywhereEnabled()) {
- return true;
- }
- if (mController.isInLockedDownShade()) {
- if (view == null) {
- // Dragging down is allowed in general
- return true;
- }
- if (view instanceof ExpandableNotificationRow) {
- // Only drag down on sensitive views, otherwise the ExpandHelper will take this
- return ((ExpandableNotificationRow) view).getEntry().isSensitive();
- }
- }
- return false;
- }
-
- @Override
- public boolean isDragDownAnywhereEnabled() {
- return mStatusbarStateController.getState() == StatusBarState.KEYGUARD
- && !mKeyguardBypassEnabledProvider.getBypassEnabled();
- }
- };
-
- public DragDownCallback getDragDownCallback() { return mDragDownCallback; }
-
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f7eb574..0d42428 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -31,6 +31,7 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeDismissed;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
@@ -38,6 +39,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Pair;
import android.view.Display;
import android.view.LayoutInflater;
@@ -59,6 +61,7 @@
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -70,6 +73,7 @@
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -107,7 +111,6 @@
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -168,6 +171,7 @@
// TODO: StatusBar should be encapsulated behind a Controller
private final StatusBar mStatusBar;
private final SectionHeaderController mSilentHeaderController;
+ private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private NotificationStackScrollLayout mView;
private boolean mFadeNotificationsOnDismiss;
@@ -181,6 +185,9 @@
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
+ private int mTotalDistanceForFullShadeTransition;
+ private int mTotalExtraMediaInsetFullShadeTransition;
+
@VisibleForTesting
final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
new View.OnAttachStateChangeListener() {
@@ -240,8 +247,20 @@
public void onThemeChanged() {
updateFooter();
}
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ updateResources();
+ }
};
+ private void updateResources() {
+ mTotalExtraMediaInsetFullShadeTransition = mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_transition_extra_media_inset);
+ mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_qs_transition_distance);
+ }
+
private final StatusBarStateController.StateListener mStateListener =
new StatusBarStateController.StateListener() {
@Override
@@ -571,6 +590,7 @@
NotifPipeline notifPipeline,
NotifCollection notifCollection,
NotificationEntryManager notificationEntryManager,
+ LockscreenShadeTransitionController lockscreenShadeTransitionController,
IStatusBarService iStatusBarService,
UiEventLogger uiEventLogger,
ForegroundServiceDismissalFeatureController fgFeatureController,
@@ -592,6 +612,7 @@
mZenModeController = zenModeController;
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
+ mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mResources = resources;
@@ -624,6 +645,7 @@
mRemoteInputManager = remoteInputManager;
mVisualStabilityManager = visualStabilityManager;
mShadeController = shadeController;
+ updateResources();
}
public void attach(NotificationStackScrollLayout view) {
@@ -676,6 +698,8 @@
mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming);
+ mLockscreenShadeTransitionController.setStackScroller(this);
+
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
mFadeNotificationsOnDismiss = // TODO: this should probably be injected directly
@@ -705,7 +729,6 @@
Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
mKeyguardMediaController.setVisibilityChangedListener(visible -> {
- mView.setKeyguardMediaControllorVisible(visible);
if (visible) {
mView.generateAddAnimation(
mKeyguardMediaController.getSinglePaneContainer(),
@@ -1203,11 +1226,6 @@
mView.runAfterAnimationFinished(r);
}
- public void setNotificationPanelController(
- NotificationPanelViewController notificationPanelViewController) {
- mView.setNotificationPanelController(notificationPanelViewController);
- }
-
public void setShelfController(NotificationShelfController notificationShelfController) {
mView.setShelfController(notificationShelfController);
}
@@ -1275,7 +1293,10 @@
NotificationLogger.getNotificationLocation(entry)));
}
- boolean hasActiveNotifications() {
+ /**
+ * @return if the shade has currently any active notifications.
+ */
+ public boolean hasActiveNotifications() {
if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
return !mNotifPipeline.getShadeList().isEmpty();
} else {
@@ -1354,11 +1375,60 @@
}
}
+ /**
+ * @return the expand helper callback.
+ */
+ public ExpandHelper.Callback getExpandHelperCallback() {
+ return mView.getExpandHelperCallback();
+ }
+
+ /**
+ * @return If the shade is in the locked down shade.
+ */
public boolean isInLockedDownShade() {
return mDynamicPrivacyController.isInLockedDownShade();
}
/**
+ * Set the dimmed state for all of the notification views.
+ */
+ public void setDimmed(boolean dimmed, boolean animate) {
+ mView.setDimmed(dimmed, animate);
+ }
+
+ /**
+ * @return the inset during the full shade transition, that needs to be added to the position
+ * of the quick settings edge. This is relevant for media, that is transitioning
+ * from the keyguard host to the quick settings one.
+ */
+ public int getFullShadeTransitionInset() {
+ MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer();
+ if (view == null || view.getHeight() == 0
+ || mStatusBarStateController.getState() != KEYGUARD) {
+ return 0;
+ }
+ return view.getHeight() + mView.getPaddingAfterMedia();
+ }
+
+ /**
+ * Set the amount of pixels we have currently dragged down if we're transitioning to the full
+ * shade. 0.0f means we're not transitioning yet.
+ */
+ public void setTransitionToFullShadeAmount(float amount) {
+ float extraTopInset;
+ MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer();
+ if (view == null || view.getHeight() == 0
+ || mStatusBarStateController.getState() != KEYGUARD) {
+ extraTopInset = 0;
+ } else {
+ extraTopInset = MathUtils.saturate(amount / mTotalDistanceForFullShadeTransition);
+ extraTopInset = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(extraTopInset);
+ extraTopInset = extraTopInset * mTotalExtraMediaInsetFullShadeTransition;
+ }
+ mView.setExtraTopInsetForFullShadeTransition(extraTopInset);
+ }
+
+ /**
* Enum for UiEvent logged from this class
*/
enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index d94d030..e931ec4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -58,6 +58,7 @@
private int mStatusBarHeight;
private float mHeadsUpInset;
private int mPinnedZTranslationExtra;
+ private float mNotificationScrimPadding;
public StackScrollAlgorithm(
Context context,
@@ -82,6 +83,7 @@
mPinnedZTranslationExtra = res.getDimensionPixelSize(
R.dimen.heads_up_pinned_elevation);
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
+ mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
}
/**
@@ -223,6 +225,8 @@
// already accounted for by the top padding and doesn't need an additional adaption
scrollY = Math.max(0, scrollY);
state.scrollY = (int) (scrollY + bottomOverScroll);
+ state.mCurrentYPosition = -state.scrollY;
+ state.mCurrentExpandedYPosition = -state.scrollY;
//now init the visible children and update paddings
int childCount = hostView.getChildCount();
@@ -258,6 +262,9 @@
// expanded. Consider updating these states in updateContentView instead so that we don't
// have to recalculate in every frame.
float currentY = -scrollY;
+ if (!ambientState.isOnKeyguard()) {
+ currentY += mNotificationScrimPadding;
+ }
float previousY = 0;
state.firstViewInShelf = null;
state.viewHeightBeforeShelf = -1;
@@ -316,11 +323,14 @@
*/
private void updatePositionsForState(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
- // The y coordinate of the current child.
- float currentYPosition = -algorithmState.scrollY;
+ if (!ambientState.isOnKeyguard()) {
+ algorithmState.mCurrentYPosition += mNotificationScrimPadding;
+ algorithmState.mCurrentExpandedYPosition += mNotificationScrimPadding;
+ }
+
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
- currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
+ updateChild(i, algorithmState, ambientState);
}
}
@@ -340,14 +350,14 @@
StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
- final boolean isShowingShelf = ambientState.getShelf() != null
+ final boolean showingShelf = ambientState.getShelf() != null
&& algorithmState.firstViewInShelf != null;
- final float stackHeight = ambientState.getStackHeight()
- - (isShowingShelf ? ambientState.getShelf().getIntrinsicHeight() : 0f);
+ final float shelfHeight = showingShelf ? ambientState.getShelf().getIntrinsicHeight() : 0f;
+ final float scrimPadding = ambientState.isOnKeyguard() ? 0 : mNotificationScrimPadding;
- float stackEndHeight = ambientState.getStackEndHeight()
- - (isShowingShelf ? ambientState.getShelf().getIntrinsicHeight() : 0f);
+ final float stackHeight = ambientState.getStackHeight() - shelfHeight - scrimPadding;
+ final float stackEndHeight = ambientState.getStackEndHeight() - shelfHeight - scrimPadding;
return stackHeight / stackEndHeight;
}
@@ -360,17 +370,11 @@
* {@link StackScrollAlgorithmState#visibleChildren}.
* @param algorithmState The overall output state of the algorithm.
* @param ambientState The input state provided to the algorithm.
- * @param currentYPosition The Y position of the current pass of the algorithm. For a forward
- * pass, this should be the top of the child; for a reverse pass, the
- * bottom of the child.
- * @return The Y position after laying out the child. This will be the {@code currentYPosition}
- * for the next call to this method, after adjusting for any gaps between children.
*/
- protected float updateChild(
+ protected void updateChild(
int i,
StackScrollAlgorithmState algorithmState,
- AmbientState ambientState,
- float currentYPosition) {
+ AmbientState ambientState) {
ExpandableView view = algorithmState.visibleChildren.get(i);
ExpandableViewState viewState = view.getViewState();
@@ -399,10 +403,11 @@
ambientState.getSectionProvider(), i,
view, getPreviousView(i, algorithmState));
if (applyGapHeight) {
- currentYPosition += expansionFraction * mGapHeight;
+ algorithmState.mCurrentYPosition += expansionFraction * mGapHeight;
+ algorithmState.mCurrentExpandedYPosition += mGapHeight;
}
- viewState.yTranslation = currentYPosition;
+ viewState.yTranslation = algorithmState.mCurrentYPosition;
if (view instanceof SectionHeaderView) {
// Add padding before sections for overscroll effect.
viewState.yTranslation += expansionFraction * ambientState.getSectionPadding();
@@ -411,12 +416,11 @@
if (view instanceof FooterView) {
final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
- final float footerEnd = viewState.yTranslation + view.getIntrinsicHeight();
- final boolean noSpaceForFooter = footerEnd > ambientState.getStackHeight();
+ final float footerEnd = algorithmState.mCurrentExpandedYPosition
+ + view.getIntrinsicHeight();
+ final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
- viewState.hidden = isShelfShowing
- || (!ambientState.isExpansionChanging() && noSpaceForFooter);
-
+ viewState.hidden = isShelfShowing || noSpaceForFooter;
} else if (view != ambientState.getTrackedHeadsUpRow()) {
if (ambientState.isExpansionChanging()) {
// Show all views. Views below the shelf will later be clipped (essentially hidden)
@@ -451,14 +455,16 @@
maxViewHeight = algorithmState.viewHeightBeforeShelf;
}
}
- viewState.height = (int) MathUtils.lerp(maxViewHeight * START_FRACTION, maxViewHeight,
- expansionFraction);
+ viewState.height = (int) (maxViewHeight * expansionFraction);
}
- currentYPosition += viewState.height + expansionFraction * mPaddingBetweenElements;
- setLocation(view.getViewState(), currentYPosition, i);
+ algorithmState.mCurrentYPosition += viewState.height
+ + expansionFraction * mPaddingBetweenElements;
+ algorithmState.mCurrentExpandedYPosition += view.getIntrinsicHeight()
+ + mPaddingBetweenElements;
+
+ setLocation(view.getViewState(), algorithmState.mCurrentYPosition, i);
viewState.yTranslation += ambientState.getStackY();
- return currentYPosition;
}
/**
@@ -731,6 +737,18 @@
* The children from the host view which are not gone.
*/
public final ArrayList<ExpandableView> visibleChildren = new ArrayList<>();
+
+ /**
+ * Y position of the current view during updating children
+ * with expansion factor applied.
+ */
+ private int mCurrentYPosition;
+
+ /**
+ * Y position of the current view during updating children
+ * without applying the expansion factor.
+ */
+ private int mCurrentExpandedYPosition;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 5399094..20e6f60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -38,7 +38,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
-import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -115,7 +114,8 @@
public static final int MODE_ONLY_WAKE = 4;
/**
- * Mode in which fingerprint unlocks the device.
+ * Mode in which fingerprint unlocks the device or passive auth (ie face auth) unlocks the
+ * device while being requested when keyguard is occluded.
*/
public static final int MODE_UNLOCK_COLLAPSING = 5;
@@ -250,16 +250,20 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
@Main Resources resources,
KeyguardBypassController keyguardBypassController, DozeParameters dozeParameters,
- MetricsLogger metricsLogger, DumpManager dumpManager) {
+ MetricsLogger metricsLogger, DumpManager dumpManager,
+ PowerManager powerManager,
+ NotificationMediaManager notificationMediaManager,
+ WakefulnessLifecycle wakefulnessLifecycle,
+ ScreenLifecycle screenLifecycle) {
mContext = context;
- mPowerManager = context.getSystemService(PowerManager.class);
+ mPowerManager = powerManager;
mShadeController = shadeController;
mUpdateMonitor = keyguardUpdateMonitor;
mDozeParameters = dozeParameters;
mUpdateMonitor.registerCallback(this);
- mMediaManager = Dependency.get(NotificationMediaManager.class);
- Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver);
- Dependency.get(ScreenLifecycle.class).addObserver(mScreenObserver);
+ mMediaManager = notificationMediaManager;
+ wakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ screenLifecycle.addObserver(mScreenObserver);
mNotificationShadeWindowController = notificationShadeWindowController;
mDozeScrimController = dozeScrimController;
@@ -352,8 +356,10 @@
Optional.ofNullable(BiometricUiEvent.SUCCESS_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(UI_EVENT_LOGGER::log);
- boolean unlockAllowed = mKeyguardBypassController.onBiometricAuthenticated(
- biometricSourceType, isStrongBiometric);
+ boolean unlockAllowed =
+ mKeyguardStateController.isOccluded()
+ || mKeyguardBypassController.onBiometricAuthenticated(
+ biometricSourceType, isStrongBiometric);
if (unlockAllowed) {
mKeyguardViewMediator.userActivity();
startWakeAndUnlock(biometricSourceType, isStrongBiometric);
@@ -415,7 +421,7 @@
if (!wasDeviceInteractive) {
mPendingShowBouncer = true;
} else {
- showBouncer();
+ mPendingShowBouncer = false;
mKeyguardViewController.notifyKeyguardAuthenticated(
false /* strongAuth */);
}
@@ -578,6 +584,9 @@
if (unlockingAllowed && deviceDreaming) {
return bypass ? MODE_WAKE_AND_UNLOCK_FROM_DREAM : MODE_ONLY_WAKE;
}
+ if (unlockingAllowed && mKeyguardStateController.isOccluded()) {
+ return MODE_UNLOCK_COLLAPSING;
+ }
if (mKeyguardViewController.isShowing()) {
if (mKeyguardViewController.bouncerIsOrWillBeShowing() && unlockingAllowed) {
if (bypass && mKeyguardBypassController.canPlaySubtleWindowAnimations()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 2b51b56..684760e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -23,6 +23,7 @@
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
@@ -80,7 +81,7 @@
private NetworkController mNetworkController;
private LinearLayout mSystemIconArea;
private View mClockView;
- private ViewGroup mOngoingCallChip;
+ private View mOngoingCallChip;
private View mNotificationIconAreaInner;
private View mCenteredIconArea;
private int mDisabled1;
@@ -325,11 +326,13 @@
// Show the ongoing call chip only if there is an ongoing call *and* notification icons
// are allowed. (The ongoing call chip occupies the same area as the notification icons,
// so if the icons are disabled then the call chip should be, too.)
- if (hasOngoingCall && !disableNotifications) {
+ boolean showOngoingCallChip = hasOngoingCall && !disableNotifications;
+ if (showOngoingCallChip) {
showOngoingCallChip(animate);
} else {
hideOngoingCallChip(animate);
}
+ mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip);
}
private boolean shouldHideNotificationIcons() {
@@ -348,7 +351,8 @@
private void showSystemIconArea(boolean animate) {
// Only show the system icon area if we are not currently animating
- if (mAnimationScheduler.getAnimationState() == IDLE) {
+ int state = mAnimationScheduler.getAnimationState();
+ if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
animateShow(mSystemIconArea, animate);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 71ba091..7f919b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -39,6 +39,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
+import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -48,11 +49,13 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
+import android.provider.Settings;
import android.service.media.CameraPrewarmService;
import android.service.quickaccesswallet.GetWalletCardsError;
import android.service.quickaccesswallet.GetWalletCardsRequest;
import android.service.quickaccesswallet.GetWalletCardsResponse;
import android.service.quickaccesswallet.QuickAccessWalletClient;
+import android.service.quickaccesswallet.QuickAccessWalletClientImpl;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -94,6 +97,7 @@
import com.android.systemui.statusbar.policy.PreviewInflater;
import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.wallet.ui.WalletActivity;
import java.util.concurrent.Executor;
@@ -189,6 +193,8 @@
private int mBurnInYOffset;
private ActivityIntentHelper mActivityIntentHelper;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private ContentObserver mWalletPreferenceObserver;
+ private SecureSettings mSecureSettings;
public KeyguardBottomAreaView(Context context) {
this(context, null);
@@ -251,7 +257,6 @@
super.onFinishInflate();
mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext),
new ActivityIntentHelper(mContext));
- mPreviewContainer = findViewById(R.id.preview_container);
mOverlayContainer = findViewById(R.id.overlay_container);
mRightAffordanceView = findViewById(R.id.camera_button);
mLeftAffordanceView = findViewById(R.id.left_button);
@@ -268,7 +273,6 @@
mKeyguardStateController.addCallback(this);
setClipChildren(false);
setClipToPadding(false);
- inflateCameraPreview();
mRightAffordanceView.setOnClickListener(this);
mLeftAffordanceView.setOnClickListener(this);
initAccessibility();
@@ -276,13 +280,21 @@
mFlashlightController = Dependency.get(FlashlightController.class);
mAccessibilityController = Dependency.get(AccessibilityController.class);
mActivityIntentHelper = new ActivityIntentHelper(getContext());
- updateLeftAffordance();
mIndicationPadding = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_area_padding);
updateWalletVisibility();
}
+ /**
+ * Set the container where the previews are rendered.
+ */
+ public void setPreviewContainer(ViewGroup previewContainer) {
+ mPreviewContainer = previewContainer;
+ inflateCameraPreview();
+ updateLeftAffordance();
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -319,6 +331,10 @@
mLeftExtension.destroy();
getContext().unregisterReceiver(mDevicePolicyReceiver);
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+
+ if (mWalletPreferenceObserver != null) {
+ mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver);
+ }
}
private void initAccessibility() {
@@ -560,7 +576,6 @@
}
});
} else {
-
// We need to delay starting the activity because ResolverActivity finishes itself if
// launched behind lockscreen.
mActivityStarter.startActivity(intent, false /* dismissShade */,
@@ -680,6 +695,9 @@
}
private void inflateCameraPreview() {
+ if (mPreviewContainer == null) {
+ return;
+ }
View previewBefore = mCameraPreview;
boolean visibleBefore = false;
if (previewBefore != null) {
@@ -697,6 +715,9 @@
}
private void updateLeftPreview() {
+ if (mPreviewContainer == null) {
+ return;
+ }
View previewBefore = mLeftPreview;
if (previewBefore != null) {
mPreviewContainer.removeView(previewBefore);
@@ -914,22 +935,47 @@
/**
* Initialize the wallet feature, only enabling if the feature is enabled within the platform.
*/
- public void initWallet(QuickAccessWalletClient client, Executor uiExecutor, boolean enabled) {
+ public void initWallet(QuickAccessWalletClient client, Executor uiExecutor,
+ SecureSettings secureSettings) {
mQuickAccessWalletClient = client;
- mWalletEnabled = enabled && client.isWalletFeatureAvailable();
+ mSecureSettings = secureSettings;
+ setupWalletPreferenceObserver();
+ updateWalletPreference();
+
mUiExecutor = uiExecutor;
queryWalletCards();
updateWalletVisibility();
}
+ private void setupWalletPreferenceObserver() {
+ if (mWalletPreferenceObserver == null) {
+ mWalletPreferenceObserver = new ContentObserver(null /* handler */) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mUiExecutor.execute(() -> updateWalletPreference());
+ }
+ };
+
+ mSecureSettings.registerContentObserver(
+ Settings.Secure.getUriFor(QuickAccessWalletClientImpl.SETTING_KEY),
+ false /* notifyForDescendants */,
+ mWalletPreferenceObserver);
+ }
+ }
+
+ private void updateWalletPreference() {
+ mWalletEnabled = mQuickAccessWalletClient.isWalletFeatureAvailable()
+ && mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked();
+ }
+
private void queryWalletCards() {
if (!mWalletEnabled || mUiExecutor == null) {
return;
}
GetWalletCardsRequest request =
new GetWalletCardsRequest(1 /* cardWidth */, 1 /* cardHeight */,
- 1 /* iconSizePx */, 2 /* maxCards */);
+ 1 /* iconSizePx */, 1 /* maxCards */);
mQuickAccessWalletClient.getWalletCards(mUiExecutor, request, mCardRetriever);
}
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 069c197..f4710f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -140,7 +140,7 @@
*/
private float mQsExpansion;
- private float mEmptyDragAmount;
+ private float mOverStretchAmount;
/**
* Setting if bypass is enabled. If true the clock should always be positioned like it's dark
@@ -181,7 +181,7 @@
int notificationStackHeight, float panelExpansion, int parentHeight,
int keyguardStatusHeight, int userSwitchHeight, int clockPreferredY,
int userSwitchPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
- float emptyDragAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
+ float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
float qsExpansion, int cutoutTopInset, boolean isSplitShade) {
mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
userSwitchHeight);
@@ -196,7 +196,7 @@
mHasCustomClock = hasCustomClock;
mHasVisibleNotifs = hasVisibleNotifs;
mDarkAmount = dark;
- mEmptyDragAmount = emptyDragAmount;
+ mOverStretchAmount = overStrechAmount;
mBypassEnabled = bypassEnabled;
mUnlockedStackScrollerPadding = unlockedStackScrollerPadding;
mQsExpansion = qsExpansion;
@@ -301,7 +301,7 @@
}
clockYDark = clockY + burnInPreventionOffsetY() + shift;
}
- return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount);
+ return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount);
}
private int getUserSwitcherY(float panelExpansion) {
@@ -312,7 +312,7 @@
float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(panelExpansion);
float userSwitchY = MathUtils.lerp(userSwitchYBouncer, userSwitchYRegular, shadeExpansion);
- return (int) (userSwitchY + mEmptyDragAmount);
+ return (int) (userSwitchY + mOverStretchAmount);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java
index b11329a..f043fcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java
@@ -25,6 +25,8 @@
* Executes an action that requres the screen to be unlocked, showing the keyguard if
* necessary. Does not close the notification shade (in case it was open).
* @param requiresShadeOpen does the shade need to be forced open when hiding the keyguard?
+ * @param afterKeyguardGone run the dismiss action after keyguard is gone?
*/
- void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen);
+ void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen,
+ boolean afterKeyguardGone);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
index c0181f4..27b68f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
@@ -50,13 +50,14 @@
* @param requiresShadeOpen does the shade need to be forced open when hiding the keyguard?
*/
@Override
- public void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen) {
+ public void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen,
+ boolean afterKeyguardGone) {
KeyguardDismissHandler dismissHandler = mDismissHandler;
if (dismissHandler == null) {
Log.wtf(TAG, "KeyguardDismissHandler not set.");
action.onDismiss();
return;
}
- dismissHandler.executeWhenUnlocked(action, requiresShadeOpen);
+ dismissHandler.executeWhenUnlocked(action, requiresShadeOpen, afterKeyguardGone);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 3181f52..9787a944 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -22,12 +22,12 @@
import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.Log;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -41,17 +41,21 @@
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
* A helper class dealing with the alert interactions between {@link NotificationGroupManagerLegacy}
* and {@link HeadsUpManager}. In particular, this class deals with keeping
- * the correct notification in a group alerting based off the group suppression.
+ * the correct notification in a group alerting based off the group suppression and alertOverride.
*/
public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
StateListener {
private static final long ALERT_TRANSFER_TIMEOUT = 300;
+ private static final String TAG = "NotifGroupAlertTransfer";
+ private static final boolean DEBUG = StatusBar.DEBUG;
+ private static final boolean SPEW = StatusBar.SPEW;
/**
* The list of entries containing group alert metadata for each group. Keyed by group key.
@@ -142,41 +146,98 @@
@Override
public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
- if (suppressed) {
- if (mHeadsUpManager.isAlerting(group.summary.getKey())) {
- handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
- }
- } else {
- // Group summary can be null if we are no longer suppressed because the summary was
- // removed. In that case, we don't need to alert the summary.
- if (group.summary == null) {
- return;
- }
- GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
- group.summary.getSbn()));
- // Group is no longer suppressed. We should check if we need to transfer the alert
- // back to the summary now that it's no longer suppressed.
- if (groupAlertEntry.mAlertSummaryOnNextAddition) {
- if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
- alertNotificationWhenPossible(group.summary, mHeadsUpManager);
- }
- groupAlertEntry.mAlertSummaryOnNextAddition = false;
- } else {
- checkShouldTransferBack(groupAlertEntry);
- }
+ if (DEBUG) {
+ Log.d(TAG, "!! onGroupSuppressionChanged: group.summary=" + group.summary
+ + " suppressed=" + suppressed);
}
+ NotificationEntry oldAlertOverride = group.alertOverride;
+ onGroupChanged(group, oldAlertOverride);
+ }
+
+ @Override
+ public void onGroupAlertOverrideChanged(NotificationGroup group,
+ @Nullable NotificationEntry oldAlertOverride,
+ @Nullable NotificationEntry newAlertOverride) {
+ if (DEBUG) {
+ Log.d(TAG, "!! onGroupAlertOverrideChanged: group.summary=" + group.summary
+ + " oldAlertOverride=" + oldAlertOverride
+ + " newAlertOverride=" + newAlertOverride);
+ }
+ onGroupChanged(group, oldAlertOverride);
}
};
- @Override
- public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
+ /**
+ * Called when either the suppressed or alertOverride fields of the group changed
+ *
+ * @param group the group which changed
+ * @param oldAlertOverride the previous value of group.alertOverride
+ */
+ private void onGroupChanged(NotificationGroup group,
+ NotificationEntry oldAlertOverride) {
+ // Group summary can be null if we are no longer suppressed because the summary was
+ // removed. In that case, we don't need to alert the summary.
+ if (group.summary == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onGroupChanged: summary is null");
+ }
+ return;
+ }
+ if (group.suppressed || group.alertOverride != null) {
+ checkForForwardAlertTransfer(group.summary, oldAlertOverride);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "onGroupChanged: maybe transfer back");
+ }
+ GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
+ group.summary.getSbn()));
+ // Group is no longer suppressed or overridden.
+ // We should check if we need to transfer the alert back to the summary.
+ if (groupAlertEntry.mAlertSummaryOnNextAddition) {
+ if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
+ alertNotificationWhenPossible(group.summary);
+ }
+ groupAlertEntry.mAlertSummaryOnNextAddition = false;
+ } else {
+ checkShouldTransferBack(groupAlertEntry);
+ }
+ }
}
- private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting,
- AlertingNotificationManager alertManager) {
- if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.getSbn())) {
- handleSuppressedSummaryAlerted(entry, alertManager);
+ @Override
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+ if (DEBUG) {
+ Log.d(TAG, "!! onHeadsUpStateChanged: entry=" + entry + " isHeadsUp=" + isHeadsUp);
+ }
+ if (isHeadsUp && entry.getSbn().getNotification().isGroupSummary()) {
+ // a group summary is alerting; trigger the forward transfer checks
+ checkForForwardAlertTransfer(entry, /* oldAlertOverride */ null);
+ }
+ }
+
+ /**
+ * Handles changes in a group's suppression or alertOverride, but where at least one of those
+ * conditions is still true (either the group is suppressed, the group has an alertOverride,
+ * or both). The method determined which kind of child needs to receive the alert, finds the
+ * entry currently alerting, and makes the transfer.
+ *
+ * Internally, this is handled with two main cases: the override needs the alert, or there is
+ * no override but the summary is suppressed (so an isolated child needs the alert).
+ *
+ * @param summary the notification entry of the summary of the logical group.
+ * @param oldAlertOverride the former value of group.alertOverride, before whatever event
+ * required us to check for for a transfer condition.
+ */
+ private void checkForForwardAlertTransfer(NotificationEntry summary,
+ NotificationEntry oldAlertOverride) {
+ if (DEBUG) {
+ Log.d(TAG, "checkForForwardAlertTransfer: enter");
+ }
+ NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
+ if (group != null && group.alertOverride != null) {
+ handleOverriddenSummaryAlerted(summary);
+ } else if (mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())) {
+ handleSuppressedSummaryAlerted(summary, oldAlertOverride);
}
}
@@ -186,9 +247,16 @@
// see as early as we can if we need to abort a transfer.
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
+ if (DEBUG) {
+ Log.d(TAG, "!! onPendingEntryAdded: entry=" + entry);
+ }
String groupKey = mGroupManager.getGroupKey(entry.getSbn());
GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
- if (groupAlertEntry != null) {
+ if (groupAlertEntry != null && groupAlertEntry.mGroup.alertOverride == null) {
+ // new pending group entries require us to transfer back from the child to the
+ // group, but alertOverrides are only present in very limited circumstances, so
+ // while it's possible the group should ALSO alert, the previous detection which set
+ // this alertOverride won't be invalidated by this notification added to this group.
checkShouldTransferBack(groupAlertEntry);
}
}
@@ -262,43 +330,128 @@
}
/**
- * Handles the scenario where a summary that has been suppressed is alerted. A suppressed
+ * Handles the scenario where a summary that has been suppressed is itself, or has a former
+ * alertOverride (in the form of an isolated logical child) which was alerted. A suppressed
* summary should for all intents and purposes be invisible to the user and as a result should
* not alert. When this is the case, it is our responsibility to pass the alert to the
* appropriate child which will be the representative notification alerting for the group.
*
- * @param summary the summary that is suppressed and alerting
- * @param alertManager the alert manager that manages the alerting summary
+ * @param summary the summary that is suppressed and (potentially) alerting
+ * @param oldAlertOverride the alertOverride before whatever event triggered this method. If
+ * the alert override was removed, this will be the entry that should
+ * be transferred back from.
*/
private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
- @NonNull AlertingNotificationManager alertManager) {
- StatusBarNotification sbn = summary.getSbn();
+ NotificationEntry oldAlertOverride) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: summary=" + summary);
+ }
GroupAlertEntry groupAlertEntry =
- mGroupAlertEntries.get(mGroupManager.getGroupKey(sbn));
+ mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
+
if (!mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())
- || !alertManager.isAlerting(sbn.getKey())
|| groupAlertEntry == null) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: invalid state");
+ }
+ return;
+ }
+ boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
+ boolean priorityIsAlerting = oldAlertOverride != null
+ && mHeadsUpManager.isAlerting(oldAlertOverride.getKey());
+ if (!summaryIsAlerting && !priorityIsAlerting) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: no summary or override alerting");
+ }
return;
}
if (pendingInflationsWillAddChildren(groupAlertEntry.mGroup)) {
// New children will actually be added to this group, let's not transfer the alert.
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: pending inflations");
+ }
return;
}
NotificationEntry child =
mGroupManager.getLogicalChildren(summary.getSbn()).iterator().next();
- if (child != null) {
- if (child.getRow().keepInParent()
- || child.isRowRemoved()
- || child.isRowDismissed()) {
- // The notification is actually already removed. No need to alert it.
- return;
+ if (summaryIsAlerting) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: transfer summary -> child");
}
- if (!alertManager.isAlerting(child.getKey()) && onlySummaryAlerts(summary)) {
- groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
+ tryTransferAlertState(summary, /*from*/ summary, /*to*/ child, groupAlertEntry);
+ return;
+ }
+ // Summary didn't have the alert, so we're in "transfer back" territory. First, make sure
+ // it's not too late to transfer back, then transfer the alert from the oldAlertOverride to
+ // the isolated child which should receive the alert.
+ if (!canStillTransferBack(groupAlertEntry)) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: transfer from override: too late");
}
- transferAlertState(summary, child, alertManager);
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: transfer override -> child");
+ }
+ tryTransferAlertState(summary, /*from*/ oldAlertOverride, /*to*/ child, groupAlertEntry);
+ }
+
+ /**
+ * Checks for and handles the scenario where the given entry is the summary of a group which
+ * has an alertOverride, and either the summary itself or one of its logical isolated children
+ * is currently alerting (which happens if the summary is suppressed).
+ */
+ private void handleOverriddenSummaryAlerted(NotificationEntry summary) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: summary=" + summary);
+ }
+ GroupAlertEntry groupAlertEntry =
+ mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
+ NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
+ if (group == null || group.alertOverride == null || groupAlertEntry == null) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: invalid state");
+ }
+ return;
+ }
+ boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
+ if (summaryIsAlerting) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: transfer summary -> override");
+ }
+ tryTransferAlertState(summary, /*from*/ summary, group.alertOverride, groupAlertEntry);
+ return;
+ }
+ // Summary didn't have the alert, so we're in "transfer back" territory. First, make sure
+ // it's not too late to transfer back, then remove the alert from any of the logical
+ // children, and if one of them was alerting, we can alert the override.
+ if (!canStillTransferBack(groupAlertEntry)) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: transfer from child: too late");
+ }
+ return;
+ }
+ List<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.getSbn());
+ if (children == null) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: no children");
+ }
+ return;
+ }
+ children.remove(group.alertOverride); // do not release the alert on our desired destination
+ boolean releasedChild = releaseChildAlerts(children);
+ if (releasedChild) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: transfer child -> override");
+ }
+ tryTransferAlertState(summary, /*from*/ null, group.alertOverride, groupAlertEntry);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: no child alert released");
+ }
}
}
@@ -307,14 +460,37 @@
* immediately to have the incorrect one up as short as possible. The second should alert
* when possible.
*
+ * @param summary entry of the summary
* @param fromEntry entry to transfer alert from
* @param toEntry entry to transfer to
- * @param alertManager alert manager for the alert type
*/
- private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry,
- @NonNull AlertingNotificationManager alertManager) {
- alertManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
- alertNotificationWhenPossible(toEntry, alertManager);
+ private void tryTransferAlertState(
+ NotificationEntry summary,
+ NotificationEntry fromEntry,
+ NotificationEntry toEntry,
+ GroupAlertEntry groupAlertEntry) {
+ if (toEntry != null) {
+ if (toEntry.getRow().keepInParent()
+ || toEntry.isRowRemoved()
+ || toEntry.isRowDismissed()) {
+ // The notification is actually already removed. No need to alert it.
+ return;
+ }
+ if (!mHeadsUpManager.isAlerting(toEntry.getKey()) && onlySummaryAlerts(summary)) {
+ groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
+ }
+ if (DEBUG) {
+ Log.d(TAG, "transferAlertState: fromEntry=" + fromEntry + " toEntry=" + toEntry);
+ }
+ transferAlertState(fromEntry, toEntry);
+ }
+ }
+ private void transferAlertState(@Nullable NotificationEntry fromEntry,
+ @NonNull NotificationEntry toEntry) {
+ if (fromEntry != null) {
+ mHeadsUpManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
+ }
+ alertNotificationWhenPossible(toEntry);
}
/**
@@ -326,11 +502,13 @@
* more children are coming. Thus, if a child is added within a certain timeframe after we
* transfer, we back out and alert the summary again.
*
+ * An alert can only transfer back within a small window of time after a transfer away from the
+ * summary to a child happened.
+ *
* @param groupAlertEntry group alert entry to check
*/
private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
- if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
- < ALERT_TRANSFER_TIMEOUT) {
+ if (canStillTransferBack(groupAlertEntry)) {
NotificationEntry summary = groupAlertEntry.mGroup.summary;
if (!onlySummaryAlerts(summary)) {
@@ -338,30 +516,17 @@
}
ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(
summary.getSbn());
- int numChildren = children.size();
+ int numActiveChildren = children.size();
int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
- numChildren += numPendingChildren;
+ int numChildren = numActiveChildren + numPendingChildren;
if (numChildren <= 1) {
return;
}
- boolean releasedChild = false;
- for (int i = 0; i < children.size(); i++) {
- NotificationEntry entry = children.get(i);
- if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
- releasedChild = true;
- mHeadsUpManager.removeNotification(
- entry.getKey(), true /* releaseImmediately */);
- }
- if (mPendingAlerts.containsKey(entry.getKey())) {
- // This is the child that would've been removed if it was inflated.
- releasedChild = true;
- mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
- }
- }
+ boolean releasedChild = releaseChildAlerts(children);
if (releasedChild && !mHeadsUpManager.isAlerting(summary.getKey())) {
- boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
+ boolean notifyImmediately = numActiveChildren > 1;
if (notifyImmediately) {
- alertNotificationWhenPossible(summary, mHeadsUpManager);
+ alertNotificationWhenPossible(summary);
} else {
// Should wait until the pending child inflates before alerting.
groupAlertEntry.mAlertSummaryOnNextAddition = true;
@@ -371,25 +536,61 @@
}
}
+ private boolean canStillTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
+ return SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
+ < ALERT_TRANSFER_TIMEOUT;
+ }
+
+ private boolean releaseChildAlerts(List<NotificationEntry> children) {
+ boolean releasedChild = false;
+ if (SPEW) {
+ Log.d(TAG, "releaseChildAlerts: numChildren=" + children.size());
+ }
+ for (int i = 0; i < children.size(); i++) {
+ NotificationEntry entry = children.get(i);
+ if (SPEW) {
+ Log.d(TAG, "releaseChildAlerts: checking i=" + i + " entry=" + entry
+ + " onlySummaryAlerts=" + onlySummaryAlerts(entry)
+ + " isAlerting=" + mHeadsUpManager.isAlerting(entry.getKey())
+ + " isPendingAlert=" + mPendingAlerts.containsKey(entry.getKey()));
+ }
+ if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
+ releasedChild = true;
+ mHeadsUpManager.removeNotification(
+ entry.getKey(), true /* releaseImmediately */);
+ }
+ if (mPendingAlerts.containsKey(entry.getKey())) {
+ // This is the child that would've been removed if it was inflated.
+ releasedChild = true;
+ mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
+ }
+ }
+ if (SPEW) {
+ Log.d(TAG, "releaseChildAlerts: didRelease=" + releasedChild);
+ }
+ return releasedChild;
+ }
+
/**
* Tries to alert the notification. If its content view is not inflated, we inflate and continue
* when the entry finishes inflating the view.
*
* @param entry entry to show
- * @param alertManager alert manager for the alert type
*/
- private void alertNotificationWhenPossible(@NonNull NotificationEntry entry,
- @NonNull AlertingNotificationManager alertManager) {
- @InflationFlag int contentFlag = alertManager.getContentFlag();
+ private void alertNotificationWhenPossible(@NonNull NotificationEntry entry) {
+ @InflationFlag int contentFlag = mHeadsUpManager.getContentFlag();
final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
if ((params.getContentViews() & contentFlag) == 0) {
+ if (DEBUG) {
+ Log.d(TAG, "alertNotificationWhenPossible: async requestRebind entry=" + entry);
+ }
mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
params.requireContentViews(contentFlag);
mRowContentBindStage.requestRebind(entry, en -> {
PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.getKey());
if (alertInfo != null) {
if (alertInfo.isStillValid()) {
- alertNotificationWhenPossible(entry, mHeadsUpManager);
+ alertNotificationWhenPossible(entry);
} else {
// The transfer is no longer valid. Free the content.
mRowContentBindStage.getStageParams(entry).markContentViewsFreeable(
@@ -400,10 +601,16 @@
});
return;
}
- if (alertManager.isAlerting(entry.getKey())) {
- alertManager.updateNotification(entry.getKey(), true /* alert */);
+ if (mHeadsUpManager.isAlerting(entry.getKey())) {
+ if (DEBUG) {
+ Log.d(TAG, "alertNotificationWhenPossible: continue alerting entry=" + entry);
+ }
+ mHeadsUpManager.updateNotification(entry.getKey(), true /* alert */);
} else {
- alertManager.showNotification(entry);
+ if (DEBUG) {
+ Log.d(TAG, "alertNotificationWhenPossible: start alerting entry=" + entry);
+ }
+ mHeadsUpManager.showNotification(entry);
}
}
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 c0d713b..9d8a9bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -114,6 +114,7 @@
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -122,6 +123,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -150,6 +152,7 @@
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.util.Utils;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.wm.shell.animation.FlingAnimationUtils;
import java.io.FileDescriptor;
@@ -206,7 +209,6 @@
private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
@VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
new StatusBarStateListener();
- private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
private final BiometricUnlockController mBiometricUnlockController;
private final NotificationPanelView mView;
private final VibratorHelper mVibratorHelper;
@@ -306,14 +308,17 @@
private final QSDetailDisplayer mQSDetailDisplayer;
private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
+ private final PrivacyDotViewController mPrivacyDotViewController;
// Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
// If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
private final int mMaxKeyguardNotifications;
+ private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private boolean mShouldUseSplitNotificationShade;
// Current max allowed keyguard notifications determined by measuring the panel
private int mMaxAllowedKeyguardNotifications;
+ private ViewGroup mPreviewContainer;
private KeyguardAffordanceHelper mAffordanceHelper;
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
@@ -363,7 +368,7 @@
private int mStatusBarMinHeight;
private int mStatusBarHeaderHeightKeyguard;
private int mNotificationsHeaderCollideDistance;
- private float mEmptyDragAmount;
+ private float mOverStretchAmount;
private float mDownX;
private float mDownY;
private int mDisplayCutoutTopInset = 0; // in pixels
@@ -479,7 +484,6 @@
private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final UserManager mUserManager;
- private final ShadeController mShadeController;
private final MediaDataManager mMediaDataManager;
private NotificationShadeDepthController mDepthController;
private int mDisplayId;
@@ -503,6 +507,39 @@
private float mSectionPadding;
/**
+ * The amount of progress we are currently in if we're transitioning to the full shade.
+ * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+ * shade. This value can also go beyond 1.1 when we're overshooting!
+ */
+ private float mTransitioningToFullShadeProgress;
+
+ /**
+ * Position of the qs bottom during the full shade transition. This is needed as the toppadding
+ * can change during state changes, which makes it much harder to do animations
+ */
+ private int mTransitionToFullShadeQSPosition;
+
+ /**
+ * Distance that the full shade transition takes in order for qs to fully transition to the
+ * shade.
+ */
+ private int mDistanceForQSFullShadeTransition;
+
+ /**
+ * The maximum overshoot allowed for the top padding for the full shade transition
+ */
+ private int mMaxOverscrollAmountForDragDown;
+
+ /**
+ * Should we animate the next bounds update
+ */
+ private boolean mAnimateNextNotificationBounds;
+ /**
+ * The delay for the next bounds animation
+ */
+ private long mNotificationBoundsAnimationDelay;
+
+ /**
* Is this a collapse that started on the panel where we should allow the panel to intercept
*/
private boolean mIsPanelCollapseOnQQS;
@@ -519,6 +556,16 @@
private boolean mDelayShowingKeyguardStatusBar;
private boolean mAnimatingQS;
+
+ /**
+ * The end bounds of a clipping animation.
+ */
+ private final Rect mQsClippingAnimationEndBounds = new Rect();
+
+ /**
+ * The animator for the qs clipping bounds.
+ */
+ private ValueAnimator mQsClippingAnimation = null;
private final Rect mKeyguardStatusAreaClipBounds = new Rect();
private int mOldLayoutDirection;
private NotificationShelfController mNotificationShelfController;
@@ -528,6 +575,7 @@
private final QuickAccessWalletClient mQuickAccessWalletClient;
private final Executor mUiExecutor;
+ private final SecureSettings mSecureSettings;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
private KeyguardMediaController mKeyguardMediaController;
@@ -567,7 +615,7 @@
NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
DynamicPrivacyController dynamicPrivacyController,
KeyguardBypassController bypassController, FalsingManager falsingManager,
- FalsingCollector falsingCollector, ShadeController shadeController,
+ FalsingCollector falsingCollector,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationEntryManager notificationEntryManager,
KeyguardStateController keyguardStateController,
@@ -589,6 +637,7 @@
KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
+ LockscreenShadeTransitionController lockscreenShadeTransitionController,
QSDetailDisplayer qsDetailDisplayer,
NotificationGroupManagerLegacy groupManager,
NotificationIconAreaController notificationIconAreaController,
@@ -602,7 +651,9 @@
FeatureFlags featureFlags,
QuickAccessWalletClient quickAccessWalletClient,
KeyguardMediaController keyguardMediaController,
- @Main Executor uiExecutor) {
+ PrivacyDotViewController privacyDotViewController,
+ @Main Executor uiExecutor,
+ SecureSettings secureSettings) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(),
@@ -610,6 +661,7 @@
mView = view;
mVibratorHelper = vibratorHelper;
mKeyguardMediaController = keyguardMediaController;
+ mPrivacyDotViewController = privacyDotViewController;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
mConfigurationController = configurationController;
@@ -653,6 +705,7 @@
mMediaDataManager = mediaDataManager;
mQuickAccessWalletClient = quickAccessWalletClient;
mUiExecutor = uiExecutor;
+ mSecureSettings = secureSettings;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
if (mQs != null) {
mQs.animateHeaderSlidingOut();
@@ -673,6 +726,8 @@
}
}
};
+ mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
+ lockscreenShadeTransitionController.setNotificationPanelController(this);
mKeyguardStateController.addCallback(keyguardMonitorCallback);
DynamicPrivacyControlListener
dynamicPrivacyControlListener =
@@ -686,7 +741,6 @@
});
mBottomAreaShadeAlphaAnimator.setDuration(160);
mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
- mShadeController = shadeController;
mLockscreenUserManager = notificationLockscreenUserManager;
mEntryManager = notificationEntryManager;
mConversationNotificationManager = conversationNotificationManager;
@@ -745,14 +799,22 @@
mOnEmptySpaceClickListener);
addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
+ mPreviewContainer = mView.findViewById(R.id.preview_container);
+ mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
mLastOrientation = mResources.getConfiguration().orientation;
initBottomArea();
mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController);
mQsFrame = mView.findViewById(R.id.qs_frame);
- mPulseExpansionHandler.setUp(
- mNotificationStackScrollLayoutController, mExpansionCallback, mShadeController);
+ mPulseExpansionHandler.setUp(mNotificationStackScrollLayoutController,
+ amount -> {
+ float progress = amount / mView.getHeight();
+ float overstretch = Interpolators.getOvershootInterpolation(progress,
+ (float) mMaxOverscrollAmountForDragDown / mView.getHeight(),
+ 0.2f);
+ setOverStrechAmount(overstretch);
+ });
mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
@Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
@@ -808,6 +870,10 @@
com.android.internal.R.dimen.status_bar_height);
mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
+ mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_qs_transition_distance);
+ mMaxOverscrollAmountForDragDown = mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_max_top_overshoot);
mScrimCornerRadius = mResources.getDimensionPixelSize(
R.dimen.notification_scrim_corner_radius);
mScreenCornerRadius = mResources.getDimensionPixelSize(
@@ -985,6 +1051,7 @@
mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate(
R.layout.keyguard_bottom_area, mView, false);
mKeyguardBottomArea.initFrom(oldBottomArea);
+ mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
mView.addView(mKeyguardBottomArea, index);
initBottomArea();
mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
@@ -1029,8 +1096,10 @@
mKeyguardBottomArea.setStatusBar(mStatusBar);
mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
mKeyguardBottomArea.setFalsingManager(mFalsingManager);
- mKeyguardBottomArea.initWallet(mQuickAccessWalletClient, mUiExecutor,
- mFeatureFlags.isQuickAccessWalletEnabled());
+
+ if (mFeatureFlags.isQuickAccessWalletEnabled()) {
+ mKeyguardBottomArea.initWallet(mQuickAccessWalletClient, mUiExecutor, mSecureSettings);
+ }
}
private void updateMaxDisplayedNotifications(boolean recompute) {
@@ -1104,58 +1173,28 @@
* showing.
*/
private void positionClockAndNotifications() {
+ positionClockAndNotifications(false /* forceUpdate */);
+ }
+
+ /**
+ * Positions the clock and notifications dynamically depending on how many notifications are
+ * showing.
+ *
+ * @param forceClockUpdate Should the clock be updated even when not on keyguard
+ */
+ private void positionClockAndNotifications(boolean forceClockUpdate) {
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
- boolean animateClock = animate || mAnimateNextPositionUpdate;
int stackScrollerPadding;
- if (mBarState != KEYGUARD) {
+ boolean onKeyguard = isOnKeyguard();
+ if (onKeyguard || forceClockUpdate) {
+ updateClockAppearance();
+ }
+ if (!onKeyguard) {
stackScrollerPadding = getUnlockedStackScrollerPadding();
} else {
- int totalHeight = mView.getHeight();
- int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
- int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight);
- int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
- boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
- final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
- .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
- mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
- int userIconHeight = mKeyguardQsUserSwitchController != null
- ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
- mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
- totalHeight - bottomPadding,
- mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
- getExpandedFraction(),
- totalHeight,
- mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1
- ? mKeyguardStatusViewController.getHeight()
- : (int) (mKeyguardStatusViewController.getHeight()
- - mShelfHeight / 2.0f - mDarkIconSize / 2.0f),
- userIconHeight,
- clockPreferredY, userSwitcherPreferredY, hasCustomClock(),
- hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
- bypassEnabled, getUnlockedStackScrollerPadding(),
- getQsExpansionFraction(),
- mDisplayCutoutTopInset,
- shouldUseSplitNotificationShade(mFeatureFlags, mResources));
- mClockPositionAlgorithm.run(mClockPositionResult);
- mKeyguardStatusViewController.updatePosition(
- mClockPositionResult.clockX, mClockPositionResult.clockY,
- mClockPositionResult.clockScale, animateClock);
- if (mKeyguardQsUserSwitchController != null) {
- mKeyguardQsUserSwitchController.updatePosition(
- mClockPositionResult.clockX,
- mClockPositionResult.userSwitchY,
- animateClock);
- }
- if (mKeyguardUserSwitcherController != null) {
- mKeyguardUserSwitcherController.updatePosition(
- mClockPositionResult.clockX,
- mClockPositionResult.userSwitchY,
- animateClock);
- }
- updateNotificationTranslucency();
- updateClock();
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
}
+
mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
@@ -1165,6 +1204,60 @@
mAnimateNextPositionUpdate = false;
}
+ private void updateClockAppearance() {
+ int totalHeight = mView.getHeight();
+ int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
+ int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight);
+ int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
+ boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
+ final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+ .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
+ mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
+ int userIconHeight = mKeyguardQsUserSwitchController != null
+ ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
+ float expandedFraction =
+ mKeyguardStatusViewController.isAnimatingScreenOffFromUnlocked() ? 1.0f
+ : getExpandedFraction();
+ float darkamount = mKeyguardStatusViewController.isAnimatingScreenOffFromUnlocked() ? 1.0f
+ : mInterpolatedDarkAmount;
+ mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
+ totalHeight - bottomPadding,
+ mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
+ expandedFraction,
+ totalHeight,
+ mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1
+ ? mKeyguardStatusViewController.getHeight()
+ : (int) (mKeyguardStatusViewController.getHeight()
+ - mShelfHeight / 2.0f - mDarkIconSize / 2.0f),
+ userIconHeight,
+ clockPreferredY, userSwitcherPreferredY, hasCustomClock(),
+ hasVisibleNotifications, darkamount, mOverStretchAmount,
+ bypassEnabled, getUnlockedStackScrollerPadding(),
+ computeQsExpansionFraction(),
+ mDisplayCutoutTopInset,
+ shouldUseSplitNotificationShade(mFeatureFlags, mResources));
+ mClockPositionAlgorithm.run(mClockPositionResult);
+ boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
+ boolean animateClock = animate || mAnimateNextPositionUpdate;
+ mKeyguardStatusViewController.updatePosition(
+ mClockPositionResult.clockX, mClockPositionResult.clockY,
+ mClockPositionResult.clockScale, animateClock);
+ if (mKeyguardQsUserSwitchController != null) {
+ mKeyguardQsUserSwitchController.updatePosition(
+ mClockPositionResult.clockX,
+ mClockPositionResult.userSwitchY,
+ animateClock);
+ }
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.updatePosition(
+ mClockPositionResult.clockX,
+ mClockPositionResult.userSwitchY,
+ animateClock);
+ }
+ updateNotificationTranslucency();
+ updateClock();
+ }
+
/**
* @return the padding of the stackscroller when unlocked
*/
@@ -1596,7 +1689,7 @@
private boolean flingExpandsQs(float vel) {
if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
- return getQsExpansionFraction() > 0.5f;
+ return computeQsExpansionFraction() > 0.5f;
} else {
return vel > 0;
}
@@ -1609,7 +1702,7 @@
return !mQsTouchAboveFalsingThreshold;
}
- private float getQsExpansionFraction() {
+ private float computeQsExpansionFraction() {
return Math.min(
1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight
- mQsMinExpansionHeight));
@@ -1834,7 +1927,7 @@
mQsTracking = false;
mTrackingPointer = -1;
trackMovement(event);
- float fraction = getQsExpansionFraction();
+ float fraction = computeQsExpansionFraction();
if (fraction != 0f || y >= mInitialTouchY) {
flingQsWithCurrentVelocity(y,
event.getActionMasked() == MotionEvent.ACTION_CANCEL);
@@ -1895,6 +1988,7 @@
mKeyguardBypassController.setQSExpanded(expanded);
mStatusBarKeyguardViewManager.setQsExpanded(expanded);
mLockIconViewController.setQsExpanded(expanded);
+ mPrivacyDotViewController.setQsExpanded(expanded);
}
}
@@ -2039,18 +2133,19 @@
protected void updateQsExpansion() {
if (mQs == null) return;
- float qsExpansionFraction = getQsExpansionFraction();
+ float qsExpansionFraction = computeQsExpansionFraction();
mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
+ setQSClippingBounds();
mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
mDepthController.setQsPanelExpansion(qsExpansionFraction);
}
private Runnable mOnStackYChanged = () -> {
if (mQs != null) {
- setNotificationBounds();
+ setQSClippingBounds();
}
};
@@ -2058,58 +2153,121 @@
* Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade
* and QS state.
*/
- private void setNotificationBounds() {
+ private void setQSClippingBounds() {
int top = 0;
int bottom = 0;
int left = 0;
int right = 0;
- final int qsPanelBottomY = calculateQsBottomPosition(getQsExpansionFraction());
- final boolean visible = (getQsExpansionFraction() > 0 || qsPanelBottomY > 0)
+ final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
+ final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0)
&& !mShouldUseSplitNotificationShade;
- final float notificationTop = mAmbientState.getStackY()
- - mNotificationScrimPadding
- - mAmbientState.getScrollY();
setQsExpansionEnabled(mAmbientState.getScrollY() == 0);
- int radius = mScrimCornerRadius;
if (!mShouldUseSplitNotificationShade) {
- top = (int) Math.min(qsPanelBottomY, notificationTop);
+ if (mTransitioningToFullShadeProgress > 0.0f) {
+ // If we're transitioning, let's use the actual value. The else case
+ // can be wrong during transitions when waiting for the keyguard to unlock
+ top = mTransitionToFullShadeQSPosition;
+ } else {
+ float notificationTop = getQSEdgePosition();
+ top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop)
+ : notificationTop);
+ }
bottom = getView().getBottom();
left = getView().getLeft();
right = getView().getRight();
- radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
- Math.min(top / (float) mScrimCornerRadius, 1f));
} else if (qsPanelBottomY > 0) { // so bounds are empty on lockscreen
top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
bottom = mNotificationStackScrollLayoutController.getHeight();
left = mNotificationStackScrollLayoutController.getLeft();
right = mNotificationStackScrollLayoutController.getRight();
}
+ applyQSClippingBounds(left, top, right, bottom, visible);
+ }
- // Fancy clipping for quick settings
- if (mQs != null) {
- mQs.setFancyClipping(top, bottom, radius, visible);
+ private void applyQSClippingBounds(int left, int top, int right, int bottom,
+ boolean visible) {
+ if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
+ if (mQsClippingAnimation != null) {
+ // update the end position of the animator
+ mQsClippingAnimationEndBounds.set(left, top, right, bottom);
+ } else {
+ applyQSClippingImmediately(left, top, right, bottom, visible);
+ }
+ } else {
+ mQsClippingAnimationEndBounds.set(left, top, right, bottom);
+ final int startLeft = mKeyguardStatusAreaClipBounds.left;
+ final int startTop = mKeyguardStatusAreaClipBounds.top;
+ final int startRight = mKeyguardStatusAreaClipBounds.right;
+ final int startBottom = mKeyguardStatusAreaClipBounds.bottom;
+ mQsClippingAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mQsClippingAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mQsClippingAnimation.setDuration(
+ StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
+ mQsClippingAnimation.setStartDelay(mNotificationBoundsAnimationDelay);
+ mQsClippingAnimation.addUpdateListener(animation -> {
+ float fraction = animation.getAnimatedFraction();
+ int animLeft = (int) MathUtils.lerp(startLeft,
+ mQsClippingAnimationEndBounds.left, fraction);
+ int animTop = (int) MathUtils.lerp(startTop,
+ mQsClippingAnimationEndBounds.top, fraction);
+ int animRight = (int) MathUtils.lerp(startRight,
+ mQsClippingAnimationEndBounds.right, fraction);
+ int animBottom = (int) MathUtils.lerp(startBottom,
+ mQsClippingAnimationEndBounds.bottom, fraction);
+ applyQSClippingImmediately(animLeft, animTop, animRight, animBottom,
+ visible /* visible */);
+ });
+ mQsClippingAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mQsClippingAnimation = null;
+ }
+ });
+ mQsClippingAnimation.start();
}
+ mAnimateNextNotificationBounds = false;
+ mNotificationBoundsAnimationDelay = 0;
+ }
+
+ private void applyQSClippingImmediately(int left, int top, int right, int bottom,
+ boolean visible) {
+ // Fancy clipping for quick settings
+ int radius = mScrimCornerRadius;
if (!mShouldUseSplitNotificationShade) {
// The padding on this area is large enough that we can use a cheaper clipping strategy
mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
mKeyguardStatusViewController.setClipBounds(visible
? mKeyguardStatusAreaClipBounds : null);
+ radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
+ Math.min(top / (float) mScrimCornerRadius, 1f));
+ }
+ if (mQs != null) {
+ mQs.setFancyClipping(top, bottom, radius, visible);
}
mScrimController.setNotificationsBounds(left, top, right, bottom);
mScrimController.setScrimCornerRadius(radius);
}
+ private float getQSEdgePosition() {
+ // TODO: replace StackY with unified calculation
+ return mAmbientState.getStackY() - mAmbientState.getScrollY();
+ }
+
private int calculateQsBottomPosition(float qsExpansionFraction) {
- int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
- if (qsExpansionFraction != 0.0) {
- qsBottomY = (int) MathUtils.lerp(
- qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction);
+ if (mTransitioningToFullShadeProgress > 0.0f) {
+ return mTransitionToFullShadeQSPosition;
+ } else {
+ int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
+ if (qsExpansionFraction != 0.0) {
+ qsBottomY = (int) MathUtils.lerp(
+ qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction);
+ }
+ // to account for shade overshooting animation, see setSectionPadding method
+ if (mSectionPadding > 0) qsBottomY += mSectionPadding;
+ return qsBottomY;
}
- // to account for shade overshooting animation, see setSectionPadding method
- if (mSectionPadding > 0) qsBottomY += mSectionPadding;
- return qsBottomY;
}
private String determineAccessibilityPaneTitle() {
@@ -2153,7 +2311,7 @@
// from a scrolled quick settings.
return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
(float) (mQsMaxExpansionHeight + mQsNotificationTopPadding),
- getQsExpansionFraction());
+ computeQsExpansionFraction());
} else {
return mQsExpansionHeight + mQsNotificationTopPadding;
}
@@ -2190,15 +2348,65 @@
}
}
-
private void updateQSPulseExpansion() {
if (mQs != null) {
- mQs.setShowCollapsedOnKeyguard(
+ mQs.setPulseExpanding(
mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()
&& mNotificationStackScrollLayoutController.isPulseExpanding());
}
}
+ /**
+ * Set the amount of pixels we have currently dragged down if we're transitioning to the full
+ * shade. 0.0f means we're not transitioning yet.
+ */
+ public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) {
+ mAnimateNextNotificationBounds = animate && !mShouldUseSplitNotificationShade;
+ mNotificationBoundsAnimationDelay = delay;
+ float progress = MathUtils.saturate(pxAmount / mView.getHeight());
+
+ float endPosition = 0;
+ if (pxAmount > 0.0f) {
+ if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0
+ && !mMediaDataManager.hasActiveMedia()) {
+ // No notifications are visible, let's animate to the height of qs instead
+ if (mQs != null) {
+ // Let's interpolate to the header height
+ endPosition = mQs.getHeader().getHeight();
+ }
+ } else {
+ // Interpolating to the new bottom edge position!
+ endPosition = getQSEdgePosition() - mOverStretchAmount;
+
+ // If we have media, we need to put the boundary below it, as the media header
+ // still uses the space during the transition.
+ endPosition +=
+ mNotificationStackScrollLayoutController.getFullShadeTransitionInset();
+ }
+ }
+
+ // Calculate the overshoot amount such that we're reaching the target after our desired
+ // distance, but only reach it fully once we drag a full shade length.
+ float transitionProgress = 0;
+ if (endPosition != 0 && progress != 0) {
+ transitionProgress = Interpolators.getOvershootInterpolation(progress,
+ mMaxOverscrollAmountForDragDown / endPosition,
+ (float) mDistanceForQSFullShadeTransition / (float) mView.getHeight());
+ }
+ mTransitioningToFullShadeProgress = transitionProgress;
+
+ int position = (int) MathUtils.lerp((float) 0, endPosition,
+ mTransitioningToFullShadeProgress);
+ if (mTransitioningToFullShadeProgress > 0.0f) {
+ // we want at least 1 pixel otherwise the panel won't be clipped
+ position = Math.max(1, position);
+ }
+ float overStretchAmount = Math.max(position - endPosition, 0.0f);
+ setOverStrechAmount(overStretchAmount);
+ mTransitionToFullShadeQSPosition = position;
+ updateQsExpansion();
+ }
+
private void trackMovement(MotionEvent event) {
if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
}
@@ -2622,7 +2830,7 @@
if (!mKeyguardShowing) {
return;
}
- float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
+ float alphaQsExpansion = 1 - Math.min(1, computeQsExpansionFraction() * 2);
float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
* mKeyguardStatusBarAnimateAlpha;
newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
@@ -2645,7 +2853,7 @@
float expansionAlpha = MathUtils.map(
isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
getExpandedFraction());
- float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
+ float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
alpha *= mBottomAreaShadeAlpha;
mKeyguardBottomArea.setAffordanceAlpha(alpha);
mKeyguardBottomArea.setImportantForAccessibility(
@@ -2667,7 +2875,7 @@
float expansionAlpha = MathUtils.map(
isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
getExpandedFraction());
- float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
+ float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
mBigClockContainer.setAlpha(alpha);
}
@@ -3219,6 +3427,7 @@
mHeightListener.onQsHeightChanged();
}
});
+ mLockscreenShadeTransitionController.setQS(mQs);
mNotificationStackScrollLayoutController.setQsContainer((ViewGroup) mQs.getView());
updateQsExpansion();
}
@@ -3456,9 +3665,9 @@
StatusBar statusBar,
NotificationShelfController notificationShelfController) {
setStatusBar(statusBar);
- mNotificationStackScrollLayoutController.setNotificationPanelController(this);
mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
mNotificationShelfController = notificationShelfController;
+ mLockscreenShadeTransitionController.bindController(notificationShelfController);
updateMaxDisplayedNotifications(true);
}
@@ -3509,10 +3718,6 @@
return new OnLayoutChangeListener();
}
- public void setEmptyDragAmount(float amount) {
- mExpansionCallback.setEmptyDragAmount(amount);
- }
-
@Override
protected TouchHandler createTouchHandler() {
return new TouchHandler() {
@@ -3981,7 +4186,7 @@
mClockPositionResult.clockX,
mClockPositionResult.clockYFullyDozing,
mClockPositionResult.clockScale,
- false);
+ false /* animate */);
}
mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
@@ -3997,11 +4202,7 @@
if (oldState == KEYGUARD && (goingToFullShade
|| statusBarState == StatusBarState.SHADE_LOCKED)) {
animateKeyguardStatusBarOut();
- long
- delay =
- mBarState == StatusBarState.SHADE_LOCKED ? 0
- : mKeyguardStateController.calculateGoingToFullShadeDelay();
- mQs.animateHeaderSlidingIn(delay);
+ updateQSMinHeight();
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == KEYGUARD) {
animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
@@ -4048,11 +4249,12 @@
}
}
- private class ExpansionCallback implements PulseExpansionHandler.ExpansionCallback {
- public void setEmptyDragAmount(float amount) {
- mEmptyDragAmount = amount * 0.2f;
- positionClockAndNotifications();
- }
+ /**
+ * Sets the overstretch amount in raw pixels when dragging down.
+ */
+ public void setOverStrechAmount(float amount) {
+ mOverStretchAmount = amount;
+ positionClockAndNotifications(true /* forceUpdate */);
}
private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener {
@@ -4067,6 +4269,7 @@
// force a call to onThemeChanged
mConfigurationListener.onThemeChanged();
mFalsingManager.addTapListener(mFalsingTapListener);
+ mKeyguardIndicationController.init();
}
@Override
@@ -4098,11 +4301,7 @@
// Calculate quick setting heights.
int oldMaxHeight = mQsMaxExpansionHeight;
if (mQs != null) {
- float previousMin = mQsMinExpansionHeight;
- mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
- if (mQsExpansionHeight == previousMin) {
- mQsExpansionHeight = mQsMinExpansionHeight;
- }
+ updateQSMinHeight();
mQsMaxExpansionHeight = mQs.getDesiredHeight();
mNotificationStackScrollLayoutController.setMaxTopPadding(
mQsMaxExpansionHeight + mQsNotificationTopPadding);
@@ -4144,6 +4343,14 @@
}
}
+ private void updateQSMinHeight() {
+ float previousMin = mQsMinExpansionHeight;
+ mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
+ if (mQsExpansionHeight == previousMin) {
+ mQsExpansionHeight = mQsMinExpansionHeight;
+ }
+ }
+
private class DebugDrawable extends Drawable {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 388d72d..ae018ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -61,7 +61,6 @@
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
@@ -85,7 +84,7 @@
private final LayoutParams mLpChanged;
private final boolean mKeyguardScreenRotation;
private final long mLockScreenDisplayTimeout;
- private final Display.Mode mKeyguardDisplayMode;
+ private final float mKeyguardRefreshRate;
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardBypassController mKeyguardBypassController;
private ViewGroup mNotificationShadeView;
@@ -135,14 +134,8 @@
// Running on the highest frame rate available can be expensive.
// Let's specify a preferred refresh rate, and allow higher FPS only when we
// know that we're not falsing (because we unlocked.)
- int keyguardRefreshRate = context.getResources()
+ mKeyguardRefreshRate = context.getResources()
.getInteger(R.integer.config_keyguardRefreshRate);
- // Find supported display mode with the same resolution and requested refresh rate.
- mKeyguardDisplayMode = Arrays.stream(supportedModes).filter(mode ->
- (int) mode.getRefreshRate() == keyguardRefreshRate
- && mode.getPhysicalWidth() == currentMode.getPhysicalWidth()
- && mode.getPhysicalHeight() == currentMode.getPhysicalHeight())
- .findFirst().orElse(null);
}
/**
@@ -273,16 +266,17 @@
mLpChanged.privateFlags &= ~LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
}
- if (mKeyguardDisplayMode != null) {
+ if (mKeyguardRefreshRate > 0) {
boolean bypassOnKeyguard = mKeyguardBypassController.getBypassEnabled()
&& state.mStatusBarState == StatusBarState.KEYGUARD
&& !state.mKeyguardFadingAway && !state.mKeyguardGoingAway;
if (state.mDozing || bypassOnKeyguard) {
- mLpChanged.preferredDisplayModeId = mKeyguardDisplayMode.getModeId();
+ mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardRefreshRate;
} else {
- mLpChanged.preferredDisplayModeId = 0;
+ mLpChanged.preferredMaxDisplayRefreshRate = 0;
}
- Trace.setCounter("display_mode_id", mLpChanged.preferredDisplayModeId);
+ Trace.setCounter("display_max_refresh_rate",
+ (long) mLpChanged.preferredMaxDisplayRefreshRate);
}
}
@@ -669,7 +663,7 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(TAG + ":");
- pw.println(" mKeyguardDisplayMode=" + mKeyguardDisplayMode);
+ pw.println(" mKeyguardRefreshRate=" + mKeyguardRefreshRate);
pw.println(mCurrentState);
if (mNotificationShadeView != null && mNotificationShadeView.getViewRootImpl() != null) {
mNotificationShadeView.getViewRootImpl().dump(" ", pw);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 4d70237..7f4dabd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -34,15 +34,14 @@
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeLog;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -73,7 +72,6 @@
private final DynamicPrivacyController mDynamicPrivacyController;
private final KeyguardBypassController mBypassController;
private final PluginManager mPluginManager;
- private final FalsingManager mFalsingManager;
private final FalsingCollector mFalsingCollector;
private final TunerService mTunerService;
private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@@ -87,6 +85,7 @@
private final ShadeController mShadeController;
private final NotificationShadeDepthController mDepthController;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+ private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private GestureDetector mGestureDetector;
@@ -119,7 +118,7 @@
PulseExpansionHandler pulseExpansionHandler,
DynamicPrivacyController dynamicPrivacyController,
KeyguardBypassController bypassController,
- FalsingManager falsingManager,
+ LockscreenShadeTransitionController transitionController,
FalsingCollector falsingCollector,
PluginManager pluginManager,
TunerService tunerService,
@@ -143,7 +142,7 @@
mPulseExpansionHandler = pulseExpansionHandler;
mDynamicPrivacyController = dynamicPrivacyController;
mBypassController = bypassController;
- mFalsingManager = falsingManager;
+ mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mPluginManager = pluginManager;
mTunerService = tunerService;
@@ -406,12 +405,7 @@
}
});
- ExpandHelper.Callback expandHelperCallback = mStackScrollLayout.getExpandHelperCallback();
- DragDownHelper.DragDownCallback dragDownCallback = mStackScrollLayout.getDragDownCallback();
- setDragDownHelper(
- new DragDownHelper(
- mView.getContext(), mView, expandHelperCallback,
- dragDownCallback, mFalsingManager, mFalsingCollector));
+ setDragDownHelper(mLockscreenShadeTransitionController.getTouchHelper());
mDepthController.setRoot(mView);
mNotificationPanelViewController.addExpansionListener(mDepthController);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index c34fa2f..b94b5d3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -28,6 +28,7 @@
import android.os.Trace;
import android.util.Log;
import android.util.MathUtils;
+import android.util.Pair;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
@@ -42,10 +43,10 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
@@ -98,6 +99,18 @@
public static final int OPAQUE = 2;
private boolean mClipsQsScrim;
+ /**
+ * The amount of progress we are currently in if we're transitioning to the full shade.
+ * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+ * shade.
+ */
+ private float mTransitionToFullShadeProgress;
+
+ /**
+ * If we're currently transitioning to the full shade.
+ */
+ private boolean mTransitioningToFullShade;
+
@IntDef(prefix = {"VISIBILITY_"}, value = {
TRANSPARENT,
SEMI_TRANSPARENT,
@@ -357,7 +370,7 @@
+ mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
+ mNotificationsAlpha);
}
- applyExpansionToAlpha();
+ applyStateToAlpha();
// Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
// We need to disable focus otherwise AOD would end up with a gray overlay.
@@ -499,17 +512,47 @@
if (!(relevantState && mExpansionAffectsAlpha)) {
return;
}
- applyAndDispatchExpansion();
+ applyAndDispatchState();
}
}
/**
+ * Set the amount of progress we are currently in if we're transitioning to the full shade.
+ * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+ * shade.
+ */
+ public void setTransitionToFullShadeProgress(float progress) {
+ if (progress != mTransitionToFullShadeProgress) {
+ mTransitionToFullShadeProgress = progress;
+ setTransitionToFullShade(progress > 0.0f);
+ applyAndDispatchState();
+ }
+ }
+
+ /**
+ * Set if we're currently transitioning to the full shade
+ */
+ private void setTransitionToFullShade(boolean transitioning) {
+ if (transitioning != mTransitioningToFullShade) {
+ mTransitioningToFullShade = transitioning;
+ if (transitioning) {
+ // Let's make sure the shade locked is ready
+ ScrimState.SHADE_LOCKED.prepare(mState);
+ }
+ }
+ }
+
+
+ /**
* Set bounds for notifications background, all coordinates are absolute
*/
public void setNotificationsBounds(float left, float top, float right, float bottom) {
- mNotificationsScrim.setDrawableBounds(left, top, right, bottom);
if (mClipsQsScrim) {
+ // "top - 1" to have 1 px of scrims overlap, see: b/186644628
+ mNotificationsScrim.setDrawableBounds(left, top - 1, right, bottom);
mScrimBehind.setBottomEdgePosition((int) top);
+ } else {
+ mNotificationsScrim.setDrawableBounds(left, top, right, bottom);
}
}
@@ -534,7 +577,7 @@
if (!(relevantState && mExpansionAffectsAlpha)) {
return;
}
- applyAndDispatchExpansion();
+ applyAndDispatchState();
}
}
@@ -553,6 +596,11 @@
if (mScrimBehind != null) {
mScrimBehind.enableBottomEdgeConcave(mClipsQsScrim);
}
+ if (mState != ScrimState.UNINITIALIZED) {
+ // the clipScrimState has changed, let's reprepare ourselves
+ mState.prepare(mState);
+ applyAndDispatchState();
+ }
}
@VisibleForTesting
@@ -583,7 +631,7 @@
}
}
- private void applyExpansionToAlpha() {
+ private void applyStateToAlpha() {
if (!mExpansionAffectsAlpha) {
return;
}
@@ -608,47 +656,40 @@
mInFrontAlpha = 0;
} else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED
|| mState == ScrimState.PULSING) {
- // Either darken of make the scrim transparent when you
- // pull down the shade
- float interpolatedFract = getInterpolatedFraction();
- float stateBehind = mClipsQsScrim ? mState.getNotifAlpha() : mState.getBehindAlpha();
- float backAlpha;
- if (mDarkenWhileDragging) {
- backAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind,
- interpolatedFract);
- } else {
- backAlpha = MathUtils.lerp(0 /* start */, stateBehind,
- interpolatedFract);
+ Pair<Integer, Float> result = calculateBackStateForState(mState);
+ int behindTint = result.first;
+ float behindAlpha = result.second;
+ if (mTransitionToFullShadeProgress > 0.0f) {
+ Pair<Integer, Float> shadeResult = calculateBackStateForState(
+ ScrimState.SHADE_LOCKED);
+ behindAlpha = MathUtils.lerp(behindAlpha, shadeResult.second,
+ mTransitionToFullShadeProgress);
+ behindTint = ColorUtils.blendARGB(behindTint, shadeResult.first,
+ mTransitionToFullShadeProgress);
}
mInFrontAlpha = mState.getFrontAlpha();
- int backTint;
if (mClipsQsScrim) {
- backTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(),
- mState.getNotifTint(), interpolatedFract);
- } else {
- backTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
- mState.getBehindTint(), interpolatedFract);
- }
- if (mQsExpansion > 0) {
- backAlpha = MathUtils.lerp(backAlpha, mDefaultScrimAlpha, mQsExpansion);
- int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint()
- : ScrimState.SHADE_LOCKED.getBehindTint();
- backTint = ColorUtils.blendARGB(backTint, stateTint, mQsExpansion);
- }
- if (mClipsQsScrim) {
- mNotificationsAlpha = backAlpha;
- mNotificationsTint = backTint;
+ mNotificationsAlpha = behindAlpha;
+ mNotificationsTint = behindTint;
mBehindAlpha = 1;
mBehindTint = Color.BLACK;
} else {
- mBehindAlpha = backAlpha;
+ mBehindAlpha = behindAlpha;
if (mState == ScrimState.SHADE_LOCKED) {
// going from KEYGUARD to SHADE_LOCKED state
mNotificationsAlpha = getInterpolatedFraction();
} else {
mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion);
}
- mBehindTint = backTint;
+ if (mState == ScrimState.KEYGUARD && mTransitionToFullShadeProgress > 0.0f) {
+ // Interpolate the notification alpha when transitioning!
+ mNotificationsAlpha = MathUtils.lerp(
+ mNotificationsAlpha,
+ getInterpolatedFraction(),
+ mTransitionToFullShadeProgress);
+ }
+ mNotificationsTint = mState.getNotifTint();
+ mBehindTint = behindTint;
}
}
if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
@@ -658,8 +699,39 @@
}
}
- private void applyAndDispatchExpansion() {
- applyExpansionToAlpha();
+ private Pair<Integer, Float> calculateBackStateForState(ScrimState state) {
+ // Either darken of make the scrim transparent when you
+ // pull down the shade
+ float interpolatedFract = getInterpolatedFraction();
+ float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha();
+ float behindAlpha;
+ int behindTint;
+ if (mDarkenWhileDragging) {
+ behindAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind,
+ interpolatedFract);
+ } else {
+ behindAlpha = MathUtils.lerp(0 /* start */, stateBehind,
+ interpolatedFract);
+ }
+ if (mClipsQsScrim) {
+ behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(),
+ state.getNotifTint(), interpolatedFract);
+ } else {
+ behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
+ state.getBehindTint(), interpolatedFract);
+ }
+ if (mQsExpansion > 0) {
+ behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion);
+ int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint()
+ : ScrimState.SHADE_LOCKED.getBehindTint();
+ behindTint = ColorUtils.blendARGB(behindTint, stateTint, mQsExpansion);
+ }
+ return new Pair<>(behindTint, behindAlpha);
+ }
+
+
+ private void applyAndDispatchState() {
+ applyStateToAlpha();
if (mUpdatePending) {
return;
}
@@ -1195,7 +1267,7 @@
public void setExpansionAffectsAlpha(boolean expansionAffectsAlpha) {
mExpansionAffectsAlpha = expansionAffectsAlpha;
if (expansionAffectsAlpha) {
- applyAndDispatchExpansion();
+ applyAndDispatchState();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 1469cda..35dda44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -96,11 +96,14 @@
AUTH_SCRIMMED {
@Override
public void prepare(ScrimState previousState) {
- mFrontTint = Color.BLACK;
+ mNotifTint = previousState.mNotifTint;
+ mNotifAlpha = previousState.mNotifAlpha;
- mBehindAlpha = 0f;
+ mBehindTint = previousState.mBehindTint;
+ mBehindAlpha = previousState.mBehindAlpha;
+
+ mFrontTint = Color.BLACK;
mFrontAlpha = .66f;
- mBubbleAlpha = 0f;
}
},
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index 2fa6795..2d41e5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -14,8 +14,6 @@
package com.android.systemui.statusbar.phone;
-import android.view.View;
-
import com.android.systemui.statusbar.StatusBarState;
/**
@@ -78,15 +76,6 @@
void runPostCollapseRunnables();
/**
- * If secure with redaction: Show bouncer, go to unlocked shade.
- *
- * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
- *
- * @param startingChild The view to expand after going to the shade.
- */
- void goToLockedShade(View startingChild);
-
- /**
* Close the shade if it was open
*
* @return true if the shade was open, else false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index a930a89..d4458e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import android.util.Log;
-import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
@@ -182,12 +181,6 @@
}
@Override
- public void goToLockedShade(View startingChild) {
- // TODO: Move this code out of StatusBar into ShadeController.
- getStatusBar().goToLockedShade(startingChild);
- }
-
- @Override
public boolean collapsePanel() {
if (!getNotificationPanelViewController().isFullyCollapsed()) {
// close the shade if it was open
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 cae7e35..9c6d623 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -194,6 +194,7 @@
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LiftReveal;
import com.android.systemui.statusbar.LightRevealScrim;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -347,6 +348,8 @@
ONLY_CORE_APPS = onlyCoreApps;
}
+ private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+
public interface ExpansionChangedListener {
void onExpansionChanged(float expansion, boolean expanded);
}
@@ -438,9 +441,6 @@
KeyguardIndicationController mKeyguardIndicationController;
- // RemoteInputView to be activated after unlock
- private View mPendingRemoteInputView;
-
private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
private View mReportRejectedTouch;
@@ -650,12 +650,6 @@
private final ScreenLifecycle mScreenLifecycle;
private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final View.OnClickListener mGoToLockedShadeListener = v -> {
- if (mState == StatusBarState.KEYGUARD) {
- wakeUpIfDozing(SystemClock.uptimeMillis(), v, "SHADE_CLICK");
- goToLockedShade(null);
- }
- };
private boolean mNoAnimationOnNextBarModeChange;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -798,6 +792,7 @@
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
+ LockscreenShadeTransitionController lockscreenShadeTransitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
super(context);
@@ -882,6 +877,8 @@
mAnimationScheduler = animationScheduler;
mStatusBarLocationPublisher = locationPublisher;
mFeatureFlags = featureFlags;
+ mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
+ lockscreenShadeTransitionController.setStatusbar(this);
mExpansionChangedListeners = new ArrayList<>();
@@ -909,6 +906,8 @@
mBroadcastDispatcher.registerReceiver(mTaskbarChangeReceiver, filter);
}
+ mKeyguardIndicationController.init();
+
mColorExtractor.addOnColorsChangedListener(this);
mStatusBarStateController.addCallback(this,
SysuiStatusBarStateController.RANK_STATUS_BAR);
@@ -1420,7 +1419,8 @@
mDozeScrimController, mScrimController, mNotificationShadeWindowController,
mDynamicPrivacyController, mKeyguardStateController,
mKeyguardIndicationController,
- this /* statusBar */, mShadeController, mCommandQueue, mInitController,
+ this /* statusBar */, mShadeController,
+ mLockscreenShadeTransitionController, mCommandQueue, mInitController,
mNotificationInterruptStateProvider);
mNotificationShelfController.setOnActivatedListener(mPresenter);
@@ -1500,7 +1500,6 @@
private void inflateShelf() {
mNotificationShelfController = mSuperStatusBarViewFactory
.getNotificationShelfController(mStackScroller);
- mNotificationShelfController.setOnClickListener(mGoToLockedShadeListener);
}
@Override
@@ -1673,7 +1672,7 @@
final boolean expandEnabled = mDeviceProvisionedController.isDeviceProvisioned()
&& (mUserSetup || mUserSwitcherController == null
|| !mUserSwitcherController.isSimpleUserSwitcher())
- && ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0)
+ && !isShadeDisabled()
&& ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
&& !mDozing
&& !ONLY_CORE_APPS;
@@ -1681,6 +1680,10 @@
Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
}
+ public boolean isShadeDisabled() {
+ return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
+ }
+
public void addQsTile(ComponentName tile) {
if (mQSPanelController != null && mQSPanelController.getHost() != null) {
mQSPanelController.getHost().addTile(tile);
@@ -2988,11 +2991,13 @@
mNotificationsController.resetUserExpandedStates();
}
- private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen) {
+ private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen,
+ boolean afterKeyguardGone) {
if (mStatusBarKeyguardViewManager.isShowing() && requiresShadeOpen) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
}
- dismissKeyguardThenExecute(action, null /* cancelAction */, false /* afterKeyguardGone */);
+ dismissKeyguardThenExecute(action, null /* cancelAction */,
+ afterKeyguardGone /* afterKeyguardGone */);
}
protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
@@ -3329,7 +3334,6 @@
public void showKeyguard() {
mStatusBarStateController.setKeyguardRequested(true);
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
- mPendingRemoteInputView = null;
updateIsKeyguard();
mAssistManagerLazy.get().onLockscreenShown();
}
@@ -3388,11 +3392,6 @@
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
updatePanelExpansionForKeyguard();
- if (mDraggedDownEntry != null) {
- mDraggedDownEntry.setUserLocked(false);
- mDraggedDownEntry.notifyHeightChanged(false /* needsAnimation */);
- mDraggedDownEntry = null;
- }
}
private void updatePanelExpansionForKeyguard() {
@@ -3520,11 +3519,7 @@
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
}
long delay = mKeyguardStateController.calculateGoingToFullShadeDelay();
- mNotificationPanelViewController.animateToFullShade(delay);
- if (mDraggedDownEntry != null) {
- mDraggedDownEntry.setUserLocked(false);
- mDraggedDownEntry = null;
- }
+ mLockscreenShadeTransitionController.onHideKeyguard(delay);
// Disable layout transitions in navbar for this transition because the load is just
// too heavy for the CPU and GPU on any device.
@@ -3711,7 +3706,23 @@
private void showBouncerIfKeyguard() {
if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
&& !mKeyguardViewMediator.isHiding()) {
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
+ mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
+ }
+ }
+
+ /**
+ * Show the bouncer if we're currently on the keyguard or shade locked and aren't hiding.
+ * @param performAction the action to perform when the bouncer is dismissed.
+ * @param cancelAction the action to perform when unlock is aborted.
+ */
+ public void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction,
+ Runnable cancelAction) {
+ if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
+ && !mKeyguardViewMediator.isHiding()) {
+ mStatusBarKeyguardViewManager.dismissWithAction(performAction, cancelAction,
+ false /* afterKeyguardGone */);
+ } else if (cancelAction != null) {
+ cancelAction.run();
}
}
@@ -3892,47 +3903,6 @@
}
/**
- * If secure with redaction: Show bouncer, go to unlocked shade.
- *
- * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
- *
- * @param expandView The view to expand after going to the shade.
- */
- void goToLockedShade(View expandView) {
- if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
- return;
- }
-
- int userId = mLockscreenUserManager.getCurrentUserId();
- ExpandableNotificationRow row = null;
- NotificationEntry entry = null;
- if (expandView instanceof ExpandableNotificationRow) {
- entry = ((ExpandableNotificationRow) expandView).getEntry();
- entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
- // Indicate that the group expansion is changing at this time -- this way the group
- // and children backgrounds / divider animations will look correct.
- entry.setGroupExpansionChanging(true);
- userId = entry.getSbn().getUserId();
- }
- boolean fullShadeNeedsBouncer = !mLockscreenUserManager.
- userAllowsPrivateNotificationsInPublic(mLockscreenUserManager.getCurrentUserId())
- || !mLockscreenUserManager.shouldShowLockscreenNotifications()
- || mFalsingCollector.shouldEnforceBouncer();
- if (mKeyguardBypassController.getBypassEnabled()) {
- fullShadeNeedsBouncer = false;
- }
- if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
- showBouncerIfKeyguard();
- mDraggedDownEntry = entry;
- mPendingRemoteInputView = null;
- } else {
- mNotificationPanelViewController.animateToFullShade(0 /* delay */);
- mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED);
- }
- }
-
- /**
* Propagation of the bouncer state, indicating that it's fully visible.
*/
public void setBouncerShowing(boolean bouncerShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 6ed375e..91d1bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -132,6 +132,9 @@
@Override
public void onVisibilityChanged(boolean isVisible) {
+ if (!isVisible) {
+ cancelPostAuthActions();
+ }
if (mAlternateAuthInterceptor != null) {
mAlternateAuthInterceptor.onBouncerVisibilityChanged();
}
@@ -362,6 +365,26 @@
return false;
}
+ /**
+ * If applicable, shows the alternate authentication bouncer. Else, shows the input
+ * (pin/password/pattern) bouncer.
+ * @param scrimmed true when the input bouncer should show scrimmed, false when the user will be
+ * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
+ */
+ public void showGenericBouncer(boolean scrimmed) {
+ if (mAlternateAuthInterceptor != null) {
+ if (mAlternateAuthInterceptor.showAlternateAuthBouncer()) {
+ mStatusBar.updateScrimController();
+ }
+ return;
+ }
+
+ showBouncer(scrimmed);
+ }
+
+ /**
+ * Hides the input bouncer (pin/password/pattern).
+ */
@VisibleForTesting
void hideBouncer(boolean destroyView) {
if (mBouncer == null) {
@@ -377,7 +400,7 @@
}
/**
- * Shows the keyguard bouncer - the password challenge on the lock screen
+ * Shows the keyguard input bouncer - the password challenge on the lock screen
*
* @param scrimmed true when the bouncer should show scrimmed, false when the user will be
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
@@ -406,21 +429,25 @@
return;
}
+ mAfterKeyguardGoneAction = r;
+ mKeyguardGoneCancelAction = cancelAction;
if (mAlternateAuthInterceptor != null) {
- mAfterKeyguardGoneAction = r;
- mKeyguardGoneCancelAction = cancelAction;
if (mAlternateAuthInterceptor.showAlternateAuthBouncer()) {
mStatusBar.updateScrimController();
}
return;
}
- if (!afterKeyguardGone) {
- mBouncer.showWithDismissAction(r, cancelAction);
- } else {
- mAfterKeyguardGoneAction = r;
- mKeyguardGoneCancelAction = cancelAction;
+ if (afterKeyguardGone) {
+ // we'll handle the dismiss action after keyguard is gone, so just show the bouncer
mBouncer.show(false /* resetSecuritySelection */);
+ } else {
+ // after authentication success, run dismiss action with the option to defer
+ // hiding the keyguard based on the return value of the OnDismissAction
+ mBouncer.showWithDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
+ // bouncer will handle the dismiss action, so we no longer need to track it here
+ mAfterKeyguardGoneAction = null;
+ mKeyguardGoneCancelAction = null;
}
}
updateStates();
@@ -1133,15 +1160,21 @@
}
/**
- * Request to show the udfps affordance in a particular color. This can be used if an
- * occluding app on the keyguard would like to request udfps. This method does nothing if
- * {@link KeyguardUpdateMonitor#shouldListenForFingerprint} is false.
+ * Request to authenticate using face.
*/
- public void requestUdfps(boolean request, int color) {
- if (mAlternateAuthInterceptor == null) {
- return;
+ public void requestFace(boolean request) {
+ mKeyguardUpdateManager.requestFaceAuthOnOccludingApp(request);
+ }
+
+ /**
+ * Request to authenticate using the fingerprint sensor. If the fingerprint sensor is udfps,
+ * uses the color provided by udfpsColor for the fingerprint icon.
+ */
+ public void requestFp(boolean request, int udfpsColor) {
+ mKeyguardUpdateManager.requestFingerprintAuthOnOccludingApp(request);
+ if (mAlternateAuthInterceptor != null) {
+ mAlternateAuthInterceptor.requestUdfps(request, udfpsColor);
}
- mAlternateAuthInterceptor.requestUdfps(request, color);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 2b5caf9..aa58527 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -48,6 +48,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -112,6 +113,7 @@
private final KeyguardIndicationController mKeyguardIndicationController;
private final StatusBar mStatusBar;
private final ShadeController mShadeController;
+ private final LockscreenShadeTransitionController mShadeTransitionController;
private final CommandQueue mCommandQueue;
private final AccessibilityManager mAccessibilityManager;
@@ -138,6 +140,7 @@
KeyguardIndicationController keyguardIndicationController,
StatusBar statusBar,
ShadeController shadeController,
+ LockscreenShadeTransitionController shadeTransitionController,
CommandQueue commandQueue,
InitController initController,
NotificationInterruptStateProvider notificationInterruptStateProvider) {
@@ -149,6 +152,7 @@
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mStatusBar = statusBar;
mShadeController = shadeController;
+ mShadeTransitionController = shadeTransitionController;
mCommandQueue = commandQueue;
mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -194,6 +198,7 @@
}
};
+ mKeyguardIndicationController.init();
mViewHierarchyManager.setUpWithPresenter(this,
stackScrollerController.getNotificationListContainer());
mEntryManager.setUpWithPresenter(this);
@@ -393,7 +398,7 @@
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
if (nowExpanded) {
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
- mShadeController.goToLockedShade(clickedEntry.getRow());
+ mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
} else if (clickedEntry.isSensitive()
&& mDynamicPrivacyController.isInLockedDownShade()) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 142cf21..ebf2465 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -97,13 +97,13 @@
mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
mIconController = iconController;
+ mCarrierConfigTracker = carrierConfigTracker;
mNetworkController = Dependency.get(NetworkController.class);
mSecurityController = Dependency.get(SecurityController.class);
Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
mNetworkController.addCallback(this);
mSecurityController.addCallback(this);
- mCarrierConfigTracker = carrierConfigTracker;
}
public void destroy() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index d0d2cb2..9722d68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -213,6 +214,7 @@
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
+ LockscreenShadeTransitionController transitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
return new StatusBar(
@@ -299,6 +301,7 @@
ongoingCallController,
animationScheduler,
locationPublisher,
+ transitionController,
featureFlags,
keyguardUnlockAnimationController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
index 1fe77fd..6e27cae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
@@ -56,6 +56,7 @@
// call starts.
minimumTextWidth = 0
shouldHideText = false
+ visibility = VISIBLE
super.setBase(base)
}
@@ -76,6 +77,9 @@
if (desiredTextWidth > enforcedTextWidth) {
shouldHideText = true
+ // Changing visibility ensures that the content description is not read aloud when the
+ // time isn't displayed.
+ visibility = GONE
setMeasuredDimension(0, 0)
} else {
// It's possible that the current text could fit in a smaller width, but we don't want
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 6d1df5b..a019895 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -23,7 +23,7 @@
import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
import android.content.Intent
import android.util.Log
-import android.view.ViewGroup
+import android.view.View
import android.widget.Chronometer
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -49,14 +49,15 @@
private val systemClock: SystemClock,
private val activityStarter: ActivityStarter,
@Main private val mainExecutor: Executor,
- private val iActivityManager: IActivityManager
+ private val iActivityManager: IActivityManager,
+ private val logger: OngoingCallLogger
) : CallbackController<OngoingCallListener> {
/** Null if there's no ongoing call. */
private var ongoingCallInfo: OngoingCallInfo? = null
/** True if the application managing the call is visible to the user. */
private var isCallAppVisible: Boolean = true
- private var chipView: ViewGroup? = null
+ private var chipView: View? = null
private var uidObserver: IUidObserver.Stub? = null
private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
@@ -81,16 +82,14 @@
entry.sbn.notification.contentIntent.intent,
entry.sbn.uid)
updateChip()
+ } else if (isCallNotification(entry)) {
+ removeChip()
}
}
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
if (isOngoingCallNotification(entry)) {
- ongoingCallInfo = null
- mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
- if (uidObserver != null) {
- iActivityManager.unregisterUidObserver(uidObserver)
- }
+ removeChip()
}
}
}
@@ -104,15 +103,25 @@
/**
* Sets the chip view that will contain ongoing call information.
*
- * Should only be called from [CollapedStatusBarFragment].
+ * Should only be called from [CollapsedStatusBarFragment].
*/
- fun setChipView(chipView: ViewGroup) {
+ fun setChipView(chipView: View) {
this.chipView = chipView
if (hasOngoingCall()) {
updateChip()
}
}
+
+ /**
+ * Called when the chip's visibility may have changed.
+ *
+ * Should only be called from [CollapsedStatusBarFragment].
+ */
+ fun notifyChipVisibilityChanged(chipIsVisible: Boolean) {
+ logger.logChipVisibilityChanged(chipIsVisible)
+ }
+
/**
* Returns true if there's an active ongoing call that should be displayed in a status bar chip.
*/
@@ -150,6 +159,7 @@
timeView.start()
currentChipView.setOnClickListener {
+ logger.logChipClicked()
activityStarter.postStartActivityDismissingKeyguard(
currentOngoingCallInfo.intent, 0,
ActivityLaunchAnimator.Controller.fromView(it))
@@ -212,6 +222,14 @@
return procState <= ActivityManager.PROCESS_STATE_TOP
}
+ private fun removeChip() {
+ ongoingCallInfo = null
+ mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
+ if (uidObserver != null) {
+ iActivityManager.unregisterUidObserver(uidObserver)
+ }
+ }
+
private class OngoingCallInfo(
val callStartTime: Long,
val intent: Intent,
@@ -221,10 +239,15 @@
private fun isOngoingCallNotification(entry: NotificationEntry): Boolean {
val extras = entry.sbn.notification.extras
- val callStyleTemplateName = Notification.CallStyle::class.java.name
- return extras.getString(Notification.EXTRA_TEMPLATE) == callStyleTemplateName &&
+ return isCallNotification(entry) &&
extras.getInt(Notification.EXTRA_CALL_TYPE, -1) == CALL_TYPE_ONGOING
}
+private fun isCallNotification(entry: NotificationEntry): Boolean {
+ val extras = entry.sbn.notification.extras
+ val callStyleTemplateName = Notification.CallStyle::class.java.name
+ return extras.getString(Notification.EXTRA_TEMPLATE) == callStyleTemplateName
+}
+
private const val TAG = "OngoingCallController"
private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
new file mode 100644
index 0000000..177f215
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** A class to log events for the ongoing call chip. */
+@SysUISingleton
+class OngoingCallLogger @Inject constructor(private val logger: UiEventLogger) {
+
+ private var chipIsVisible: Boolean = false
+
+ /** Logs that the ongoing call chip was clicked. */
+ fun logChipClicked() {
+ logger.log(OngoingCallEvents.ONGOING_CALL_CLICKED)
+ }
+
+ /**
+ * If needed, logs that the ongoing call chip's visibility has changed.
+ *
+ * For now, only logs when the chip changes from not visible to visible.
+ */
+ fun logChipVisibilityChanged(chipIsVisible: Boolean) {
+ if (chipIsVisible && chipIsVisible != this.chipIsVisible) {
+ logger.log(OngoingCallEvents.ONGOING_CALL_VISIBLE)
+ }
+ this.chipIsVisible = chipIsVisible
+ }
+
+ @VisibleForTesting
+ enum class OngoingCallEvents(val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The ongoing call chip became visible")
+ ONGOING_CALL_VISIBLE(813),
+
+ @UiEvent(doc = "The ongoing call chip was clicked")
+ ONGOING_CALL_CLICKED(814);
+
+ override fun getId() = metricId
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 82ad00a..2e75395 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -30,6 +30,8 @@
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
@@ -60,9 +62,28 @@
private final ArrayMap<String, Long> mSnoozedPackages;
private final AccessibilityManagerWrapper mAccessibilityMgr;
+ private final UiEventLogger mUiEventLogger;
+
+ /**
+ * Enum entry for notification peek logged from this class.
+ */
+ enum NotificationPeekEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Heads-up notification peeked on screen.")
+ NOTIFICATION_PEEK(801);
+
+ private final int mId;
+ NotificationPeekEvent(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+ }
+
public HeadsUpManager(@NonNull final Context context) {
mContext = context;
mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
+ mUiEventLogger = Dependency.get(UiEventLogger.class);
Resources resources = context.getResources();
mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
mAutoDismissNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
@@ -130,6 +151,11 @@
if (entry.isRowPinned() != isPinned) {
entry.setRowPinned(isPinned);
updatePinnedMode();
+ if (isPinned && entry.getSbn() != null) {
+ mUiEventLogger.logWithInstanceId(
+ NotificationPeekEvent.NOTIFICATION_PEEK, entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(), entry.getSbn().getInstanceId());
+ }
for (OnHeadsUpChangedListener listener : mListeners) {
if (isPinned) {
listener.onHeadsUpPinned(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f45218d..07e9fed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -364,11 +364,11 @@
if (network.equals(mLastNetwork) && validated == lastValidated) {
// Should not rely on getTransportTypes() returning the same order of transport
// types. So sort the array before comparing.
- int[] newTypes = networkCapabilities.getTransportTypes();
+ int[] newTypes = getProcessedTransportTypes(networkCapabilities);
Arrays.sort(newTypes);
int[] lastTypes = (mLastNetworkCapabilities != null)
- ? mLastNetworkCapabilities.getTransportTypes() : null;
+ ? getProcessedTransportTypes(mLastNetworkCapabilities) : null;
if (lastTypes != null) Arrays.sort(lastTypes);
if (Arrays.equals(newTypes, lastTypes)) {
@@ -533,6 +533,21 @@
return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
}
+ private int[] getProcessedTransportTypes(NetworkCapabilities networkCapabilities) {
+ int[] transportTypes = networkCapabilities.getTransportTypes();
+ for (int i = 0; i < transportTypes.length; i++) {
+ // For VCN over WiFi, the transportType is set to be TRANSPORT_CELLULAR in the
+ // NetworkCapabilities, but we need to convert it into TRANSPORT_WIFI in order to
+ // distinguish it from VCN over Cellular.
+ if (transportTypes[i] == NetworkCapabilities.TRANSPORT_CELLULAR
+ && Utils.tryGetWifiInfoForVcn(networkCapabilities) != null) {
+ transportTypes[i] = NetworkCapabilities.TRANSPORT_WIFI;
+ break;
+ }
+ }
+ return transportTypes;
+ }
+
private MobileSignalController getDataController() {
int dataSubId = mSubDefaults.getActiveDataSubId();
return getControllerWithSubId(dataSubId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
index e3e2572..b563d86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
@@ -482,7 +482,7 @@
private fun KeyguardDismissUtil.executeWhenUnlocked(
requiresShadeOpen: Boolean,
onDismissAction: () -> Boolean
-) = executeWhenUnlocked(onDismissAction, requiresShadeOpen)
+) = executeWhenUnlocked(onDismissAction, requiresShadeOpen, false)
// convenience function that swaps parameter order so that lambda can be placed at the end
private fun ActivityStarter.startPendingIntentDismissingKeyguard(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index edec618..41b1dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -23,6 +23,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ContrastColorUtil;
@@ -58,15 +59,6 @@
/** Spacing to be applied between views. */
private final int mSpacing;
- /** Horizontal padding of smart reply buttons if all of them use only one line of text. */
- private final int mSingleLineButtonPaddingHorizontal;
-
- /** Horizontal padding of smart reply buttons if at least one of them uses two lines of text. */
- private final int mDoubleLineButtonPaddingHorizontal;
-
- /** Increase in width of a smart reply button as a result of using two lines instead of one. */
- private final int mSingleToDoubleLineButtonWidthIncrease;
-
private final BreakIterator mBreakIterator;
private PriorityQueue<Button> mCandidateButtonQueueForSqueezing;
@@ -114,8 +106,6 @@
mDefaultBackgroundColor);
int spacing = 0;
- int singleLineButtonPaddingHorizontal = 0;
- int doubleLineButtonPaddingHorizontal = 0;
int strokeWidth = 0;
final TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.SmartReplyView,
@@ -125,10 +115,6 @@
int attr = arr.getIndex(i);
if (attr == R.styleable.SmartReplyView_spacing) {
spacing = arr.getDimensionPixelSize(i, 0);
- } else if (attr == R.styleable.SmartReplyView_singleLineButtonPaddingHorizontal) {
- singleLineButtonPaddingHorizontal = arr.getDimensionPixelSize(i, 0);
- } else if (attr == R.styleable.SmartReplyView_doubleLineButtonPaddingHorizontal) {
- doubleLineButtonPaddingHorizontal = arr.getDimensionPixelSize(i, 0);
} else if (attr == R.styleable.SmartReplyView_buttonStrokeWidth) {
strokeWidth = arr.getDimensionPixelSize(i, 0);
}
@@ -137,10 +123,6 @@
mStrokeWidth = strokeWidth;
mSpacing = spacing;
- mSingleLineButtonPaddingHorizontal = singleLineButtonPaddingHorizontal;
- mDoubleLineButtonPaddingHorizontal = doubleLineButtonPaddingHorizontal;
- mSingleToDoubleLineButtonWidthIncrease =
- 2 * (doubleLineButtonPaddingHorizontal - singleLineButtonPaddingHorizontal);
mBreakIterator = BreakIterator.getLineInstance();
@@ -222,6 +204,12 @@
return new LayoutParams(params.width, params.height);
}
+ private void clearLayoutLineCount(View view) {
+ if (view instanceof TextView) {
+ ((TextView) view).nullLayouts();
+ }
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int targetWidth = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED
@@ -237,8 +225,7 @@
SmartSuggestionMeasures accumulatedMeasures = new SmartSuggestionMeasures(
mPaddingLeft + mPaddingRight,
- 0 /* maxChildHeight */,
- mSingleLineButtonPaddingHorizontal);
+ 0 /* maxChildHeight */);
int displayedChildCount = 0;
// Set up a list of suggestions where actions come before replies. Note that the Buttons
@@ -268,8 +255,7 @@
continue;
}
- child.setPadding(accumulatedMeasures.mButtonPaddingHorizontal, child.getPaddingTop(),
- accumulatedMeasures.mButtonPaddingHorizontal, child.getPaddingBottom());
+ clearLayoutLineCount(child);
child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
coveredSuggestions.add(child);
@@ -299,18 +285,6 @@
accumulatedMeasures.mMaxChildHeight =
Math.max(accumulatedMeasures.mMaxChildHeight, childHeight);
- // Do we need to increase the number of lines in smart reply buttons to two?
- final boolean increaseToTwoLines =
- (accumulatedMeasures.mButtonPaddingHorizontal
- == mSingleLineButtonPaddingHorizontal)
- && (lineCount == 2 || accumulatedMeasures.mMeasuredWidth > targetWidth);
- if (increaseToTwoLines) {
- accumulatedMeasures.mMeasuredWidth +=
- (displayedChildCount + 1) * mSingleToDoubleLineButtonWidthIncrease;
- accumulatedMeasures.mButtonPaddingHorizontal =
- mDoubleLineButtonPaddingHorizontal;
- }
-
// If the last button doesn't fit into the remaining width, try squeezing preceding
// smart reply buttons.
if (accumulatedMeasures.mMeasuredWidth > targetWidth) {
@@ -372,18 +346,11 @@
mCandidateButtonQueueForSqueezing.clear();
// Finally, we need to re-measure some buttons.
- remeasureButtonsIfNecessary(accumulatedMeasures.mButtonPaddingHorizontal,
- accumulatedMeasures.mMaxChildHeight);
+ remeasureButtonsIfNecessary(accumulatedMeasures.mMaxChildHeight);
int buttonHeight = Math.max(getSuggestedMinimumHeight(), mPaddingTop
+ accumulatedMeasures.mMaxChildHeight + mPaddingBottom);
- // Set the corner radius to half the button height to make the side of the buttons look like
- // a semicircle.
- for (View smartSuggestionButton : smartSuggestions) {
- setCornerRadius((Button) smartSuggestionButton, ((float) buttonHeight) / 2);
- }
-
setMeasuredDimension(
resolveSize(Math.max(getSuggestedMinimumWidth(),
accumulatedMeasures.mMeasuredWidth),
@@ -411,18 +378,14 @@
private static class SmartSuggestionMeasures {
int mMeasuredWidth = -1;
int mMaxChildHeight = -1;
- int mButtonPaddingHorizontal = -1;
- SmartSuggestionMeasures(int measuredWidth, int maxChildHeight,
- int buttonPaddingHorizontal) {
+ SmartSuggestionMeasures(int measuredWidth, int maxChildHeight) {
this.mMeasuredWidth = measuredWidth;
this.mMaxChildHeight = maxChildHeight;
- this.mButtonPaddingHorizontal = buttonPaddingHorizontal;
}
public SmartSuggestionMeasures clone() {
- return new SmartSuggestionMeasures(
- mMeasuredWidth, mMaxChildHeight, mButtonPaddingHorizontal);
+ return new SmartSuggestionMeasures(mMeasuredWidth, mMaxChildHeight);
}
}
@@ -553,17 +516,11 @@
private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) {
int oldWidth = button.getMeasuredWidth();
- if (button.getPaddingLeft() != mDoubleLineButtonPaddingHorizontal) {
- // Correct for the fact that the button was laid out with single-line horizontal
- // padding.
- oldWidth += mSingleToDoubleLineButtonWidthIncrease;
- }
// Re-measure the squeezed smart reply button.
- button.setPadding(mDoubleLineButtonPaddingHorizontal, button.getPaddingTop(),
- mDoubleLineButtonPaddingHorizontal, button.getPaddingBottom());
+ clearLayoutLineCount(button);
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
- 2 * mDoubleLineButtonPaddingHorizontal + textWidth
+ button.getPaddingLeft() + button.getPaddingRight() + textWidth
+ getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
button.measure(widthMeasureSpec, heightMeasureSpec);
@@ -579,8 +536,7 @@
}
}
- private void remeasureButtonsIfNecessary(
- int buttonPaddingHorizontal, int maxChildHeight) {
+ private void remeasureButtonsIfNecessary(int maxChildHeight) {
final int maxChildHeightMeasure =
MeasureSpec.makeMeasureSpec(maxChildHeight, MeasureSpec.EXACTLY);
@@ -602,24 +558,7 @@
newWidth = Integer.MAX_VALUE;
}
- // Re-measure reason 2: The button's horizontal padding is incorrect (because it was
- // measured with the wrong number of lines).
- if (child.getPaddingLeft() != buttonPaddingHorizontal) {
- requiresNewMeasure = true;
- if (newWidth != Integer.MAX_VALUE) {
- if (buttonPaddingHorizontal == mSingleLineButtonPaddingHorizontal) {
- // Change padding (2->1 line).
- newWidth -= mSingleToDoubleLineButtonWidthIncrease;
- } else {
- // Change padding (1->2 lines).
- newWidth += mSingleToDoubleLineButtonWidthIncrease;
- }
- }
- child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
- buttonPaddingHorizontal, child.getPaddingBottom());
- }
-
- // Re-measure reason 3: The button's height is less than the max height of all buttons
+ // Re-measure reason 2: The button's height is less than the max height of all buttons
// (all should have the same height).
if (child.getMeasuredHeight() != maxChildHeight) {
requiresNewMeasure = true;
@@ -725,23 +664,6 @@
button.setTextColor(mCurrentTextColor);
}
- private void setCornerRadius(Button button, float radius) {
- Drawable drawable = button.getBackground();
- if (drawable instanceof RippleDrawable) {
- // Mutate in case other notifications are using this drawable.
- drawable = drawable.mutate();
- RippleDrawable ripple = (RippleDrawable) drawable;
- Drawable inset = ripple.getDrawable(0);
- if (inset instanceof InsetDrawable) {
- Drawable background = ((InsetDrawable) inset).getDrawable();
- if (background instanceof GradientDrawable) {
- GradientDrawable gradientDrawable = (GradientDrawable) background;
- gradientDrawable.setCornerRadius(radius);
- }
- }
- }
- }
-
enum SmartButtonType {
REPLY,
ACTION
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index 3c1e123..865aa23f 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -66,6 +66,13 @@
"android.theme.customization.accent_color";
static final String OVERLAY_CATEGORY_SYSTEM_PALETTE =
"android.theme.customization.system_palette";
+
+ static final String OVERLAY_COLOR_SOURCE = "android.theme.customization.color_source";
+
+ static final String COLOR_SOURCE_PRESET = "preset";
+
+ static final String TIMESTAMP_FIELD = "_applied_timestamp";
+
@VisibleForTesting
static final String OVERLAY_CATEGORY_FONT = "android.theme.customization.font";
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 9bdd8c0..195114f 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -15,8 +15,11 @@
*/
package com.android.systemui.theme;
+import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE;
+import static com.android.systemui.theme.ThemeOverlayApplier.TIMESTAMP_FIELD;
import android.annotation.Nullable;
import android.app.WallpaperColors;
@@ -90,12 +93,12 @@
private final UserManager mUserManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final Executor mBgExecutor;
- private final SecureSettings mSecureSettings;
+ private SecureSettings mSecureSettings;
private final Executor mMainExecutor;
private final Handler mBgHandler;
private final WallpaperManager mWallpaperManager;
private final boolean mIsMonetEnabled;
- private final UserTracker mUserTracker;
+ private UserTracker mUserTracker;
private DeviceProvisionedController mDeviceProvisionedController;
private WallpaperColors mSystemColors;
// If fabricated overlays were already created for the current theme.
@@ -112,6 +115,8 @@
private boolean mAcceptColorEvents = true;
// Defers changing themes until Setup Wizard is done.
private boolean mDeferredThemeEvaluation;
+ // Determines if we should ignore THEME_CUSTOMIZATION_OVERLAY_PACKAGES setting changes.
+ private boolean mSkipSettingChange;
private final DeviceProvisionedListener mDeviceProvisionedListener =
new DeviceProvisionedListener() {
@@ -162,6 +167,35 @@
}
}
}
+ // Check if we need to reset to default colors (if a color override was set that is sourced
+ // from the wallpaper)
+ int currentUser = mUserTracker.getUserId();
+ String overlayPackageJson = mSecureSettings.getStringForUser(
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ currentUser);
+ if (!TextUtils.isEmpty(overlayPackageJson)) {
+ try {
+ JSONObject jsonObject = new JSONObject(overlayPackageJson);
+ if ((jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR)
+ || jsonObject.has(OVERLAY_CATEGORY_SYSTEM_PALETTE))
+ && !COLOR_SOURCE_PRESET.equals(
+ jsonObject.optString(OVERLAY_COLOR_SOURCE))) {
+ mSkipSettingChange = true;
+ jsonObject.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
+ jsonObject.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ jsonObject.remove(OVERLAY_COLOR_SOURCE);
+ jsonObject.put(TIMESTAMP_FIELD, System.currentTimeMillis());
+ if (DEBUG) {
+ Log.d(TAG, "Updating theme setting from "
+ + overlayPackageJson + " to " + jsonObject.toString());
+ }
+ mSecureSettings.putString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ jsonObject.toString());
+ }
+ } catch (JSONException e) {
+ Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
+ }
+ }
reevaluateSystemTheme(false /* forceReload */);
};
@@ -232,6 +266,11 @@
mDeferredThemeEvaluation = true;
return;
}
+ if (mSkipSettingChange) {
+ if (DEBUG) Log.d(TAG, "Skipping setting change");
+ mSkipSettingChange = false;
+ return;
+ }
reevaluateSystemTheme(true /* forceReload */);
}
},
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
index 2270f96..7a5ceb5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui.util.concurrency;
+import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;
@@ -29,6 +30,14 @@
*/
public interface ThreadFactory {
/**
+ * Returns a {@link Handler} running on a named thread.
+ *
+ * The thread is implicitly started and may be left running indefinitely, depending on the
+ * implementation. Assume this is the case and use responsibly.
+ */
+ Handler builderHandlerOnNewThread(String threadName);
+
+ /**
* Return an {@link java.util.concurrent.Executor} running on a named thread.
*
* The thread is implicitly started and may be left running indefinitely, depending on the
@@ -45,6 +54,11 @@
DelayableExecutor buildDelayableExecutorOnNewThread(String threadName);
/**
+ * Return an {@link DelayableExecutor} running on the given HandlerThread.
+ **/
+ DelayableExecutor buildDelayableExecutorOnHandler(Handler handler);
+
+ /**
* Return an {@link DelayableExecutor} running the given Looper
**/
DelayableExecutor buildDelayableExecutorOnLooper(Looper looper);
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
index 2d9f2b4..184b831 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.util.concurrency;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -27,16 +28,31 @@
@Inject
ThreadFactoryImpl() {}
+ @Override
+ public Handler builderHandlerOnNewThread(String threadName) {
+ HandlerThread handlerThread = new HandlerThread(threadName);
+ handlerThread.start();
+ return new Handler(handlerThread.getLooper());
+ }
+
+ @Override
public Executor buildExecutorOnNewThread(String threadName) {
return buildDelayableExecutorOnNewThread(threadName);
}
+ @Override
public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) {
HandlerThread handlerThread = new HandlerThread(threadName);
handlerThread.start();
- return new ExecutorImpl(handlerThread.getLooper());
+ return buildDelayableExecutorOnLooper(handlerThread.getLooper());
}
+ @Override
+ public DelayableExecutor buildDelayableExecutorOnHandler(Handler handler) {
+ return buildDelayableExecutorOnLooper(handler.getLooper());
+ }
+
+ @Override
public DelayableExecutor buildDelayableExecutorOnLooper(Looper looper) {
return new ExecutorImpl(looper);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
index 644addf..5e9bae9 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
@@ -20,13 +20,10 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.text.TextPaint;
import android.util.MathUtils;
import android.view.View;
-import android.widget.TextView;
import androidx.annotation.ColorInt;
-import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
@@ -39,12 +36,10 @@
@ColorInt private final int mUnselectedColor;
@ColorInt private final int mSelectedColor;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
private WalletCardCarousel mCardCarousel;
DotIndicatorDecoration(Context context) {
super();
-
mUnselectedRadius =
context.getResources().getDimensionPixelSize(
R.dimen.card_carousel_dot_unselected_radius);
@@ -53,13 +48,8 @@
R.dimen.card_carousel_dot_selected_radius);
mDotMargin = context.getResources().getDimensionPixelSize(R.dimen.card_carousel_dot_margin);
- TextView textView = new TextView(context);
- mTextPaint.set(textView.getPaint());
- // Text color is not copied from text appearance.
- mTextPaint.setColor(ContextCompat.getColor(context, R.color.GM2_blue_600));
-
- mUnselectedColor = ContextCompat.getColor(context, R.color.GM2_grey_300);
- mSelectedColor = ContextCompat.getColor(context, R.color.GM2_blue_600);
+ mUnselectedColor = context.getColor(com.android.internal.R.color.system_neutral1_300);
+ mSelectedColor = context.getColor(com.android.internal.R.color.system_neutral1_0);
}
@Override
@@ -107,9 +97,9 @@
int i = isLayoutLtr() ? itemsDrawn : itemCount - itemsDrawn - 1;
if (isSelectedItem(i)) {
- drawSelectedDot(canvas, interpolatedProgress, i);
+ drawSelectedDot(canvas, interpolatedProgress);
} else if (isNextItemInScrollingDirection(i)) {
- drawFadingUnselectedDot(canvas, interpolatedProgress, i);
+ drawFadingUnselectedDot(canvas, interpolatedProgress);
} else {
drawUnselectedDot(canvas);
}
@@ -121,7 +111,7 @@
this.mCardCarousel = null; // No need to hold a reference.
}
- private void drawSelectedDot(Canvas canvas, float progress, int position) {
+ private void drawSelectedDot(Canvas canvas, float progress) {
// Divide progress by 2 because the other half of the animation is done by
// drawFadingUnselectedDot.
mPaint.setColor(
@@ -132,13 +122,13 @@
canvas.translate(radius * 2, 0);
}
- private void drawFadingUnselectedDot(Canvas canvas, float progress, int position) {
+ private void drawFadingUnselectedDot(Canvas canvas, float progress) {
// Divide progress by 2 because the first half of the animation is done by drawSelectedDot.
int blendedColor =
ColorUtils.blendARGB(
mUnselectedColor, mSelectedColor, progress / 2);
mPaint.setColor(getTransitionAdjustedColor(blendedColor));
- float radius = MathUtils.lerp(mSelectedRadius, mUnselectedRadius, progress / 2);
+ float radius = MathUtils.lerp(mUnselectedRadius, mSelectedColor, progress / 2);
canvas.drawCircle(radius, 0, radius, mPaint);
canvas.translate(radius * 2, 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index ac8b16a..83aa01f 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -16,7 +16,11 @@
package com.android.systemui.wallet.ui;
+import static android.provider.Settings.ACTION_LOCKSCREEN_SETTINGS;
+
+import android.content.Intent;
import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.service.quickaccesswallet.QuickAccessWalletClient;
@@ -95,9 +99,10 @@
}
setTitle("");
getActionBar().setDisplayHomeAsUpEnabled(true);
- getActionBar().setHomeAsUpIndicator(R.drawable.ic_close);
+ getActionBar().setHomeAsUpIndicator(getHomeIndicatorDrawable());
getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close);
WalletView walletView = requireViewById(R.id.wallet_view);
+
mWalletScreenController = new WalletScreenController(
this,
walletView,
@@ -115,20 +120,31 @@
&& mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
return;
}
- mActivityStarter.startActivity(
- mQuickAccessWalletClient.createWalletIntent(), true);
- finish();
+
+ if (mKeyguardStateController.isUnlocked()) {
+ mActivityStarter.startActivity(
+ mQuickAccessWalletClient.createWalletIntent(), true);
+ finish();
+ } else {
+ mKeyguardDismissUtil.executeWhenUnlocked(() -> {
+ mActivityStarter.startActivity(
+ mQuickAccessWalletClient.createWalletIntent(), true);
+ finish();
+ return false;
+ }, false, true);
+ }
});
+
// Click the action button to re-render the screen when the device is unlocked.
- if (!mKeyguardStateController.isUnlocked()) {
- walletView.getActionButton().setOnClickListener(
- v -> {
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return;
- }
- mKeyguardDismissUtil.executeWhenUnlocked(() -> false, false);
- });
- }
+ walletView.setDeviceLockedActionOnClickListener(
+ v -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return;
+ }
+
+ mKeyguardDismissUtil.executeWhenUnlocked(() -> false, false,
+ false);
+ });
}
@Override
@@ -141,13 +157,15 @@
protected void onResume() {
super.onResume();
mWalletScreenController.queryWalletCards();
- mKeyguardViewManager.requestUdfps(true, Color.BLACK);
+ mKeyguardViewManager.requestFp(true, Color.BLACK);
+ mKeyguardViewManager.requestFace(true);
}
@Override
protected void onPause() {
super.onPause();
- mKeyguardViewManager.requestUdfps(false, -1);
+ mKeyguardViewManager.requestFp(false, -1);
+ mKeyguardViewManager.requestFace(false);
}
@Override
@@ -163,7 +181,10 @@
finish();
return true;
} else if (itemId == R.id.wallet_lockscreen_settings) {
- // TODO(b/186496392): Navigate to Lock Screen Settings page when the item is clicked.
+ Intent intent =
+ new Intent(ACTION_LOCKSCREEN_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mActivityStarter.startActivity(intent, true);
return true;
}
return super.onOptionsItemSelected(item);
@@ -175,4 +196,10 @@
mWalletScreenController.onDismissed();
super.onDestroy();
}
+
+ private Drawable getHomeIndicatorDrawable() {
+ Drawable drawable = getDrawable(R.drawable.ic_close);
+ drawable.setTint(getColor(com.android.internal.R.color.system_neutral1_300));
+ return drawable;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index ec62981..d0662e7 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -131,8 +131,14 @@
if (data.isEmpty()) {
showEmptyStateView();
} else {
- mWalletView.showCardCarousel(
- data, response.getSelectedIndex(), !mKeyguardStateController.isUnlocked());
+ int selectedIndex = response.getSelectedIndex();
+ if (selectedIndex >= data.size()) {
+ Log.w(TAG, "Invalid selected card index, showing empty state.");
+ showEmptyStateView();
+ } else {
+ mWalletView.showCardCarousel(
+ data, selectedIndex, !mKeyguardStateController.isUnlocked());
+ }
}
removeMinHeightAndRecordHeightOnLayout();
});
@@ -178,6 +184,11 @@
}
@Override
+ public void onUnlockedChanged() {
+ queryWalletCards();
+ }
+
+ @Override
public void onCardSelected(@NonNull WalletCardViewInfo card) {
if (mIsDismissed) {
return;
@@ -234,7 +245,9 @@
mWalletView.show();
mWalletView.hideErrorMessage();
int iconSizePx =
- mContext.getResources().getDimensionPixelSize(R.dimen.wallet_view_header_icon_size);
+ mContext
+ .getResources()
+ .getDimensionPixelSize(R.dimen.wallet_screen_header_icon_size);
GetWalletCardsRequest request =
new GetWalletCardsRequest(cardWidthPx, cardHeightPx, iconSizePx, MAX_CARDS);
mWalletClient.getWalletCards(mExecutor, request, this);
@@ -340,7 +353,11 @@
@Override
public CharSequence getLabel() {
- return mWalletCard.getCardLabel();
+ CharSequence label = mWalletCard.getCardLabel();
+ if (label == null) {
+ return "";
+ }
+ return label;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index c547bb3..bf146b6 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -37,6 +37,7 @@
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import java.util.List;
@@ -62,6 +63,7 @@
private final ViewGroup mEmptyStateView;
private CharSequence mCenterCardText;
private boolean mIsDeviceLocked = false;
+ private OnClickListener mDeviceLockedActionOnClickListener;
public WalletView(Context context) {
this(context, null);
@@ -100,7 +102,7 @@
public void onCardScroll(WalletCardViewInfo centerCard, WalletCardViewInfo nextCard,
float percentDistanceFromCenter) {
CharSequence centerCardText = getLabelText(centerCard);
- Drawable centerCardIcon = centerCard.getIcon();
+ Drawable centerCardIcon = getHeaderIcon(mContext, centerCard);
if (!TextUtils.equals(mCenterCardText, centerCardText)) {
mCenterCardText = centerCardText;
mCardLabel.setText(centerCardText);
@@ -133,7 +135,8 @@
mCardCarouselContainer.setVisibility(VISIBLE);
mErrorView.setVisibility(GONE);
mEmptyStateView.setVisibility(GONE);
- renderHeaderIconAndActionButton(data.get(selectedIndex), isDeviceLocked);
+ mIcon.setImageDrawable(getHeaderIcon(mContext, data.get(selectedIndex)));
+ renderActionButton(data.get(selectedIndex), isDeviceLocked);
if (shouldAnimate) {
animateViewsShown(mIcon, mCardLabel, mActionButton);
}
@@ -176,6 +179,10 @@
mEmptyStateView.setVisibility(GONE);
}
+ void setDeviceLockedActionOnClickListener(OnClickListener onClickListener) {
+ mDeviceLockedActionOnClickListener = onClickListener;
+ }
+
void hide() {
setVisibility(GONE);
}
@@ -220,10 +227,15 @@
return mCardLabel;
}
- private void renderHeaderIconAndActionButton(WalletCardViewInfo walletCard, boolean isLocked) {
- mIcon.setImageDrawable(walletCard.getIcon());
- mIcon.setVisibility(VISIBLE);
- renderActionButton(walletCard, isLocked);
+ @Nullable
+ private static Drawable getHeaderIcon(Context context, WalletCardViewInfo walletCard) {
+ Drawable icon = walletCard.getIcon();
+ if (icon != null) {
+ icon.setTint(
+ Utils.getColorAttrDefaultColor(
+ context, com.android.internal.R.attr.colorAccentPrimary));
+ }
+ return icon;
}
private void renderActionButton(WalletCardViewInfo walletCard, boolean isDeviceLocked) {
@@ -231,6 +243,7 @@
if (isDeviceLocked) {
mActionButton.setVisibility(VISIBLE);
mActionButton.setText(R.string.wallet_action_button_label_unlock);
+ mActionButton.setOnClickListener(mDeviceLockedActionOnClickListener);
} else if (actionButtonText != null) {
mActionButton.setText(actionButtonText);
mActionButton.setVisibility(VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 74077a2..92ef850 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -17,7 +17,6 @@
package com.android.systemui.wmshell;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
@@ -35,7 +34,6 @@
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
-import android.view.KeyEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -59,7 +57,6 @@
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.nano.WmShellTraceProto;
import com.android.wm.shell.onehanded.OneHanded;
-import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
@@ -230,10 +227,6 @@
@VisibleForTesting
void initOneHanded(OneHanded oneHanded) {
- int currentMode = mNavigationModeController.addListener(mode ->
- oneHanded.setThreeButtonModeEnabled(mode == NAV_BAR_MODE_3BUTTON));
- oneHanded.setThreeButtonModeEnabled(currentMode == NAV_BAR_MODE_3BUTTON);
-
oneHanded.registerTransitionCallback(new OneHandedTransitionCallback() {
@Override
public void onStartTransition(boolean isEntering) {
@@ -260,32 +253,6 @@
}
});
- oneHanded.registerGestureCallback(new OneHandedGestureEventCallback() {
- @Override
- public void onStart() {
- mSysUiMainExecutor.execute(() -> {
- if (oneHanded.isOneHandedEnabled()) {
- oneHanded.startOneHanded();
- } else if (oneHanded.isSwipeToNotificationEnabled()) {
- mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
- }
- });
- }
-
- @Override
- public void onStop() {
- mSysUiMainExecutor.execute(() -> {
- if (oneHanded.isOneHandedEnabled()) {
- // Log metrics for 3-button navigation mode.
- oneHanded.stopOneHanded(
- OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
- } else if (oneHanded.isSwipeToNotificationEnabled()) {
- mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP);
- }
- });
- }
- });
-
mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onKeyguardBouncerChanged(boolean bouncer) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 8c5f74d..10322f2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -19,30 +19,20 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.smartspace.SmartspaceTarget;
-import android.content.Context;
-import android.content.pm.UserInfo;
import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
-import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
@@ -50,21 +40,14 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin.IntentStarter;
import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Test;
@@ -74,78 +57,54 @@
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Mock
+ private KeyguardClockSwitch mView;
+ @Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private SysuiColorExtractor mColorExtractor;
@Mock
private ClockManager mClockManager;
@Mock
- private KeyguardClockSwitch mView;
- @Mock
- private NotificationIconContainer mNotificationIcons;
- @Mock
- private ClockPlugin mClockPlugin;
- @Mock
- ColorExtractor.GradientColors mGradientColors;
- @Mock
KeyguardSliceViewController mKeyguardSliceViewController;
@Mock
- Resources mResources;
- @Mock
NotificationIconAreaController mNotificationIconAreaController;
@Mock
BroadcastDispatcher mBroadcastDispatcher;
@Mock
- private FeatureFlags mFeatureFlags;
+ BatteryController mBatteryController;
@Mock
- private Executor mExecutor;
+ KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ KeyguardBypassController mBypassController;
+ @Mock
+ LockscreenSmartspaceController mSmartspaceController;
+
+ @Mock
+ Resources mResources;
+ @Mock
+ private ClockPlugin mClockPlugin;
+ @Mock
+ ColorExtractor.GradientColors mGradientColors;
+
+ @Mock
+ private NotificationIconContainer mNotificationIcons;
@Mock
private AnimatableClockView mClockView;
@Mock
private AnimatableClockView mLargeClockView;
@Mock
private FrameLayout mLargeClockFrame;
- @Mock
- BatteryController mBatteryController;
- @Mock
- ConfigurationController mConfigurationController;
- @Mock
- Optional<BcSmartspaceDataPlugin> mOptionalSmartspaceDataProvider;
- @Mock
- BcSmartspaceDataPlugin mSmartspaceDataProvider;
- @Mock
- SmartspaceView mSmartspaceView;
- @Mock
- ActivityStarter mActivityStarter;
- @Mock
- FalsingManager mFalsingManager;
- @Mock
- KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- KeyguardBypassController mBypassController;
- @Mock
- Handler mHandler;
- @Mock
- UserTracker mUserTracker;
- @Mock
- SecureSettings mSecureSettings;
+
+ private final View mFakeSmartspaceView = new View(mContext);
private KeyguardClockSwitchController mController;
private View mStatusArea;
- private static final int USER_ID = 5;
- private static final int MANAGED_USER_ID = 15;
-
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -162,9 +121,9 @@
when(mClockView.getContext()).thenReturn(getContext());
when(mLargeClockView.getContext()).thenReturn(getContext());
- when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
when(mView.isAttachedToWindow()).thenReturn(true);
when(mResources.getString(anyInt())).thenReturn("h:mm");
+ when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController = new KeyguardClockSwitchController(
mView,
mStatusBarStateController,
@@ -173,28 +132,16 @@
mKeyguardSliceViewController,
mNotificationIconAreaController,
mBroadcastDispatcher,
- mFeatureFlags,
- mExecutor,
mBatteryController,
- mConfigurationController,
- mActivityStarter,
- mFalsingManager,
mKeyguardUpdateMonitor,
mBypassController,
- mHandler,
- mUserTracker,
- mSecureSettings,
- mOptionalSmartspaceDataProvider
- );
+ mSmartspaceController);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
mStatusArea = new View(getContext());
when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea);
- when(mOptionalSmartspaceDataProvider.isPresent()).thenReturn(true);
- when(mOptionalSmartspaceDataProvider.get()).thenReturn(mSmartspaceDataProvider);
- when(mSmartspaceDataProvider.getView(any())).thenReturn(mSmartspaceView);
}
@Test
@@ -255,119 +202,41 @@
@Test
public void testSmartspaceEnabledRemovesKeyguardStatusArea() {
- when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
+ when(mSmartspaceController.isEnabled()).thenReturn(true);
+ when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController.init();
assertEquals(View.GONE, mStatusArea.getVisibility());
}
@Test
- public void testSmartspaceEnabledNoDataProviderShowsKeyguardStatusArea() {
- when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
- when(mOptionalSmartspaceDataProvider.isPresent()).thenReturn(false);
- mController.init();
-
- assertEquals(View.VISIBLE, mStatusArea.getVisibility());
- }
-
- @Test
public void testSmartspaceDisabledShowsKeyguardStatusArea() {
- when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(false);
+ when(mSmartspaceController.isEnabled()).thenReturn(false);
mController.init();
assertEquals(View.VISIBLE, mStatusArea.getVisibility());
}
@Test
- public void testThemeChangeNotifiesSmartspace() {
+ public void testDetachRemovesSmartspaceView() {
+ when(mSmartspaceController.isEnabled()).thenReturn(true);
+ when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController.init();
- verify(mSmartspaceView).setPrimaryTextColor(anyInt());
+ verify(mView).addView(eq(mFakeSmartspaceView), anyInt(), any());
- mController.getConfigurationListener().onThemeChanged();
- verify(mSmartspaceView, times(2)).setPrimaryTextColor(anyInt());
+ ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+ verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
+
+ listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
+ verify(mView).removeView(mFakeSmartspaceView);
}
@Test
- public void doNotFilterRegularTarget() {
- setupPrimaryAndManagedUser();
- mController.init();
+ public void testRefresh() {
+ mController.refresh();
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(0);
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
- .thenReturn(0);
-
- mController.getSettingsObserver().onChange(true, null);
-
- SmartspaceTarget t = mock(SmartspaceTarget.class);
- when(t.isSensitive()).thenReturn(false);
- when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
- assertEquals(false, mController.filterSmartspaceTarget(t));
-
- reset(t);
- when(t.isSensitive()).thenReturn(false);
- when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
- assertEquals(false, mController.filterSmartspaceTarget(t));
- }
-
- @Test
- public void filterAllSensitiveTargetsAllUsers() {
- setupPrimaryAndManagedUser();
- mController.init();
-
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(0);
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
- .thenReturn(0);
-
- mController.getSettingsObserver().onChange(true, null);
-
- SmartspaceTarget t = mock(SmartspaceTarget.class);
- when(t.isSensitive()).thenReturn(true);
- when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
- assertEquals(true, mController.filterSmartspaceTarget(t));
-
- reset(t);
- when(t.isSensitive()).thenReturn(true);
- when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
- assertEquals(true, mController.filterSmartspaceTarget(t));
- }
-
- @Test
- public void filterSensitiveManagedUserTargets() {
- setupPrimaryAndManagedUser();
- mController.init();
-
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(1);
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
- .thenReturn(0);
-
- mController.getSettingsObserver().onChange(true, null);
-
- SmartspaceTarget t = mock(SmartspaceTarget.class);
- when(t.isSensitive()).thenReturn(true);
- when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
- assertEquals(false, mController.filterSmartspaceTarget(t));
-
- reset(t);
- when(t.isSensitive()).thenReturn(true);
- when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
- assertEquals(true, mController.filterSmartspaceTarget(t));
- }
-
- private void setupPrimaryAndManagedUser() {
- UserInfo userInfo = mock(UserInfo.class);
- when(userInfo.isManagedProfile()).thenReturn(true);
- when(userInfo.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
- when(mUserTracker.getUserProfiles()).thenReturn(List.of(userInfo));
-
- when(mUserTracker.getUserId()).thenReturn(USER_ID);
- when(mUserTracker.getUserHandle()).thenReturn(new UserHandle(USER_ID));
- }
-
- private void setupPrimaryAndNoManagedUser() {
- when(mUserTracker.getUserProfiles()).thenReturn(Collections.emptyList());
-
- when(mUserTracker.getUserId()).thenReturn(USER_ID);
- when(mUserTracker.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+ verify(mSmartspaceController).requestSmartspaceUpdate();
}
private void verifyAttachment(VerificationMode times) {
@@ -377,25 +246,4 @@
any(ColorExtractor.OnColorsChangedListener.class));
verify(mView, times).updateColors(mGradientColors);
}
-
- private static class SmartspaceView extends View
- implements BcSmartspaceDataPlugin.SmartspaceView {
- SmartspaceView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void registerDataProvider(BcSmartspaceDataPlugin plugin) { }
-
- public void setPrimaryTextColor(int color) { }
-
- public void setDozeAmount(float amount) { }
-
- public void setIntentStarter(IntentStarter intentStarter) { }
-
- public void setFalsingManager(FalsingManager falsingManager) { }
-
- public void setDnd(@Nullable Drawable dndIcon, @Nullable String description) { }
-
- public void setNextAlarm(@Nullable Drawable dndIcon, @Nullable String description) { }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
index 42314bf..6f2c565 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
@@ -81,7 +81,7 @@
mTestableResources.addOverride(
R.bool.can_use_one_handed_bouncer, false);
mTestableResources.addOverride(
- com.android.internal.R.bool.config_enableOneHandedKeyguard, false);
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning, false);
when(mKeyguardSecurityContainerControllerFactory.create(any(
KeyguardSecurityContainer.SecurityCallback.class)))
@@ -150,7 +150,7 @@
mTestableResources.addOverride(
R.bool.can_use_one_handed_bouncer, false);
mTestableResources.addOverride(
- com.android.internal.R.bool.config_enableOneHandedKeyguard, false);
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning, false);
mKeyguardHostViewController.init();
assertEquals(
@@ -161,7 +161,7 @@
mTestableResources.addOverride(
R.bool.can_use_one_handed_bouncer, true);
mTestableResources.addOverride(
- com.android.internal.R.bool.config_enableOneHandedKeyguard, true);
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning, true);
mKeyguardHostViewController.updateResources();
assertEquals(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 9557d5c..f5916e7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -246,7 +246,8 @@
boolean sysuiResourceCanUseOneHandedKeyguard,
SecurityMode securityMode) {
TestableResources testableResources = mContext.getOrCreateTestableResources();
- testableResources.addOverride(com.android.internal.R.bool.config_enableOneHandedKeyguard,
+ testableResources.addOverride(
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning,
deviceConfigCanUseOneHandedKeyguard);
testableResources.addOverride(R.bool.can_use_one_handed_bouncer,
sysuiResourceCanUseOneHandedKeyguard);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 77582bd..29b4405 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -842,10 +842,8 @@
@Test
public void testStartUdfpsServiceBeginsOnKeyguard() {
// GIVEN
- // - bouncer isn't showing
// - status bar state is on the keyguard
// - user has authenticated since boot
- setKeyguardBouncerVisibility(false /* isVisible */);
mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
@@ -854,11 +852,44 @@
}
@Test
+ public void testOccludingAppFingerprintListeningState() {
+ // GIVEN keyguard isn't visible (app occluding)
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mKeyguardUpdateMonitor.setKeyguardOccluded(true);
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(false);
+ when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
+
+ // THEN we shouldn't listen for fingerprints
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isEqualTo(false);
+
+ // THEN we should listen for udfps (hiding of mechanism to actually auth is
+ // controlled by UdfpsKeyguardViewController)
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(true);
+ }
+
+ @Test
+ public void testOccludingAppRequestsFingerprint() {
+ // GIVEN keyguard isn't visible (app occluding)
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mKeyguardUpdateMonitor.setKeyguardOccluded(true);
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(false);
+
+ // WHEN an occluding app requests fp
+ mKeyguardUpdateMonitor.requestFingerprintAuthOnOccludingApp(true);
+
+ // THEN we should listen for fingerprints
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isEqualTo(true);
+
+ // WHEN an occluding app stops requesting fp
+ mKeyguardUpdateMonitor.requestFingerprintAuthOnOccludingApp(false);
+
+ // THEN we shouldn't listen for fingeprints
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isEqualTo(false);
+ }
+
+ @Test
public void testStartUdfpsServiceNoAuthenticationSinceLastBoot() {
- // GIVEN
- // - bouncer isn't showing
- // - status bar state is on the keyguard
- setKeyguardBouncerVisibility(false /* isVisible */);
+ // GIVEN status bar state is on the keyguard
mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
// WHEN user hasn't authenticated since last boot
@@ -871,7 +902,6 @@
@Test
public void testShouldNotListenForUdfps_whenTrustEnabled() {
// GIVEN a "we should listen for udfps" state
- setKeyguardBouncerVisibility(false /* isVisible */);
mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
@@ -886,7 +916,6 @@
@Test
public void testShouldNotListenForUdfps_whenFaceAuthenticated() {
// GIVEN a "we should listen for udfps" state
- setKeyguardBouncerVisibility(false /* isVisible */);
mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
@@ -914,6 +943,34 @@
}
@Test
+ public void testShouldNotUpdateBiometricListeningStateOnStatusBarStateChange() {
+ // GIVEN state for face auth should run aside from StatusBarState
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
+ KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+ mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
+ setKeyguardBouncerVisibility(false /* isVisible */);
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ when(mKeyguardBypassController.canBypass()).thenReturn(true);
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+
+ // WHEN status bar state reports a change to the keyguard that would normally indicate to
+ // start running face auth
+ mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isEqualTo(true);
+
+ // THEN face unlock is not running b/c status bar state changes don't cause biometric
+ // listening state to update
+ assertThat(mKeyguardUpdateMonitor.isFaceUnlockRunning(
+ KeyguardUpdateMonitor.getCurrentUser())).isEqualTo(false);
+
+ // WHEN biometric listening state is updated
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+
+ // THEN face unlock is running
+ assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isEqualTo(true);
+ }
+
+ @Test
public void testRequireUnlockForNfc_Broadcast() {
KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
mKeyguardUpdateMonitor.registerCallback(callback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index b9ce203..ed5cbe2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -67,7 +67,6 @@
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
-import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -87,13 +86,12 @@
private static final Rect ZERO_RECT = new Rect();
- private TestableLooper mTestableLooper;
private ScreenDecorations mScreenDecorations;
private WindowManager mWindowManager;
private DisplayManager mDisplayManager;
private SecureSettings mSecureSettings;
- private Handler mMainHandler;
- private ThreadFactory mThreadFactory;
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private FakeThreadFactory mThreadFactory;
@Mock
private TunerService mTunerService;
@Mock
@@ -107,10 +105,10 @@
public void setup() {
MockitoAnnotations.initMocks(this);
- mTestableLooper = TestableLooper.get(this);
- mMainHandler = new Handler(mTestableLooper.getLooper());
+ Handler mainHandler = new Handler(TestableLooper.get(this).getLooper());
mSecureSettings = new FakeSettings();
- mThreadFactory = new FakeThreadFactory(new FakeExecutor(new FakeSystemClock()));
+ mThreadFactory = new FakeThreadFactory(mExecutor);
+ mThreadFactory.setHandler(mainHandler);
mWindowManager = mock(WindowManager.class);
WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
@@ -124,30 +122,25 @@
when(mDisplayManager.getDisplay(anyInt())).thenReturn(display);
mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
- mScreenDecorations = spy(new ScreenDecorations(mContext, mMainHandler, mSecureSettings,
+ mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
mThreadFactory) {
@Override
public void start() {
super.start();
- mTestableLooper.processAllMessages();
- }
-
- @Override
- Handler startHandlerThread() {
- return new Handler(mTestableLooper.getLooper());
+ mExecutor.runAllReady();
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mTestableLooper.processAllMessages();
+ mExecutor.runAllReady();
}
@Override
public void onTuningChanged(String key, String newValue) {
super.onTuningChanged(key, newValue);
- mTestableLooper.processAllMessages();
+ mExecutor.runAllReady();
}
});
reset(mTunerService);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index 1d9eaae..77286b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -37,7 +37,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import org.junit.Before;
@@ -67,9 +68,12 @@
@Mock
private ModeSwitchesController mModeSwitchesController;
@Mock
- private NavigationModeController mNavigationModeController;
+ private SysUiState mSysUiState;
@Mock
private IRemoteMagnificationAnimationCallback mAnimationCallback;
+ @Mock
+ private OverviewProxyService mOverviewProxyService;
+
private IWindowMagnificationConnection mIWindowMagnificationConnection;
private WindowMagnification mWindowMagnification;
@@ -83,8 +87,8 @@
}).when(mAccessibilityManager).setWindowMagnificationConnection(
any(IWindowMagnificationConnection.class));
mWindowMagnification = new WindowMagnification(getContext(),
- getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
- mNavigationModeController);
+ getContext().getMainThreadHandler(), mCommandQueue,
+ mModeSwitchesController, mSysUiState, mOverviewProxyService);
mWindowMagnification.mAnimationControllerSupplier = new FakeAnimationControllerSupplier(
mContext.getSystemService(DisplayManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 4e4c33a..045fb57f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -41,6 +41,7 @@
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.model.SysUiState;
import org.junit.After;
import org.junit.Before;
@@ -83,6 +84,8 @@
IRemoteMagnificationAnimationCallback mAnimationCallback;
@Mock
IRemoteMagnificationAnimationCallback mAnimationCallback2;
+ @Mock
+ SysUiState mSysUiState;
private SpyWindowMagnificationController mController;
private WindowMagnificationController mSpyController;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@@ -98,7 +101,7 @@
mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2;
mController = new SpyWindowMagnificationController(mContext, mHandler,
mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(),
- mWindowMagnifierCallback);
+ mWindowMagnifierCallback, mSysUiState);
mSpyController = mController.getSpyController();
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
mContext, mController, newValueAnimator());
@@ -394,6 +397,13 @@
verify(mSpyController).onConfigurationChanged(100);
}
+ @Test
+ public void updateSysUiStateFlag_passThrough() {
+ mWindowMagnificationAnimationController.updateSysUiStateFlag();
+
+ verify(mSpyController).updateSysUIStateFlag();
+ }
+
private void verifyFinalSpec(float expectedScale, float expectedCenterX,
float expectedCenterY) {
assertEquals(expectedScale, mController.getScale(), 0f);
@@ -440,9 +450,9 @@
SpyWindowMagnificationController(Context context, Handler handler,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
- WindowMagnifierCallback callback) {
+ WindowMagnifierCallback callback, SysUiState sysUiState) {
super(context, handler, sfVsyncFrameProvider, mirrorWindowControl, transaction,
- callback);
+ callback, sysUiState);
mSpyController = Mockito.mock(WindowMagnificationController.class);
}
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 7d617db..b8734df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -17,12 +17,15 @@
package com.android.systemui.accessibility;
import static android.view.Choreographer.FrameCallback;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+import static android.view.WindowInsets.Type.systemGestures;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItems;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -33,6 +36,9 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -40,49 +46,60 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Handler;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.testing.TestableResources;
+import android.text.TextUtils;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.util.leak.ReferenceTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-@SmallTest
+@LargeTest
@RunWith(AndroidTestingRunner.class)
public class WindowMagnificationControllerTest extends SysuiTestCase {
+ private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
@Mock
- Handler mHandler;
+ private Handler mHandler;
@Mock
- SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+ private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
- MirrorWindowControl mMirrorWindowControl;
+ private MirrorWindowControl mMirrorWindowControl;
@Mock
- WindowMagnifierCallback mWindowMagnifierCallback;
- @Mock
- SurfaceControl.Transaction mTransaction;
- @Mock
+ private WindowMagnifierCallback mWindowMagnifierCallback;
+ @Mock (answer = Answers.RETURNS_DEEP_STUBS)
+ private SurfaceControl.Transaction mTransaction;
private WindowManager mWindowManager;
+ private SysUiState mSysUiState = new SysUiState();
private Resources mResources;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
@@ -93,37 +110,30 @@
MockitoAnnotations.initMocks(this);
mContext = Mockito.spy(getContext());
mInstrumentation = InstrumentationRegistry.getInstrumentation();
- WindowManager wm = mContext.getSystemService(WindowManager.class);
- doAnswer(invocation ->
- wm.getMaximumWindowMetrics()
- ).when(mWindowManager).getMaximumWindowMetrics();
- doAnswer(invocation ->
- wm.getCurrentWindowMetrics()
- ).when(mWindowManager).getCurrentWindowMetrics();
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ mWindowManager = spy(new TestableWindowManager(wm));
+
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
doAnswer(invocation -> {
- mMirrorView = invocation.getArgument(0);
- WindowManager.LayoutParams lp = invocation.getArgument(1);
- mMirrorView.setLayoutParams(lp);
- return null;
- }).when(mWindowManager).addView(any(View.class), any(WindowManager.LayoutParams.class));
- doAnswer(invocation -> {
- mMirrorView = null;
- return null;
- }).when(mWindowManager).removeView(any(View.class));
- doAnswer(invocation -> {
FrameCallback callback = invocation.getArgument(0);
callback.doFrame(0);
return null;
}).when(mSfVsyncFrameProvider).postFrameCallback(
any(FrameCallback.class));
- when(mTransaction.remove(any())).thenReturn(mTransaction);
- when(mTransaction.setGeometry(any(), any(), any(),
- anyInt())).thenReturn(mTransaction);
+ doAnswer(invocation -> {
+ final Runnable runnable = invocation.getArgument(0);
+ runnable.run();
+ return null;
+ }).when(mHandler).post(
+ any(Runnable.class));
+
+ mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
+
mResources = getContext().getOrCreateTestableResources().getResources();
mWindowMagnificationController = new WindowMagnificationController(mContext,
mHandler, mSfVsyncFrameProvider,
- mMirrorWindowControl, mTransaction, mWindowMagnifierCallback);
+ mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
+
verify(mMirrorWindowControl).setWindowDelegate(
any(MirrorWindowControl.MirrorWindowDelegate.class));
}
@@ -135,12 +145,21 @@
}
@Test
- public void enableWindowMagnification_showControl() {
+ public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN);
});
+
verify(mMirrorWindowControl).showControl();
+ ArgumentCaptor<Rect> boundsCaptor = ArgumentCaptor.forClass(Rect.class);
+ verify(mWindowMagnifierCallback,
+ timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onWindowMagnifierBoundsChanged(
+ eq(mContext.getDisplayId()), boundsCaptor.capture());
+ final Rect actualBounds = new Rect();
+ mMirrorView.getBoundsOnScreen(actualBounds);
+ assertEquals(actualBounds, boundsCaptor.getValue());
+
}
@Test
@@ -158,6 +177,25 @@
}
@Test
+ public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() {
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ bounds.bottom);
+ });
+ ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.deleteWindowMagnification();
+ });
+
+ verify(mMirrorWindowControl).destroyControl();
+ assertFalse(hasMagnificationOverlapFlag());
+ }
+
+ @Test
public void moveMagnifier_schedulesFrame() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -210,7 +248,8 @@
});
assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
- verify(mWindowManager).updateViewLayout(any(), any());
+ // The first invocation is called when the surface is created.
+ verify(mWindowManager, times(2)).updateViewLayout(any(), any());
}
@Test
@@ -318,17 +357,6 @@
}
@Test
- public void onNavigationModeChanged_updateMirrorViewLayout() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.onNavigationModeChanged(NAV_BAR_MODE_GESTURAL);
- });
-
- verify(mWindowManager).updateViewLayout(eq(mMirrorView), any());
- }
-
- @Test
public void enableWindowMagnification_hasA11yWindowTitle() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -353,16 +381,12 @@
final TestableResources testableResources = getContext().getOrCreateTestableResources();
testableResources.addOverride(com.android.internal.R.string.android_system_label,
newA11yWindowTitle);
- when(mContext.getResources()).thenReturn(testableResources.getResources());
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
});
- ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
- WindowManager.LayoutParams.class);
- verify(mWindowManager).updateViewLayout(eq(mMirrorView), paramsArgumentCaptor.capture());
- assertEquals(newA11yWindowTitle, paramsArgumentCaptor.getValue().accessibilityTitle);
+ assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle()));
}
@Test
@@ -386,4 +410,95 @@
}
fail("mMirrorView scale is not changed");
}
+
+ @Test
+ public void moveWindowMagnificationToTheBottom_enabled_overlapFlagIsTrue() {
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.moveWindowMagnifier(0, bounds.height());
+ });
+
+ ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag());
+ }
+
+ private CharSequence getAccessibilityWindowTitle() {
+ if (mMirrorView == null) {
+ return null;
+ }
+ WindowManager.LayoutParams layoutParams =
+ (WindowManager.LayoutParams) mMirrorView.getLayoutParams();
+ return layoutParams.accessibilityTitle;
+ }
+
+ private boolean hasMagnificationOverlapFlag() {
+ return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0;
+ }
+
+ private class TestableWindowManager implements WindowManager {
+
+ private final WindowManager mWindowManager;
+
+ TestableWindowManager(WindowManager windowManager) {
+ mWindowManager = windowManager;
+ }
+
+ @Override
+ public Display getDefaultDisplay() {
+ return mWindowManager.getDefaultDisplay();
+ }
+
+ @Override
+ public void removeViewImmediate(View view) {
+ mWindowManager.removeViewImmediate(view);
+ }
+
+ @Override
+ public void requestAppKeyboardShortcuts(KeyboardShortcutsReceiver receiver, int deviceId) {
+ mWindowManager.requestAppKeyboardShortcuts(receiver, deviceId);
+ }
+
+ @Override
+ public Region getCurrentImeTouchRegion() {
+ return mWindowManager.getCurrentImeTouchRegion();
+ }
+
+ @Override
+ public void addView(View view, ViewGroup.LayoutParams params) {
+ mMirrorView = view;
+ mWindowManager.addView(view, params);
+ }
+
+ @Override
+ public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
+ mWindowManager.updateViewLayout(view, params);
+ }
+
+ @Override
+ public void removeView(View view) {
+ mMirrorView = null;
+ mWindowManager.removeView(view);
+ }
+
+ @Override
+ public WindowMetrics getCurrentWindowMetrics() {
+ final Insets systemGesturesInsets = Insets.of(0, 0, 0, 10);
+ final WindowInsets insets = new WindowInsets.Builder()
+ .setInsets(systemGestures(), systemGesturesInsets)
+ .build();
+ final WindowMetrics windowMetrics = new WindowMetrics(
+ mWindowManager.getCurrentWindowMetrics().getBounds(), insets);
+ return windowMetrics;
+ }
+
+ @Override
+ public WindowMetrics getMaximumWindowMetrics() {
+ return mWindowManager.getMaximumWindowMetrics();
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index 7833114..6ef7cc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -16,15 +16,21 @@
package com.android.systemui.accessibility;
+import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
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.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -36,12 +42,14 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
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;
@@ -50,17 +58,21 @@
@TestableLooper.RunWithLooper
public class WindowMagnificationTest extends SysuiTestCase {
+ private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
@Mock
private AccessibilityManager mAccessibilityManager;
@Mock
private ModeSwitchesController mModeSwitchesController;
@Mock
- private NavigationModeController mNavigationModeController;
+ private SysUiState mSysUiState;
@Mock
private IWindowMagnificationConnectionCallback mConnectionCallback;
+ @Mock
+ private OverviewProxyService mOverviewProxyService;
+
private CommandQueue mCommandQueue;
private WindowMagnification mWindowMagnification;
-
+ private OverviewProxyListener mOverviewProxyListener;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -72,11 +84,18 @@
}).when(mAccessibilityManager).setWindowMagnificationConnection(
any(IWindowMagnificationConnection.class));
+ when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+
mCommandQueue = new CommandQueue(getContext());
mWindowMagnification = new WindowMagnification(getContext(),
getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
- mNavigationModeController);
+ mSysUiState, mOverviewProxyService);
mWindowMagnification.start();
+
+ final ArgumentCaptor<OverviewProxyListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(OverviewProxyListener.class);
+ verify(mOverviewProxyService).addCallback(listenerArgumentCaptor.capture());
+ mOverviewProxyListener = listenerArgumentCaptor.getValue();
}
@Test
@@ -99,10 +118,9 @@
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.onWindowMagnifierBoundsChanged(Display.DEFAULT_DISPLAY, testBounds);
+ mWindowMagnification.onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds);
- verify(mConnectionCallback).onWindowMagnifierBoundsChanged(Display.DEFAULT_DISPLAY,
- testBounds);
+ verify(mConnectionCallback).onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds);
}
@Test
@@ -111,10 +129,9 @@
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.onPerformScaleAction(Display.DEFAULT_DISPLAY, newScale);
+ mWindowMagnification.onPerformScaleAction(TEST_DISPLAY, newScale);
- verify(mConnectionCallback).onPerformScaleAction(eq(Display.DEFAULT_DISPLAY),
- eq(newScale));
+ verify(mConnectionCallback).onPerformScaleAction(TEST_DISPLAY, newScale);
}
@Test
@@ -122,9 +139,9 @@
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.onAccessibilityActionPerformed(Display.DEFAULT_DISPLAY);
+ mWindowMagnification.onAccessibilityActionPerformed(TEST_DISPLAY);
- verify(mConnectionCallback).onAccessibilityActionPerformed(eq(Display.DEFAULT_DISPLAY));
+ verify(mConnectionCallback).onAccessibilityActionPerformed(TEST_DISPLAY);
}
@Test
@@ -135,4 +152,42 @@
verify(mModeSwitchesController).onConfigurationChanged(anyInt());
}
+
+ @Test
+ public void overviewProxyIsConnected_noController_resetFlag() {
+ mOverviewProxyListener.onConnectionChanged(true);
+
+ verify(mSysUiState).setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false);
+ verify(mSysUiState).commitUpdate(mContext.getDisplayId());
+ }
+
+ @Test
+ public void overviewProxyIsConnected_controllerIsAvailable_updateSysUiStateFlag() {
+ final WindowMagnificationAnimationController mController = mock(
+ WindowMagnificationAnimationController.class);
+ mWindowMagnification.mAnimationControllerSupplier = new FakeAnimationControllerSupplier(
+ mContext.getSystemService(DisplayManager.class), mController);
+ mWindowMagnification.mAnimationControllerSupplier.get(TEST_DISPLAY);
+
+ mOverviewProxyListener.onConnectionChanged(true);
+
+ verify(mController).updateSysUiStateFlag();
+ }
+
+ private static class FakeAnimationControllerSupplier extends
+ DisplayIdIndexSupplier<WindowMagnificationAnimationController> {
+
+ private final WindowMagnificationAnimationController mController;
+
+ FakeAnimationControllerSupplier(DisplayManager displayManager,
+ WindowMagnificationAnimationController controller) {
+ super(displayManager);
+ mController = controller;
+ }
+
+ @Override
+ protected WindowMagnificationAnimationController createInstance(Display display) {
+ return mController;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
new file mode 100644
index 0000000..46c930f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.testing.AndroidTestingRunner;
+import android.text.SpannableStringBuilder;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** Tests for {@link AnnotationLinkSpan}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class AnnotationLinkSpanTest extends SysuiTestCase {
+
+ private AnnotationLinkSpan.LinkInfo mLinkInfo;
+
+ @Before
+ public void setUp() {
+ mLinkInfo = new AnnotationLinkSpan.LinkInfo(
+ AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
+ mock(View.OnClickListener.class));
+ }
+
+ @Test
+ public void linkifyText_textAttachedWithSpan() {
+ final CharSequence text = getContext().getText(
+ R.string.accessibility_floating_button_migration_tooltip);
+ final SpannableStringBuilder builder =
+ (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, mLinkInfo);
+ final int AnnotationLinkSpanNum =
+ builder.getSpans(/* queryStart= */ 0, builder.length(),
+ AnnotationLinkSpan.class).length;
+
+ assertThat(AnnotationLinkSpanNum).isEqualTo(1);
+ }
+
+ @Test
+ public void linkifyText_withoutAnnotationTag_textWithoutSpan() {
+ final CharSequence text = "text without any annotation tag";
+ final SpannableStringBuilder builder =
+ (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, mLinkInfo);
+ final int AnnotationLinkSpanNum =
+ builder.getSpans(/* queryStart= */ 0, builder.length(),
+ AnnotationLinkSpan.class).length;
+
+ assertThat(AnnotationLinkSpanNum).isEqualTo(0);
+ }
+
+ @Test
+ public void linkifyText_twoLinkInfoWithSameAnnotation_listenerInvoked() {
+ final AtomicBoolean isClicked = new AtomicBoolean(false);
+ final CharSequence text = getContext().getText(
+ R.string.accessibility_floating_button_migration_tooltip);
+ final View.OnClickListener firstListener = v -> isClicked.set(true);
+ final AnnotationLinkSpan.LinkInfo firstLinkInfo = new AnnotationLinkSpan.LinkInfo(
+ AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION, firstListener);
+
+ final SpannableStringBuilder builder =
+ (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, firstLinkInfo, mLinkInfo);
+ final AnnotationLinkSpan[] firstAnnotationLinkSpan =
+ builder.getSpans(/* queryStart= */ 0, builder.length(),
+ AnnotationLinkSpan.class);
+ firstAnnotationLinkSpan[0].onClick(mock(View.class));
+
+ assertThat(isClicked.get()).isTrue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java
new file mode 100644
index 0000000..6db5761
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.MotionEventHelper;
+
+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;
+
+/** Tests for {@link BaseTooltipView}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class BaseTooltipViewTest extends SysuiTestCase {
+
+ @Mock
+ private WindowManager mWindowManager;
+
+ private AccessibilityFloatingMenuView mMenuView;
+ private BaseTooltipView mToolTipView;
+
+ private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
+ mWindowManager).getMaximumWindowMetrics();
+ mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
+ mMenuView = new AccessibilityFloatingMenuView(mContext);
+ mToolTipView = new BaseTooltipView(mContext, mMenuView);
+ }
+
+ @Test
+ public void showToolTipView_success() {
+ mToolTipView.show();
+
+ verify(mWindowManager).addView(eq(mToolTipView), any(WindowManager.LayoutParams.class));
+ }
+
+ @Test
+ public void touchOutsideWhenToolTipViewShown_dismiss() {
+ final MotionEvent outsideEvent =
+ mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0,
+ /* eventTime= */1,
+ MotionEvent.ACTION_OUTSIDE,
+ /* x= */ 0,
+ /* y= */ 0);
+
+ mToolTipView.show();
+ mToolTipView.dispatchTouchEvent(outsideEvent);
+
+ verify(mWindowManager).removeView(mToolTipView);
+ }
+
+ @Test
+ public void getAccessibilityActionList_matchResult() {
+ final AccessibilityNodeInfo infos = new AccessibilityNodeInfo();
+ mToolTipView.onInitializeAccessibilityNodeInfo(infos);
+
+ assertThat(infos.getActionList().size()).isEqualTo(1);
+ }
+
+ @Test
+ public void accessibilityAction_dismiss_success() {
+ final BaseTooltipView tooltipView =
+ spy(new BaseTooltipView(mContext, mMenuView));
+
+ final boolean isActionPerformed =
+ tooltipView.performAccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS.getId(),
+ /* arguments= */ null);
+
+ assertThat(isActionPerformed).isTrue();
+ verify(tooltipView).hide();
+ }
+
+ @After
+ public void tearDown() {
+ mToolTipView.hide();
+ mMotionEventHelper.recycleEvents();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java
new file mode 100644
index 0000000..41b948f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.MotionEventHelper;
+
+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;
+
+/** Tests for {@link DockTooltipView}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DockTooltipViewTest extends SysuiTestCase {
+
+ @Mock
+ private WindowManager mWindowManager;
+
+ private AccessibilityFloatingMenuView mMenuView;
+ private DockTooltipView mDockTooltipView;
+ private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
+ mWindowManager).getMaximumWindowMetrics();
+ mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
+ mMenuView = spy(new AccessibilityFloatingMenuView(mContext));
+ mDockTooltipView = new DockTooltipView(mContext, mMenuView);
+ }
+
+ @Test
+ public void showTooltip_success() {
+ mDockTooltipView.show();
+
+ verify(mMenuView).startTranslateXAnimation();
+ verify(mWindowManager).addView(eq(mDockTooltipView), any(WindowManager.LayoutParams.class));
+ }
+
+ @Test
+ public void hideTooltip_success() {
+ mDockTooltipView.show();
+ mDockTooltipView.hide();
+
+ verify(mMenuView).stopTranslateXAnimation();
+ verify(mWindowManager).removeView(mDockTooltipView);
+ }
+
+ @Test
+ public void touchOutsideWhenToolTipViewShown_stopAnimation() {
+ final MotionEvent outsideEvent =
+ mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0,
+ /* eventTime= */ 1,
+ MotionEvent.ACTION_OUTSIDE,
+ /* x= */ 0,
+ /* y= */ 0);
+
+ mDockTooltipView.show();
+ mDockTooltipView.dispatchTouchEvent(outsideEvent);
+
+ verify(mMenuView).stopTranslateXAnimation();
+ }
+
+ @After
+ public void tearDown() {
+ mMotionEventHelper.recycleEvents();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java
new file mode 100644
index 0000000..c5bd2fe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link MigrationTooltipView}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MigrationTooltipViewTest extends SysuiTestCase {
+
+ private TextView mTextView;
+
+ @Before
+ public void setUp() {
+ final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext);
+ final MigrationTooltipView toolTipView = new MigrationTooltipView(mContext, menuView);
+ mTextView = toolTipView.findViewById(R.id.text);
+ }
+
+ @Test
+ public void onCreate_setLinkMovementMethod() {
+ assertThat(mTextView.getMovementMethod()).isInstanceOf(LinkMovementMethod.class);
+ }
+
+ @Test
+ public void onCreate_setDescription_matchTextAndSpanNum() {
+ final CharSequence expectedTextWithoutSpan =
+ AnnotationLinkSpan.linkify(mContext.getText(
+ R.string.accessibility_floating_button_migration_tooltip)).toString();
+ final SpannableString spannableString = (SpannableString) mTextView.getText();
+ final int AnnotationLinkSpanNum =
+ spannableString.getSpans(/* queryStart= */ 0, spannableString.length(),
+ AnnotationLinkSpan.class).length;
+
+ assertThat(AnnotationLinkSpanNum).isEqualTo(1);
+ assertThat(mTextView.getText().toString().contentEquals(expectedTextWithoutSpan)).isTrue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 875696a..46c1848 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -35,6 +35,7 @@
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
@@ -69,6 +70,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -93,6 +95,8 @@
@Mock
private WindowManager mWindowManager;
@Mock
+ private UdfpsHbmCallback mHbmCallback;
+ @Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private StatusBar mStatusBar;
@@ -114,6 +118,8 @@
private AccessibilityManager mAccessibilityManager;
@Mock
private ScreenLifecycle mScreenLifecycle;
+ @Mock
+ private Vibrator mVibrator;
private FakeExecutor mFgExecutor;
@@ -170,7 +176,9 @@
mFalsingManager,
mPowerManager,
mAccessibilityManager,
- mScreenLifecycle);
+ mScreenLifecycle,
+ mVibrator,
+ Optional.of(mHbmCallback));
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -302,4 +310,29 @@
// THEN no illumination because screen is off
verify(mUdfpsView, never()).startIllumination(any());
}
+
+ @Test
+ public void playHapticOnTouchUdfpsArea() throws RemoteException {
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN that the overlay is showing
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+ IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ // WHEN ACTION_DOWN is received
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ downEvent.recycle();
+ MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+ moveEvent.recycle();
+
+ // THEN click haptic is played
+ verify(mVibrator).vibrate(mUdfpsController.mEffectClick,
+ UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 3a657c8..a1f283b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -18,10 +18,12 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -55,6 +57,8 @@
@Mock
private UdfpsKeyguardView mView;
@Mock
+ private Context mResourceContext;
+ @Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private StatusBar mStatusBar;
@@ -90,6 +94,8 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mView.getContext()).thenReturn(mResourceContext);
+ when(mResourceContext.getString(anyInt())).thenReturn("test string");
when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false);
when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true);
mController = new UdfpsKeyguardViewController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index bc24445..3c41216 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -21,6 +21,7 @@
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -185,7 +186,7 @@
}
@Test
- public void testAvoidDozing() {
+ public void testAvoidDozingNotPulsing() {
MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
@@ -199,4 +200,21 @@
mFalsingCollector.onTouchEvent(up);
verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
}
+
+ @Test
+ public void testAvoidDozingButPulsing() {
+ MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mStatusBarStateController.isPulsing()).thenReturn(true);
+
+ // Nothing passed initially
+ mFalsingCollector.onTouchEvent(down);
+ verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+
+ // Up event would flushes
+ mFalsingCollector.onTouchEvent(up);
+ verify(mFalsingDataProvider, times(2)).onMotionEvent(any(MotionEvent.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
index c678f46..ecefb55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
@@ -115,7 +115,7 @@
`when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(STRONG_AUTH_NOT_REQUIRED)
`when`(keyguardStateController.isUnlocked()).thenReturn(false)
- `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt()))
+ `when`(secureSettings.getInt(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS), anyInt()))
.thenReturn(0)
val component = setupComponent(true)
@@ -127,7 +127,7 @@
`when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(STRONG_AUTH_NOT_REQUIRED)
`when`(keyguardStateController.isUnlocked()).thenReturn(false)
- `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt()))
+ `when`(secureSettings.getInt(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS), anyInt()))
.thenReturn(1)
val component = setupComponent(true)
@@ -136,7 +136,7 @@
@Test
fun testFeatureEnabledAndCanShowWhileUnlockedVisibility() {
- `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt()))
+ `when`(secureSettings.getInt(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS), anyInt()))
.thenReturn(0)
`when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(STRONG_AUTH_NOT_REQUIRED)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 80716f9..003368d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -39,6 +39,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.IWindowManager;
+import android.view.View;
import android.view.WindowManagerPolicyConstants;
import androidx.test.filters.SmallTest;
@@ -172,6 +173,44 @@
}
@Test
+ public void testShouldLogClose_backButton() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+ dialog.onBackPressed();
+ mTestableLooper.processAllMessages();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_BACK);
+ }
+
+ @Test
+ public void testShouldLogOnTapOutside() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+ View container = dialog.findViewById(com.android.systemui.R.id.global_actions_container);
+ container.callOnClick();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ }
+
+ @Test
public void testShouldLogBugreportPress() throws InterruptedException {
GlobalActionsDialog.BugReportAction bugReportAction =
mGlobalActionsDialogLite.makeBugReportActionForTesting();
@@ -286,4 +325,44 @@
assertThat(mGlobalActionsDialogLite.mOverflowItems).isEmpty();
assertThat(mGlobalActionsDialogLite.mPowerItems).isEmpty();
}
+
+ @Test
+ public void testShouldLogLockdownPress() {
+ GlobalActionsDialogLite.LockDownAction lockDownAction =
+ mGlobalActionsDialogLite.new LockDownAction();
+ lockDownAction.onPress();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_LOCKDOWN_PRESS);
+ }
+
+ @Test
+ public void testShouldLogShutdownPress() {
+ GlobalActionsDialogLite.ShutDownAction shutDownAction =
+ mGlobalActionsDialogLite.new ShutDownAction();
+ shutDownAction.onPress();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SHUTDOWN_PRESS);
+ }
+
+ @Test
+ public void testShouldLogShutdownLongPress() {
+ GlobalActionsDialogLite.ShutDownAction shutDownAction =
+ mGlobalActionsDialogLite.new ShutDownAction();
+ shutDownAction.onLongPress();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
+ }
+
+ @Test
+ public void testShouldLogRebootPress() {
+ GlobalActionsDialogLite.RestartAction restartAction =
+ mGlobalActionsDialogLite.new RestartAction();
+ restartAction.onPress();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_PRESS);
+ }
+
+ @Test
+ public void testShouldLogRebootLongPress() {
+ GlobalActionsDialogLite.RestartAction restartAction =
+ mGlobalActionsDialogLite.new RestartAction();
+ restartAction.onLongPress();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
+ }
}
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 dd3a192..e1f1dc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -289,6 +289,7 @@
captor.value.onLongClick(holder.player)
verify(mediaViewController, never()).openGuts()
+ verify(mediaViewController).closeGuts(false)
}
@Test
@@ -324,7 +325,8 @@
assertThat(dismiss.isEnabled).isEqualTo(true)
dismiss.callOnClick()
val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java)
- verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean())
+ verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean(),
+ eq(false))
captor.value.onDismiss()
verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index bfd60b96..fc0506a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -30,6 +30,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito
@@ -228,37 +229,39 @@
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noMedia_nonEmptyRecommendation_usesSmartspace() {
+ fun testOnSmartspaceMediaDataLoaded_noMedia_nonEmptyRec_prioritizesSmartspace() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+ verify(listener)
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noMedia_emptyRecommendation_showsNothing() {
+ fun testOnSmartspaceMediaDataLoaded_noMedia_emptyRec_showsNothing() {
`when`(smartspaceData.iconGrid).thenReturn(listOf())
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener, never())
- .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), anyBoolean())
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noRecentMedia_nonEmptyRecommendation_usesSmartspace() {
+ fun testOnSmartspaceMediaDataLoaded_noRecentMedia_nonEmptyRec_prioritizesSmartspace() {
val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+ verify(listener)
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noRecentMedia_emptyRecommendation_showsNothing() {
+ fun testOnSmartspaceMediaDataLoaded_noRecentMedia_emptyRec_showsNothing() {
`when`(smartspaceData.iconGrid).thenReturn(listOf())
val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
@@ -267,12 +270,14 @@
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener, never())
- .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), anyBoolean())
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
}
@Test
- fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_usesMedia() {
+ fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_emptyRec_usesMedia() {
+ `when`(smartspaceData.iconGrid).thenReturn(listOf())
+
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
@@ -285,19 +290,41 @@
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+ // Smartspace update shouldn't be propagated for the empty rec list.
+ verify(listener, never())
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), anyBoolean())
}
@Test
- fun testOnSmartspaceMediaDataRemoved_usedSmartspace_clearsMedia() {
+ fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_nonEmptyRec_usesBoth() {
+ // WHEN we have media that was recently played, but not currently active
+ val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent))
+
+ // AND we get a smartspace signal
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+ // THEN we should tell listeners to treat the media as active instead
+ val dataCurrentAndActive = dataCurrent.copy(active = true)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive))
+ assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+ // Smartspace update should also be propagated but not prioritized.
+ verify(listener)
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+ }
+
+ @Test
+ fun testOnSmartspaceMediaDataRemoved_usedSmartspace_clearsSmartspace() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
- assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
+ assertThat(mediaDataFilter.hasSmartspace).isFalse()
}
@Test
- fun testOnSmartspaceMediaDataRemoved_usedMedia_clearsMedia() {
+ fun testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() {
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -305,6 +332,8 @@
mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrent))
+ verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
+ assertThat(mediaDataFilter.hasSmartspace).isFalse()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index acfc513..59527f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -28,6 +28,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
@@ -41,7 +42,6 @@
private const val KEY = "KEY"
private const val KEY_2 = "KEY_2"
private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
-private const val KEY_NONE_MEDIA_SMARTSPACE = "NONE_MEDIA_SMARTSPACE_ID"
private const val PACKAGE_NAME = "com.android.systemui"
private const val APP_NAME = "SystemUI"
private const val SESSION_ARTIST = "artist"
@@ -364,13 +364,14 @@
fun testOnSmartspaceMediaDataLoaded_hasNewMediaTarget_callsListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
verify(listener).onSmartspaceMediaDataLoaded(
- eq(KEY_MEDIA_SMARTSPACE), eq(mediaSmartspaceTarget))
+ eq(KEY_MEDIA_SMARTSPACE), eq(mediaSmartspaceTarget), eq(false))
}
@Test
fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_notCallsListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf())
- verify(listener, never()).onSmartspaceMediaDataLoaded(anyObject(), anyObject())
+ verify(listener, never())
+ .onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 406f40c..c6aef4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -24,11 +24,13 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import org.junit.Assert.assertNotNull
@@ -73,11 +75,17 @@
@Mock
private lateinit var mediaCarouselController: MediaCarouselController
@Mock
+ private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
+ @Mock
private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock
+ private lateinit var configurationController: ConfigurationController
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
+ @Captor
+ private lateinit var statusBarCallback: ArgumentCaptor<(StatusBarStateController.StateListener)>
@JvmField
@Rule
val mockito = MockitoJUnit.rule()
@@ -93,13 +101,17 @@
bypassController,
mediaCarouselController,
notificationLockscreenUserManager,
+ configurationController,
wakefulnessLifecycle,
statusBarKeyguardViewManager)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
+ verify(statusBarStateController).addCallback(statusBarCallback.capture())
setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN)
setupHost(qsHost, MediaHierarchyManager.LOCATION_QS)
setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS)
`when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+ `when`(mediaCarouselController.mediaCarouselScrollHandler)
+ .thenReturn(mediaCarouselScrollHandler)
// We'll use the viewmanager to verify a few calls below, let's reset this.
clearInvocations(mediaCarouselController)
}
@@ -153,4 +165,11 @@
verify(mediaCarouselController).closeGuts()
}
+
+ @Test
+ fun testCloseGutsWhenDoze() {
+ statusBarCallback.value.onDozingChanged(true)
+
+ verify(mediaCarouselController).closeGuts()
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 5c70a4ef2..7172307 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.people;
import static android.app.Notification.CATEGORY_MISSED_CALL;
+import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
@@ -25,6 +26,7 @@
import static android.app.people.PeopleSpaceTile.SHOW_CONTACTS;
import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS;
import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT;
@@ -127,6 +129,9 @@
.build();
@Mock
+ private Icon mIcon;
+
+ @Mock
private Context mMockContext;
@Mock
private PackageManager mPackageManager;
@@ -169,7 +174,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
PERSON_TILE_WITHOUT_NOTIFICATION, mOptions).getViews();
@@ -218,7 +223,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithLastInteraction, mOptions).getViews();
@@ -278,7 +283,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithAvailabilityAndNewStory, mOptions).getViews();
@@ -340,7 +345,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusTemplate, mOptions).getViews();
@@ -376,6 +381,8 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.image).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -403,7 +410,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusTemplate, mOptions).getViews();
@@ -413,6 +420,8 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.image).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -426,6 +435,55 @@
}
@Test
+ public void testCreateRemoteViewsWithStatusTemplateWithImageOnMediumAndLarge() {
+ PeopleSpaceTile tileWithIconInStatusTemplate =
+ PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
+ Arrays.asList(new ConversationStatus.Builder(PERSON_TILE.getId(),
+ ACTIVITY_ANNIVERSARY).setDescription("Anniversary").setAvailability(
+ AVAILABILITY_AVAILABLE).setIcon(mIcon).build())).build();
+ RemoteViews views = getPeopleTileViewHelper(
+ tileWithIconInStatusTemplate, mOptions).getViews();
+ View result = views.apply(mContext, null);
+
+ assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.VISIBLE, result.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.image).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // Has status.
+ TextView statusContent = (TextView) result.findViewById(R.id.name);
+ assertEquals(statusContent.getText(), "Anniversary");
+ assertThat(statusContent.getMaxLines()).isEqualTo(1);
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_large));
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
+ getSizeInDp(R.dimen.required_height_for_large));
+ RemoteViews largeView = getPeopleTileViewHelper(
+ tileWithIconInStatusTemplate, mOptions).getViews();
+ View largeResult = largeView.apply(mContext, null);
+
+ assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.image).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ View personIcon = largeResult.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
+ // Has status content.
+ statusContent = (TextView) largeResult.findViewById(R.id.text_content);
+ assertEquals(View.VISIBLE, statusContent.getVisibility());
+ assertEquals(statusContent.getText(), "Anniversary");
+ assertThat(statusContent.getMaxLines()).isEqualTo(2);
+ }
+
+ @Test
public void testCreateRemoteViewsWithPackageSuspended() {
PeopleSpaceTile tile = PERSON_TILE.toBuilder()
.setIsPackageSuspended(true)
@@ -434,7 +492,7 @@
tile, mOptions).getViews();
View result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
}
@Test
@@ -446,7 +504,7 @@
tile, mOptions).getViews();
View result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_work_profile_quiet_layout);
}
@Test
@@ -458,7 +516,7 @@
tileWithDndBlocking, mOptions).getViews();
View result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(BLOCK_CONVERSATIONS)
@@ -468,7 +526,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS)
@@ -477,7 +535,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS)
@@ -487,7 +545,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_STARRED_CONTACTS)
@@ -497,7 +555,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_STARRED_CONTACTS)
@@ -507,7 +565,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
@@ -517,7 +575,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
@@ -527,7 +585,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
@@ -536,7 +594,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
}
@Test
@@ -581,7 +639,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithMissedCallNotification, mOptions).getViews();
@@ -617,6 +675,7 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
assertEquals(View.GONE, result.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.scrim_layout).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -649,7 +708,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusAndNotification, mOptions).getViews();
@@ -659,6 +718,7 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
assertEquals(View.GONE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -725,7 +785,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusAndNotification, mOptions).getViews();
@@ -802,7 +862,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusAndNotification, mOptions).getViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 107ce28..d63c529 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -1041,7 +1041,6 @@
Bundle newOptions = new Bundle();
mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions);
-
// Check that options is not modified
verify(mAppWidgetManager, never()).updateAppWidgetOptions(
eq(SECOND_WIDGET_ID_WITH_SHORTCUT), any());
@@ -1478,6 +1477,8 @@
widgetSp5.edit().clear().commit();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
sp.edit().clear().commit();
+ mManager.mListeners.clear();
+ mManager.mTiles.clear();
}
private void setStorageForTile(String shortcutId, String packageName, int widgetId,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
index c050b62..75cf855 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
@@ -20,7 +20,9 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -30,6 +32,7 @@
import android.testing.ViewUtils;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
@@ -58,24 +61,27 @@
private DetailAdapter mMockDetailAdapter;
private TestableLooper mTestableLooper;
private UiEventLoggerFake mUiEventLogger;
+ private FrameLayout mParent;
@Before
public void setup() throws Exception {
mTestableLooper = TestableLooper.get(this);
mUiEventLogger = QSEvents.INSTANCE.setLoggerForTesting();
- mTestableLooper.runWithLooper(() -> {
- mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
- mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
- mQsDetail = (QSDetail) LayoutInflater.from(mContext).inflate(R.layout.qs_detail, null);
- mQsPanelController = mock(QSPanelController.class);
- mQuickHeader = mock(QuickStatusBarHeader.class);
- mQsDetail.setQsPanel(mQsPanelController, mQuickHeader, mock(QSFooter.class));
+ mParent = new FrameLayout(mContext);
+ mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
+ mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
+ LayoutInflater.from(mContext).inflate(R.layout.qs_detail, mParent);
+ mQsDetail = (QSDetail) mParent.getChildAt(0);
- mMockDetailAdapter = mock(DetailAdapter.class);
- when(mMockDetailAdapter.createDetailView(any(), any(), any()))
- .thenReturn(mock(View.class));
- });
+ mQsPanelController = mock(QSPanelController.class);
+ mQuickHeader = mock(QuickStatusBarHeader.class);
+ mQsDetail.setQsPanel(mQsPanelController, mQuickHeader, mock(QSFooter.class));
+ mQsDetail.mClipper = mock(QSDetailClipper.class);
+
+ mMockDetailAdapter = mock(DetailAdapter.class);
+ when(mMockDetailAdapter.createDetailView(any(), any(), any()))
+ .thenReturn(new View(mContext));
// Only detail in use is the user detail
when(mMockDetailAdapter.openDetailEvent())
@@ -84,16 +90,18 @@
.thenReturn(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE);
when(mMockDetailAdapter.moreSettingsEvent())
.thenReturn(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS);
+ ViewUtils.attachView(mParent);
}
@After
public void tearDown() {
QSEvents.INSTANCE.resetLogger();
+ mTestableLooper.processAllMessages();
+ ViewUtils.detachView(mParent);
}
@Test
public void testShowDetail_Metrics() {
- ViewUtils.attachView(mQsDetail);
mTestableLooper.processAllMessages();
mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
@@ -107,14 +115,66 @@
assertEquals(1, mUiEventLogger.numLogs());
assertEquals(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE.getId(), mUiEventLogger.eventId(0));
+ }
- ViewUtils.detachView(mQsDetail);
+ @Test
+ public void testShowDetail_ShouldAnimate() {
mTestableLooper.processAllMessages();
+
+ when(mMockDetailAdapter.shouldAnimate()).thenReturn(true);
+ mQsDetail.setFullyExpanded(true);
+
+ mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
+ verify(mQsDetail.mClipper).updateCircularClip(eq(true) /* animate */, anyInt(), anyInt(),
+ eq(true) /* in */, any());
+ clearInvocations(mQsDetail.mClipper);
+
+ mQsDetail.handleShowingDetail(null, 0, 0, false);
+ verify(mQsDetail.mClipper).updateCircularClip(eq(true) /* animate */, anyInt(), anyInt(),
+ eq(false) /* in */, any());
+ }
+
+ @Test
+ public void testShowDetail_ShouldNotAnimate() {
+ mTestableLooper.processAllMessages();
+
+ when(mMockDetailAdapter.shouldAnimate()).thenReturn(false);
+ mQsDetail.setFullyExpanded(true);
+
+ mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
+ verify(mQsDetail.mClipper).updateCircularClip(eq(false) /* animate */, anyInt(), anyInt(),
+ eq(true) /* in */, any());
+ clearInvocations(mQsDetail.mClipper);
+
+ mQsDetail.handleShowingDetail(null, 0, 0, false);
+ verify(mQsDetail.mClipper).updateCircularClip(eq(false) /* animate */, anyInt(), anyInt(),
+ eq(false) /* in */, any());
+ }
+
+ @Test
+ public void testDoneButton_CloseDetailPanel() {
+ mTestableLooper.processAllMessages();
+
+ when(mMockDetailAdapter.onDoneButtonClicked()).thenReturn(false);
+
+ mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
+ mQsDetail.requireViewById(android.R.id.button1).performClick();
+ verify(mQsPanelController).closeDetail();
+ }
+
+ @Test
+ public void testDoneButton_KeepDetailPanelOpen() {
+ mTestableLooper.processAllMessages();
+
+ when(mMockDetailAdapter.onDoneButtonClicked()).thenReturn(true);
+
+ mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
+ mQsDetail.requireViewById(android.R.id.button1).performClick();
+ verify(mQsPanelController, never()).closeDetail();
}
@Test
public void testMoreSettingsButton() {
- ViewUtils.attachView(mQsDetail);
mTestableLooper.processAllMessages();
mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
@@ -127,9 +187,6 @@
assertEquals(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS.getId(), mUiEventLogger.eventId(0));
verify(mActivityStarter).postStartActivityDismissingKeyguard(any(), anyInt());
-
- ViewUtils.detachView(mQsDetail);
- mTestableLooper.processAllMessages();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index d35597f..6f7bf3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -35,6 +36,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -82,6 +84,7 @@
private QuickQSPanelController mQuickQSPanelController;
private FakeTunerService mFakeTunerService;
private MetricsLogger mMetricsLogger = new FakeMetricsLogger();
+ private FalsingManagerFake mFalsingManager;
@Mock
private SettingsButton mSettingsButton;
@@ -95,12 +98,15 @@
private View mPowerMenuLiteView;
@Mock
private GlobalActionsDialogLite mGlobalActionsDialog;
+ @Mock
+ private UiEventLogger mUiEventLogger;
private QSFooterViewController mController;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ mFalsingManager = new FalsingManagerFake();
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
@@ -121,7 +127,8 @@
mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
- mMetricsLogger, new FalsingManagerFake(), false, mGlobalActionsDialog);
+ mMetricsLogger, mFalsingManager, false, mGlobalActionsDialog,
+ mUiEventLogger);
mController.init();
}
@@ -154,4 +161,27 @@
// Verify Settings wasn't launched.
verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
}
+
+ @Test
+ public void testLogPowerMenuClick() {
+ // Enable power menu button
+ mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
+ mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
+ mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
+ mMetricsLogger, new FalsingManagerFake(), true, mGlobalActionsDialog,
+ mUiEventLogger);
+ mController.init();
+ mController.setExpanded(true);
+ mFalsingManager.setFalseTap(false);
+
+ ArgumentCaptor<View.OnClickListener> onClickCaptor =
+ ArgumentCaptor.forClass(View.OnClickListener.class);
+ verify(mPowerMenuLiteView).setOnClickListener(onClickCaptor.capture());
+
+ onClickCaptor.getValue().onClick(mPowerMenuLiteView);
+
+ // Verify clicks are logged
+ verify(mUiEventLogger, times(1))
+ .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index e5e2e53..126dca5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -20,6 +20,7 @@
import android.graphics.drawable.Drawable
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
import android.text.TextUtils
import android.view.View
import androidx.test.filters.SmallTest
@@ -36,6 +37,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class QSTileViewImplTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 6cf3434..f17fe9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -39,6 +39,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -87,6 +88,8 @@
private lateinit var serviceInfo: ControlsServiceInfo
@Mock
private lateinit var uiEventLogger: UiEventLogger
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
@Captor
private lateinit var listingCallbackCaptor:
ArgumentCaptor<ControlsListingController.ControlsListingCallback>
@@ -109,7 +112,8 @@
`when`(qsHost.context).thenReturn(spiedContext)
`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
`when`(controlsComponent.isEnabled()).thenReturn(true)
- secureSettings.putInt(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 1)
+ `when`(keyguardStateController.isUnlocked()).thenReturn(true)
+ secureSettings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 1)
setupControlsComponent()
@@ -306,7 +310,8 @@
statusBarStateController,
activityStarter,
qsLogger,
- controlsComponent
+ controlsComponent,
+ keyguardStateController
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index e894b7b..7533cf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -92,6 +92,7 @@
public class QuickAccessWalletTileTest extends SysuiTestCase {
private static final String CARD_ID = "card_id";
+ private static final String LABEL = "QAW";
private static final Icon CARD_IMAGE =
Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888));
@@ -141,6 +142,7 @@
when(mHost.getContext()).thenReturn(mSpiedContext);
when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(true);
+ when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(LABEL);
when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true);
@@ -248,13 +250,19 @@
mTile.handleUpdateState(state, null);
- assertEquals(mContext.getString(R.string.wallet_title), state.label.toString());
+ assertEquals(LABEL, state.label.toString());
assertTrue(state.label.toString().contentEquals(state.contentDescription));
assertEquals(icon, state.icon);
}
@Test
- public void testGetTileLabel() {
+ public void testGetTileLabel_serviceLabelExists() {
+ assertEquals(LABEL, mTile.getTileLabel().toString());
+ }
+
+ @Test
+ public void testGetTileLabel_serviceLabelDoesNotExist() {
+ when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(null);
assertEquals(mContext.getString(R.string.wallet_title), mTile.getTileLabel().toString());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
index d1259d3..360eef9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
@@ -68,8 +68,8 @@
.addChange(TRANSIT_CLOSE, 0 /* flags */)
.addChange(TRANSIT_OPEN, FLAG_IS_WALLPAPER).build();
// Check non-wallpaper extraction
- RemoteAnimationTargetCompat[] wrapped =
- RemoteAnimationTargetCompat.wrap(combined, false /* wallpapers */);
+ RemoteAnimationTargetCompat[] wrapped = RemoteAnimationTargetCompat.wrap(combined,
+ false /* wallpapers */, mock(SurfaceControl.Transaction.class), null /* leashes */);
assertEquals(2, wrapped.length);
int changeLayer = -1;
int closeLayer = -1;
@@ -86,8 +86,8 @@
assertTrue(closeLayer < changeLayer);
// Check wallpaper extraction
- RemoteAnimationTargetCompat[] wallps =
- RemoteAnimationTargetCompat.wrap(combined, true /* wallpapers */);
+ RemoteAnimationTargetCompat[] wallps = RemoteAnimationTargetCompat.wrap(combined,
+ true /* wallpapers */, mock(SurfaceControl.Transaction.class), null /* leashes */);
assertEquals(1, wallps.length);
assertTrue(wallps[0].prefixOrderIndex < closeLayer);
assertEquals(MODE_OPENING, wallps[0].mode);
@@ -95,16 +95,15 @@
@Test
public void testLegacyTargetWrapper() {
+ TransitionInfo tinfo = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_CHANGE, FLAG_TRANSLUCENT).build();
+ final TransitionInfo.Change change = tinfo.getChanges().get(0);
final Rect endBounds = new Rect(40, 60, 140, 200);
- final TransitionInfo.Change change =
- new TransitionInfo.Change(null /* token */, null /* leash */);
change.setTaskInfo(createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_HOME));
- change.setMode(TRANSIT_CHANGE);
change.setEndAbsBounds(endBounds);
change.setEndRelOffset(0, 0);
- change.setFlags(FLAG_TRANSLUCENT);
- final RemoteAnimationTargetCompat wrapped =
- new RemoteAnimationTargetCompat(change, 0 /* order */);
+ final RemoteAnimationTargetCompat wrapped = new RemoteAnimationTargetCompat(change,
+ 0 /* order */, tinfo, mock(SurfaceControl.Transaction.class));
assertEquals(ACTIVITY_TYPE_HOME, wrapped.activityType);
assertEquals(new Rect(0, 0, 100, 140), wrapped.localBounds);
assertEquals(endBounds, wrapped.screenSpaceBounds);
@@ -122,7 +121,7 @@
TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
@TransitionInfo.ChangeFlags int flags) {
final TransitionInfo.Change change =
- new TransitionInfo.Change(null /* token */, null /* leash */);
+ new TransitionInfo.Change(null /* token */, createMockSurface(true));
change.setMode(mode);
change.setFlags(flags);
mInfo.addChange(change);
@@ -138,6 +137,7 @@
SurfaceControl sc = mock(SurfaceControl.class);
if (valid) {
doReturn(true).when(sc).isValid();
+ doReturn("TestSurface").when(sc).toString();
}
return sc;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 6f0ae22..b8db115 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -46,6 +46,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.IActivityManager;
import android.app.Instrumentation;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
@@ -69,6 +70,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
@@ -141,6 +143,10 @@
private KeyguardIndicationRotateTextViewController mRotateTextViewController;
@Mock
private FalsingManager mFalsingManager;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
+ private IActivityManager mIActivityManager;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
@Captor
@@ -208,7 +214,8 @@
mController = new KeyguardIndicationController(mContext, mWakeLockBuilder,
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
- mUserManager, mExecutor, mFalsingManager);
+ mUserManager, mExecutor, mFalsingManager, mLockPatternUtils, mIActivityManager);
+ mController.init();
mController.setIndicationArea(mIndicationArea);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
new file mode 100644
index 0000000..18b6c30
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -0,0 +1,225 @@
+package com.android.systemui.statusbar
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.util.DisplayMetrics
+import androidx.test.filters.SmallTest
+import com.android.systemui.ExpandHelper
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.media.MediaHierarchyManager
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.stack.AmbientState
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.policy.ConfigurationController
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
+
+private fun <T> anyObject(): T {
+ return Mockito.anyObject<T>()
+}
+
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner::class)
+class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
+
+ lateinit var transitionController: LockscreenShadeTransitionController
+ lateinit var row: ExpandableNotificationRow
+ @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
+ @Mock lateinit var lockscreenGestureLogger: LockscreenGestureLogger
+ @Mock lateinit var keyguardBypassController: KeyguardBypassController
+ @Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
+ @Mock lateinit var falsingCollector: FalsingCollector
+ @Mock lateinit var ambientState: AmbientState
+ @Mock lateinit var displayMetrics: DisplayMetrics
+ @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
+ @Mock lateinit var scrimController: ScrimController
+ @Mock lateinit var configurationController: ConfigurationController
+ @Mock lateinit var falsingManager: FalsingManager
+ @Mock lateinit var notificationPanelController: NotificationPanelViewController
+ @Mock lateinit var nsslController: NotificationStackScrollLayoutController
+ @Mock lateinit var featureFlags: FeatureFlags
+ @Mock lateinit var stackscroller: NotificationStackScrollLayout
+ @Mock lateinit var expandHelperCallback: ExpandHelper.Callback
+ @Mock lateinit var statusbar: StatusBar
+ @Mock lateinit var qS: QS
+ @JvmField @Rule val mockito = MockitoJUnit.rule()
+
+ @Before
+ fun setup() {
+ val helper = NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this))
+ row = helper.createRow()
+ transitionController = LockscreenShadeTransitionController(
+ statusBarStateController = statusbarStateController,
+ lockscreenGestureLogger = lockscreenGestureLogger,
+ keyguardBypassController = keyguardBypassController,
+ lockScreenUserManager = lockScreenUserManager,
+ falsingCollector = falsingCollector,
+ ambientState = ambientState,
+ displayMetrics = displayMetrics,
+ mediaHierarchyManager = mediaHierarchyManager,
+ scrimController = scrimController,
+ featureFlags = featureFlags,
+ context = context,
+ configurationController = configurationController,
+ falsingManager = falsingManager
+ )
+ whenever(nsslController.view).thenReturn(stackscroller)
+ whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
+ transitionController.notificationPanelController = notificationPanelController
+ transitionController.statusbar = statusbar
+ transitionController.qS = qS
+ transitionController.setStackScroller(nsslController)
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(nsslController.isInLockedDownShade).thenReturn(false)
+ whenever(qS.isFullyCollapsed).thenReturn(true)
+ whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
+ true)
+ whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
+ whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
+ whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false)
+ whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
+ clearInvocations(statusbar)
+ }
+
+ @After
+ fun tearDown() {
+ transitionController.dragDownAnimator?.cancel()
+ }
+
+ @Test
+ fun testCantDragDownWhenQSExpanded() {
+ assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
+ whenever(qS.isFullyCollapsed).thenReturn(false)
+ assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
+ }
+
+ @Test
+ fun testCanDragDownInLockedDownShade() {
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ assertFalse("Can drag down in shade locked", transitionController.canDragDown())
+ whenever(nsslController.isInLockedDownShade).thenReturn(true)
+ assertTrue("Can't drag down in locked down shade", transitionController.canDragDown())
+ }
+
+ @Test
+ fun testGoingToLockedShade() {
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ }
+
+ @Test
+ fun testGoToLockedShadeOnlyOnKeyguard() {
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ transitionController.goToLockedShade(null)
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE)
+ transitionController.goToLockedShade(null)
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.FULLSCREEN_USER_SWITCHER)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ }
+
+ @Test
+ fun testDontGoWhenShadeDisabled() {
+ whenever(statusbar.isShadeDisabled).thenReturn(true)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ }
+
+ @Test
+ fun testUserExpandsViewOnGoingToFullShade() {
+ assertFalse("Row shouldn't be user expanded yet", row.isUserExpanded)
+ transitionController.goToLockedShade(row)
+ assertTrue("Row wasn't user expanded on drag down", row.isUserExpanded)
+ }
+
+ @Test
+ fun testTriggeringBouncerWhenPrivateNotificationsArentAllowed() {
+ whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
+ false)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
+ verify(statusbar).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ }
+
+ @Test
+ fun testTriggeringBouncerNoNotificationsOnLockscreen() {
+ whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
+ verify(statusbar).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ }
+
+ @Test
+ fun testGoToLockedShadeCreatesQSAnimation() {
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ verify(notificationPanelController).animateToFullShade(anyLong())
+ assertNotNull(transitionController.dragDownAnimator)
+ }
+
+ @Test
+ fun testGoToLockedShadeDoesntCreateQSAnimation() {
+ transitionController.goToLockedShade(null, needsQSAnimation = false)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ verify(notificationPanelController).animateToFullShade(anyLong())
+ assertNull(transitionController.dragDownAnimator)
+ }
+
+ @Test
+ fun testDragDownAmountDoesntCallOutInLockedDownShade() {
+ whenever(nsslController.isInLockedDownShade).thenReturn(true)
+ transitionController.dragDownAmount = 10f
+ verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
+ verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
+ verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat())
+ verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(),
+ anyBoolean(), anyLong())
+ verify(qS, never()).setTransitionToFullShadeAmount(anyFloat(), anyBoolean())
+ }
+
+ @Test
+ fun testDragDownAmountCallsOut() {
+ transitionController.dragDownAmount = 10f
+ verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
+ verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
+ verify(scrimController).setTransitionToFullShadeProgress(anyFloat())
+ verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(),
+ anyBoolean(), anyLong())
+ verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyBoolean())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 2a50273..0c65830 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,9 +16,11 @@
package com.android.systemui.statusbar;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.content.Intent.ACTION_USER_SWITCHED;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_ALERTING;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_MEDIA_CONTROLS;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_PEOPLE;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
@@ -55,6 +57,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.KeyguardNotificationSuppressor;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -383,13 +386,44 @@
assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
}
+ @Test
+ public void testKeyguardNotificationSuppressors() {
+ // GIVEN a notification that should be shown on the lockscreen
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .build();
+ entry.setBucket(BUCKET_ALERTING);
+
+ // WHEN a suppressor is added that filters out all entries
+ FakeKeyguardSuppressor suppressor = new FakeKeyguardSuppressor();
+ mLockscreenUserManager.addKeyguardNotificationSuppressor(suppressor);
+
+ // THEN it's filtered out
+ assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
+
+ // WHEN the suppressor no longer filters out entries
+ suppressor.setShouldSuppress(false);
+
+ // THEN it's no longer filtered out
+ assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
+ }
+
private class TestNotificationLockscreenUserManager
extends NotificationLockscreenUserManagerImpl {
public TestNotificationLockscreenUserManager(Context context) {
- super(context, mBroadcastDispatcher, mDevicePolicyManager, mUserManager,
- mClickNotifier, NotificationLockscreenUserManagerTest.this.mKeyguardManager,
- mStatusBarStateController, Handler.createAsync(Looper.myLooper()),
- mDeviceProvisionedController, mKeyguardStateController);
+ super(
+ context,
+ mBroadcastDispatcher,
+ mDevicePolicyManager,
+ mUserManager,
+ mClickNotifier,
+ NotificationLockscreenUserManagerTest.this.mKeyguardManager,
+ mStatusBarStateController,
+ Handler.createAsync(Looper.myLooper()),
+ mDeviceProvisionedController,
+ mKeyguardStateController);
}
public BroadcastReceiver getBaseBroadcastReceiverForTest() {
@@ -404,4 +438,17 @@
return mSettingsObserver;
}
}
+
+ private static class FakeKeyguardSuppressor implements KeyguardNotificationSuppressor {
+ private boolean mShouldSuppress = true;
+
+ @Override
+ public boolean shouldSuppressOnKeyguard(NotificationEntry entry) {
+ return mShouldSuppress;
+ }
+
+ public void setShouldSuppress(boolean shouldSuppress) {
+ mShouldSuppress = shouldSuppress;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
new file mode 100644
index 0000000..9b5c33d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.lockscreen
+
+import android.app.smartspace.SmartspaceManager
+import android.app.smartspace.SmartspaceSession
+import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener
+import android.app.smartspace.SmartspaceTarget
+import android.content.ComponentName
+import android.content.ContentResolver
+import android.content.pm.UserInfo
+import android.database.ContentObserver
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import com.android.systemui.util.concurrency.FakeExecution
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.Optional
+
+@SmallTest
+class LockscreenSmartspaceControllerTest : SysuiTestCase() {
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var smartspaceManager: SmartspaceManager
+ @Mock
+ private lateinit var smartspaceSession: SmartspaceSession
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var falsingManager: FalsingManager
+ @Mock
+ private lateinit var secureSettings: SecureSettings
+ @Mock
+ private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var contentResolver: ContentResolver
+ @Mock
+ private lateinit var configurationController: ConfigurationController
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var handler: Handler
+
+ @Mock
+ private lateinit var plugin: BcSmartspaceDataPlugin
+ @Mock
+ private lateinit var controllerListener: SmartspaceTargetListener
+
+ @Captor
+ private lateinit var sessionListenerCaptor: ArgumentCaptor<OnTargetsAvailableListener>
+ @Captor
+ private lateinit var userTrackerCaptor: ArgumentCaptor<UserTracker.Callback>
+ @Captor
+ private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
+ @Captor
+ private lateinit var configChangeListenerCaptor: ArgumentCaptor<ConfigurationListener>
+ @Captor
+ private lateinit var statusBarStateListenerCaptor: ArgumentCaptor<StateListener>
+
+ private lateinit var sessionListener: OnTargetsAvailableListener
+ private lateinit var userListener: UserTracker.Callback
+ private lateinit var settingsObserver: ContentObserver
+ private lateinit var configChangeListener: ConfigurationListener
+ private lateinit var statusBarStateListener: StateListener
+
+ private val clock = FakeSystemClock()
+ private val executor = FakeExecutor(clock)
+ private val execution = FakeExecution()
+ private val fakeParent = FrameLayout(context)
+ private val fakePrivateLockscreenSettingUri = Uri.Builder().appendPath("test").build()
+
+ private val userHandlePrimary: UserHandle = UserHandle(0)
+ private val userHandleManaged: UserHandle = UserHandle(2)
+ private val userHandleSecondary: UserHandle = UserHandle(3)
+
+ private val userList = listOf(
+ mockUserInfo(userHandlePrimary, isManagedProfile = false),
+ mockUserInfo(userHandleManaged, isManagedProfile = true),
+ mockUserInfo(userHandleSecondary, isManagedProfile = false)
+ )
+
+ private lateinit var controller: LockscreenSmartspaceController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(featureFlags.isSmartspaceEnabled).thenReturn(true)
+
+ `when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
+ .thenReturn(fakePrivateLockscreenSettingUri)
+ `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession)
+ `when`(plugin.getView(any())).thenReturn(fakeSmartspaceView)
+ `when`(userTracker.userProfiles).thenReturn(userList)
+ `when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
+
+ setActiveUser(userHandlePrimary)
+ setAllowPrivateNotifications(userHandlePrimary, true)
+ setAllowPrivateNotifications(userHandleManaged, true)
+ setAllowPrivateNotifications(userHandleSecondary, true)
+
+ controller = LockscreenSmartspaceController(
+ context,
+ featureFlags,
+ smartspaceManager,
+ activityStarter,
+ falsingManager,
+ secureSettings,
+ userTracker,
+ contentResolver,
+ configurationController,
+ statusBarStateController,
+ execution,
+ executor,
+ handler,
+ Optional.of(plugin)
+ )
+ }
+
+ @Test(expected = RuntimeException::class)
+ fun testThrowsIfFlagIsDisabled() {
+ // GIVEN the feature flag is disabled
+ `when`(featureFlags.isSmartspaceEnabled).thenReturn(false)
+
+ // WHEN we try to build the view
+ controller.buildAndConnectView(fakeParent)
+
+ // THEN an exception is thrown
+ }
+
+ @Test
+ fun testListenersAreRegistered() {
+ // GIVEN a listener is added after a session is created
+ connectSession()
+
+ // WHEN a listener is registered
+ controller.addListener(controllerListener)
+
+ // THEN the listener is registered to the underlying plugin
+ verify(plugin).registerListener(controllerListener)
+ }
+
+ @Test
+ fun testEarlyRegisteredListenersAreAttachedAfterConnected() {
+ // GIVEN a listener that is registered before the session is created
+ controller.addListener(controllerListener)
+
+ // WHEN the session is created
+ connectSession()
+
+ // THEN the listener is subsequently registered
+ verify(plugin).registerListener(controllerListener)
+ }
+
+ @Test
+ fun testEmptyListIsEmittedAfterDisconnect() {
+ // GIVEN a registered listener on an active session
+ connectSession()
+ clearInvocations(plugin)
+
+ // WHEN the session is closed
+ controller.disconnect()
+
+ // THEN the listener receives an empty list of targets
+ verify(plugin).onTargetsAvailable(emptyList())
+ }
+
+ @Test
+ fun testUserChangeReloadsSmartspace() {
+ // GIVEN a connected smartspace session
+ connectSession()
+
+ // WHEN the active user changes
+ userListener.onUserChanged(-1, context)
+
+ // THEN we request a new smartspace update
+ verify(smartspaceSession).requestSmartspaceUpdate()
+ }
+
+ @Test
+ fun testSettingsChangeReloadsSmartspace() {
+ // GIVEN a connected smartspace session
+ connectSession()
+
+ // WHEN the lockscreen privacy setting changes
+ settingsObserver.onChange(true, null)
+
+ // THEN we request a new smartspace update
+ verify(smartspaceSession).requestSmartspaceUpdate()
+ }
+
+ @Test
+ fun testThemeChangeUpdatesTextColor() {
+ // GIVEN a connected smartspace session
+ connectSession()
+
+ // WHEN the theme changes
+ configChangeListener.onThemeChanged()
+
+ // We update the new text color to match the wallpaper color
+ verify(fakeSmartspaceView).setPrimaryTextColor(anyInt())
+ }
+
+ @Test
+ fun testDozeAmountChangeUpdatesView() {
+ // GIVEN a connected smartspace session
+ connectSession()
+
+ // WHEN the doze amount changes
+ statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
+
+ // We pass that along to the view
+ verify(fakeSmartspaceView).setDozeAmount(0.7f)
+ }
+
+ @Test
+ fun testSensitiveTargetsAreNotFilteredIfAllowed() {
+ // GIVEN the active and managed users allow sensitive content
+ connectSession()
+
+ // WHEN we receive a list of targets
+ val targets = listOf(
+ makeTarget(1, userHandlePrimary, isSensitive = true),
+ makeTarget(2, userHandleManaged, isSensitive = true),
+ makeTarget(3, userHandlePrimary, isSensitive = true)
+ )
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN all sensitive content is still shown
+ verify(plugin).onTargetsAvailable(eq(targets))
+ }
+
+ @Test
+ fun testNonSensitiveTargetsAreNeverFiltered() {
+ // GIVEN the active user doesn't allow sensitive lockscreen content
+ setAllowPrivateNotifications(userHandlePrimary, false)
+ connectSession()
+
+ // WHEN we receive a list of targets
+ val targets = listOf(
+ makeTarget(1, userHandlePrimary),
+ makeTarget(2, userHandlePrimary),
+ makeTarget(3, userHandlePrimary)
+ )
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN all non-sensitive content is still shown
+ verify(plugin).onTargetsAvailable(eq(targets))
+ }
+
+ @Test
+ fun testSensitiveTargetsAreFilteredOutForAppropriateUsers() {
+ // GIVEN the active and managed users don't allow sensitive lockscreen content
+ setAllowPrivateNotifications(userHandlePrimary, false)
+ setAllowPrivateNotifications(userHandleManaged, false)
+ connectSession()
+
+ // WHEN we receive a list of targets
+ val targets = listOf(
+ makeTarget(0, userHandlePrimary),
+ makeTarget(1, userHandlePrimary, isSensitive = true),
+ makeTarget(2, userHandleManaged, isSensitive = true),
+ makeTarget(3, userHandleManaged),
+ makeTarget(4, userHandlePrimary, isSensitive = true),
+ makeTarget(5, userHandlePrimary),
+ makeTarget(6, userHandleSecondary, isSensitive = true)
+ )
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN only non-sensitive content from those accounts is shown
+ verify(plugin).onTargetsAvailable(eq(listOf(
+ targets[0],
+ targets[3],
+ targets[5]
+ )))
+ }
+
+ @Test
+ fun testSettingsAreReloaded() {
+ // GIVEN a connected session where the privacy settings later flip to false
+ connectSession()
+ setAllowPrivateNotifications(userHandlePrimary, false)
+ setAllowPrivateNotifications(userHandleManaged, false)
+ settingsObserver.onChange(true, fakePrivateLockscreenSettingUri)
+
+ // WHEN we receive a new list of targets
+ val targets = listOf(
+ makeTarget(1, userHandlePrimary, isSensitive = true),
+ makeTarget(2, userHandleManaged, isSensitive = true),
+ makeTarget(4, userHandlePrimary, isSensitive = true)
+ )
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN we filter based on the new settings values
+ verify(plugin).onTargetsAvailable(emptyList())
+ }
+
+ @Test
+ fun testRecognizeSwitchToSecondaryUser() {
+ // GIVEN an inactive secondary user that doesn't allow sensitive content
+ setAllowPrivateNotifications(userHandleSecondary, false)
+ connectSession()
+
+ // WHEN the secondary user becomes the active user
+ setActiveUser(userHandleSecondary)
+ userListener.onUserChanged(userHandleSecondary.identifier, context)
+
+ // WHEN we receive a new list of targets
+ val targets = listOf(
+ makeTarget(0, userHandlePrimary),
+ makeTarget(1, userHandleSecondary),
+ makeTarget(2, userHandleSecondary, isSensitive = true),
+ makeTarget(3, userHandleManaged),
+ makeTarget(4, userHandleSecondary),
+ makeTarget(5, userHandleManaged),
+ makeTarget(6, userHandlePrimary)
+ )
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN only non-sensitive content from the secondary user is shown
+ verify(plugin).onTargetsAvailable(listOf(
+ targets[1],
+ targets[4]
+ ))
+ }
+
+ @Test
+ fun testUnregisterListenersOnCleanup() {
+ // GIVEN a connected session
+ connectSession()
+
+ // WHEN we are told to cleanup
+ controller.disconnect()
+
+ // THEN we disconnect from the session and unregister any listeners
+ verify(smartspaceSession).removeOnTargetsAvailableListener(sessionListener)
+ verify(smartspaceSession).close()
+ verify(userTracker).removeCallback(userListener)
+ verify(contentResolver).unregisterContentObserver(settingsObserver)
+ verify(configurationController).removeCallback(configChangeListener)
+ verify(statusBarStateController).removeCallback(statusBarStateListener)
+ }
+
+ @Test
+ fun testBuildViewIsIdempotent() {
+ // GIVEN a connected session
+ connectSession()
+ clearInvocations(plugin)
+
+ // WHEN we disconnect and then reconnect
+ controller.disconnect()
+ controller.buildAndConnectView(fakeParent)
+
+ // THEN the view is not rebuilt
+ verify(plugin, never()).getView(any())
+ assertEquals(fakeSmartspaceView, controller.view)
+ }
+
+ @Test
+ fun testDoubleConnectIsIgnored() {
+ // GIVEN a connected session
+ connectSession()
+ clearInvocations(smartspaceManager)
+ clearInvocations(plugin)
+
+ // WHEN we're asked to connect a second time and add to a parent
+ val view = controller.buildAndConnectView(fakeParent)
+ fakeParent.addView(view)
+
+ // THEN the existing view and session are reused
+ verify(smartspaceManager, never()).createSmartspaceSession(any())
+ verify(plugin, never()).getView(any())
+ assertEquals(fakeSmartspaceView, controller.view)
+ }
+
+ private fun connectSession() {
+ controller.buildAndConnectView(fakeParent)
+
+ verify(smartspaceSession)
+ .addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor))
+ sessionListener = sessionListenerCaptor.value
+
+ verify(userTracker).addCallback(capture(userTrackerCaptor), any())
+ userListener = userTrackerCaptor.value
+
+ verify(contentResolver).registerContentObserver(
+ eq(fakePrivateLockscreenSettingUri),
+ eq(true),
+ capture(settingsObserverCaptor),
+ eq(UserHandle.USER_ALL))
+ settingsObserver = settingsObserverCaptor.value
+
+ verify(configurationController).addCallback(configChangeListenerCaptor.capture())
+ configChangeListener = configChangeListenerCaptor.value
+
+ verify(statusBarStateController).addCallback(statusBarStateListenerCaptor.capture())
+ statusBarStateListener = statusBarStateListenerCaptor.value
+
+ verify(smartspaceSession).requestSmartspaceUpdate()
+ clearInvocations(smartspaceSession)
+
+ verify(fakeSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(fakeSmartspaceView).setDozeAmount(0.5f)
+ clearInvocations(fakeSmartspaceView)
+
+ fakeParent.addView(fakeSmartspaceView)
+ }
+
+ private fun setActiveUser(userHandle: UserHandle) {
+ `when`(userTracker.userId).thenReturn(userHandle.identifier)
+ `when`(userTracker.userHandle).thenReturn(userHandle)
+ }
+
+ private fun mockUserInfo(userHandle: UserHandle, isManagedProfile: Boolean): UserInfo {
+ val userInfo = mock(UserInfo::class.java)
+ `when`(userInfo.userHandle).thenReturn(userHandle)
+ `when`(userInfo.isManagedProfile).thenReturn(isManagedProfile)
+ return userInfo
+ }
+
+ fun makeTarget(
+ id: Int,
+ userHandle: UserHandle,
+ isSensitive: Boolean = false
+ ): SmartspaceTarget {
+ return SmartspaceTarget.Builder(
+ "target$id",
+ ComponentName("testpackage", "testclass$id"),
+ userHandle)
+ .setSensitive(isSensitive)
+ .build()
+ }
+
+ private fun setAllowPrivateNotifications(user: UserHandle, value: Boolean) {
+ `when`(secureSettings.getIntForUser(
+ eq(PRIVATE_LOCKSCREEN_SETTING),
+ anyInt(),
+ eq(user.identifier))
+ ).thenReturn(if (value) 1 else 0)
+ }
+
+ private val fakeSmartspaceView = spy(object : View(context), SmartspaceView {
+ override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
+ }
+
+ override fun setPrimaryTextColor(color: Int) {
+ }
+
+ override fun setDozeAmount(amount: Float) {
+ }
+
+ override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
+ }
+
+ override fun setFalsingManager(falsingManager: FalsingManager?) {
+ }
+
+ override fun setDnd(image: Drawable?, description: String?) {
+ }
+
+ override fun setNextAlarm(image: Drawable?, description: String?) {
+ }
+ })
+}
+
+private const val PRIVATE_LOCKSCREEN_SETTING =
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
index 68cf66d..7d06abf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
@@ -60,13 +62,15 @@
mDynamicPrivacyController.setStatusBarKeyguardViewManager(
mock(StatusBarKeyguardViewManager.class));
mDynamicPrivacyController.addListener(mListener);
+ // Disable dynamic privacy by default
+ allowPrivateNotificationsInPublic(true);
}
@Test
public void testDynamicFalseWhenCannotSkipBouncer() {
enableDynamicPrivacy();
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
- Assert.assertFalse("can't skip bouncer but is dynamically unlocked",
+ assertFalse("can't skip bouncer but is dynamically unlocked",
mDynamicPrivacyController.isDynamicallyUnlocked());
}
@@ -102,9 +106,26 @@
verify(mListener).onDynamicPrivacyChanged();
}
- private void enableDynamicPrivacy() {
+ @Test
+ public void dynamicPrivacyOnlyWhenHidingPrivate() {
+ // Verify that when only hiding notifications, this isn't enabled
+ allowPrivateNotificationsInPublic(true);
when(mLockScreenUserManager.shouldHideNotifications(any())).thenReturn(
false);
+ assertFalse("Dynamic privacy shouldn't be enabled when only hiding notifications",
+ mDynamicPrivacyController.isDynamicPrivacyEnabled());
+ allowPrivateNotificationsInPublic(false);
+ assertTrue("Should be enabled when hiding notification contents",
+ mDynamicPrivacyController.isDynamicPrivacyEnabled());
+ }
+
+ private void enableDynamicPrivacy() {
+ allowPrivateNotificationsInPublic(false);
+ }
+
+ private void allowPrivateNotificationsInPublic(boolean allow) {
+ when(mLockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
+ allow);
}
@Test
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 6459c0c..1be14b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -190,16 +190,6 @@
mEntryManager = new NotificationEntryManager(
mLogger,
mGroupManager,
- () -> new NotificationRankingManager(
- () -> mNotificationMediaManager,
- mGroupManager,
- mHeadsUpManager,
- mock(NotificationFilter.class),
- mLogger,
- mock(NotificationSectionsFeatureManager.class),
- mock(PeopleNotificationIdentifier.class),
- mock(HighPriorityProvider.class)),
- mEnvironment,
mFeatureFlags,
() -> mNotificationRowBinder,
() -> mRemoteInputManager,
@@ -207,6 +197,17 @@
mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.class)
);
+ mEntryManager.setRanker(
+ new NotificationRankingManager(
+ () -> mNotificationMediaManager,
+ mGroupManager,
+ mHeadsUpManager,
+ mock(NotificationFilter.class),
+ mLogger,
+ mock(NotificationSectionsFeatureManager.class),
+ mock(PeopleNotificationIdentifier.class),
+ mock(HighPriorityProvider.class),
+ mEnvironment));
mEntryManager.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener);
mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index b493b9a..b1eef4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -77,13 +77,16 @@
mock(StatusBarNotification.class);
@Mock
- ForegroundServiceController mFsc;
+ StatusBarStateController mStatusBarStateController;
@Mock
KeyguardEnvironment mEnvironment;
@Mock
- MediaFeatureFlag mMediaFeatureFlag;
+ ForegroundServiceController mFsc;
@Mock
- StatusBarStateController mStatusBarStateController;
+ NotificationLockscreenUserManager mUserManager;
+ @Mock
+ MediaFeatureFlag mMediaFeatureFlag;
+
private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
private NotificationFilter mNotificationFilter;
@@ -127,7 +130,12 @@
mDependency,
TestableLooper.get(this));
mRow = testHelper.createRow();
- mNotificationFilter = new NotificationFilter(mStatusBarStateController, mMediaFeatureFlag);
+ mNotificationFilter = new NotificationFilter(
+ mStatusBarStateController,
+ mEnvironment,
+ mFsc,
+ mUserManager,
+ mMediaFeatureFlag);
}
@After
@@ -195,7 +203,11 @@
public void shouldFilterOtherNotificationWhenDisabled() {
// GIVEN that the media feature is disabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
- NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
+ NotificationFilter filter = new NotificationFilter(
+ mStatusBarStateController,
+ mEnvironment,
+ mFsc,
+ mUserManager,
mMediaFeatureFlag);
// WHEN the media filter is asked about an entry
NotificationEntry otherEntry = new NotificationEntryBuilder().build();
@@ -208,7 +220,11 @@
public void shouldFilterOtherNotificationWhenEnabled() {
// GIVEN that the media feature is enabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
- NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
+ NotificationFilter filter = new NotificationFilter(
+ mStatusBarStateController,
+ mEnvironment,
+ mFsc,
+ mUserManager,
mMediaFeatureFlag);
// WHEN the media filter is asked about an entry
NotificationEntry otherEntry = new NotificationEntryBuilder().build();
@@ -221,7 +237,11 @@
public void shouldFilterMediaNotificationWhenDisabled() {
// GIVEN that the media feature is disabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
- NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
+ NotificationFilter filter = new NotificationFilter(
+ mStatusBarStateController,
+ mEnvironment,
+ mFsc,
+ mUserManager,
mMediaFeatureFlag);
// WHEN the media filter is asked about a media entry
final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
@@ -233,7 +253,11 @@
public void shouldFilterMediaNotificationWhenEnabled() {
// GIVEN that the media feature is enabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
- NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
+ NotificationFilter filter = new NotificationFilter(
+ mStatusBarStateController,
+ mEnvironment,
+ mFsc,
+ mUserManager,
mMediaFeatureFlag);
// WHEN the media filter is asked about a media entry
final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index bdd4a01..c51c628 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -30,9 +30,11 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
@@ -42,7 +44,6 @@
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.google.common.truth.Truth.assertThat
import dagger.Lazy
@@ -79,9 +80,11 @@
mock(NotificationEntryManagerLogger::class.java),
sectionsManager,
personNotificationIdentifier,
- HighPriorityProvider(personNotificationIdentifier,
- mock(NotificationGroupManagerLegacy::class.java))
- )
+ HighPriorityProvider(
+ personNotificationIdentifier,
+ mock(NotificationGroupManagerLegacy::class.java)),
+ mock(KeyguardEnvironment::class.java)
+ )
}
@Test
@@ -486,7 +489,8 @@
logger: NotificationEntryManagerLogger,
sectionsFeatureManager: NotificationSectionsFeatureManager,
peopleNotificationIdentifier: PeopleNotificationIdentifier,
- highPriorityProvider: HighPriorityProvider
+ highPriorityProvider: HighPriorityProvider,
+ keyguardEnvironment: KeyguardEnvironment
) : NotificationRankingManager(
mediaManager,
groupManager,
@@ -495,7 +499,8 @@
logger,
sectionsFeatureManager,
peopleNotificationIdentifier,
- highPriorityProvider
+ highPriorityProvider,
+ keyguardEnvironment
) {
fun applyTestRankingMap(r: RankingMap) {
rankingMap = r
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
new file mode 100644
index 0000000..a8db8d7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.app.smartspace.SmartspaceTarget
+import android.content.ComponentName
+import android.os.UserHandle
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.concurrent.TimeUnit
+
+@SmallTest
+class SmartspaceDedupingCoordinatorTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var statusBarStateController: SysuiStatusBarStateController
+ @Mock
+ private lateinit var smartspaceController: LockscreenSmartspaceController
+ @Mock
+ private lateinit var notificationEntryManager: NotificationEntryManager
+ @Mock
+ private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
+ @Mock
+ private lateinit var notifPipeline: NotifPipeline
+ @Mock
+ private lateinit var pluggableListener: Pluggable.PluggableListener<NotifFilter>
+
+ @Captor
+ private lateinit var filterCaptor: ArgumentCaptor<NotifFilter>
+ @Captor
+ private lateinit var collectionListenerCaptor: ArgumentCaptor<NotifCollectionListener>
+ @Captor
+ private lateinit var stateListenerCaptor: ArgumentCaptor<StatusBarStateController.StateListener>
+ @Captor
+ private lateinit var smartspaceListenerCaptor: ArgumentCaptor<SmartspaceTargetListener>
+
+ private lateinit var filter: NotifFilter
+ private lateinit var collectionListener: NotifCollectionListener
+ private lateinit var statusBarListener: StatusBarStateController.StateListener
+ private lateinit var newTargetListener: SmartspaceTargetListener
+
+ private lateinit var entry1HasRecentlyAlerted: NotificationEntry
+ private lateinit var entry2HasNotRecentlyAlerted: NotificationEntry
+ private lateinit var entry3NotAssociatedWithTarget: NotificationEntry
+ private lateinit var entry4HasNotRecentlyAlerted: NotificationEntry
+ private lateinit var target1: SmartspaceTarget
+ private lateinit var target2: SmartspaceTarget
+ private lateinit var target4: SmartspaceTarget
+
+ private val clock = FakeSystemClock()
+ private val executor = FakeExecutor(clock)
+ private val now = clock.currentTimeMillis()
+
+ private lateinit var deduper: SmartspaceDedupingCoordinator
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ // Mock out some behavior
+ `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+ // Build the deduper
+ deduper = SmartspaceDedupingCoordinator(
+ statusBarStateController,
+ smartspaceController,
+ notificationEntryManager,
+ notificationLockscreenUserManager,
+ notifPipeline,
+ executor,
+ clock
+ )
+
+ // Attach the deduper and capture the listeners/filters that it registers
+ deduper.attach(notifPipeline)
+
+ verify(notifPipeline).addPreGroupFilter(filterCaptor.capture())
+ filter = filterCaptor.value
+ filter.setInvalidationListener(pluggableListener)
+
+ verify(notifPipeline).addCollectionListener(capture(collectionListenerCaptor))
+ collectionListener = collectionListenerCaptor.value
+
+ verify(statusBarStateController).addCallback(capture(stateListenerCaptor))
+ statusBarListener = stateListenerCaptor.value
+
+ verify(smartspaceController).addListener(capture(smartspaceListenerCaptor))
+ newTargetListener = smartspaceListenerCaptor.value
+
+ // Initialize some test data
+ entry1HasRecentlyAlerted = NotificationEntryBuilder()
+ .setPkg(PACKAGE_1)
+ .setId(11)
+ .setLastAudiblyAlertedMs(now - 10000)
+ .build()
+ entry2HasNotRecentlyAlerted = NotificationEntryBuilder()
+ .setPkg(PACKAGE_2)
+ .setId(22)
+ .build()
+ entry3NotAssociatedWithTarget = NotificationEntryBuilder()
+ .setPkg("com.test.package.3")
+ .setId(33)
+ .setLastAudiblyAlertedMs(now - 10000)
+ .build()
+ entry4HasNotRecentlyAlerted = NotificationEntryBuilder()
+ .setPkg(PACKAGE_2)
+ .setId(44)
+ .build()
+
+ target1 = buildTargetFor(entry1HasRecentlyAlerted)
+ target2 = buildTargetFor(entry2HasNotRecentlyAlerted)
+ target4 = buildTargetFor(entry4HasNotRecentlyAlerted)
+ }
+
+ @Test
+ fun testBasicFiltering() {
+ // GIVEN a few notifications
+ addEntries(
+ entry2HasNotRecentlyAlerted,
+ entry3NotAssociatedWithTarget,
+ entry4HasNotRecentlyAlerted)
+
+ // WHEN we receive smartspace targets associated with entry 2 and 3
+ sendTargets(target2, target4)
+
+ // THEN both pipelines are rerun
+ verifyPipelinesInvalidated()
+
+ // THEN the first target is filtered out, but the other ones aren't
+ assertTrue(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
+ assertFalse(filter.shouldFilterOut(entry3NotAssociatedWithTarget, now))
+ assertFalse(filter.shouldFilterOut(entry4HasNotRecentlyAlerted, now))
+ }
+
+ @Test
+ fun testDoNotFilterRecentlyAlertedNotifs() {
+ // GIVEN one notif that recently alerted and a second that hasn't
+ addEntries(entry1HasRecentlyAlerted, entry2HasNotRecentlyAlerted)
+
+ // WHEN they become associated with smartspace targets
+ sendTargets(target1, target2)
+
+ // THEN neither is filtered (the first because it's recently alerted and the second
+ // because it's not in the first position
+ verifyPipelinesNotInvalidated()
+ assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, now))
+ assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
+ }
+
+ @Test
+ fun testFilterAlertedButNotRecentNotifs() {
+ // GIVEN a notification that alerted, but a very long time ago
+ val entryOldAlert = NotificationEntryBuilder(entry1HasRecentlyAlerted)
+ .setLastAudiblyAlertedMs(now - 40000)
+ .build()
+ addEntries(entryOldAlert)
+
+ // WHEN it becomes part of smartspace
+ val target = buildTargetFor(entryOldAlert)
+ sendTargets(target)
+
+ // THEN it's still filtered out (because it's not in the alert window)
+ verifyPipelinesInvalidated()
+ assertTrue(filter.shouldFilterOut(entryOldAlert, now))
+ }
+
+ @Test
+ fun testExceptionExpires() {
+ // GIVEN a recently-alerted notif that is the primary smartspace target
+ addEntries(entry1HasRecentlyAlerted)
+ sendTargets(target1)
+ clearPipelineInvocations()
+
+ // WHEN we go beyond the target's exception window
+ clock.advanceTime(20000)
+
+ // THEN the pipeline is invalidated
+ verifyPipelinesInvalidated()
+ assertExecutorIsClear()
+ }
+
+ @Test
+ fun testExceptionIsEventuallyFiltered() {
+ // GIVEN a notif that has recently alerted
+ addEntries(entry1HasRecentlyAlerted)
+
+ // WHEN it becomes the primary smartspace target
+ sendTargets(target1)
+
+ // THEN it isn't filtered out (because it recently alerted)
+ assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, now))
+
+ // WHEN we pass the alert window
+ clock.advanceTime(20000)
+
+ // THEN the notif is once again filtered
+ assertTrue(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
+ }
+
+ @Test
+ fun testExceptionIsUpdated() {
+ // GIVEN a notif that has recently alerted and is the primary smartspace target
+ addEntries(entry1HasRecentlyAlerted)
+ sendTargets(target1)
+ clearPipelineInvocations()
+ assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
+
+ // GIVEN the notif is updated with a much more recent alert time
+ NotificationEntryBuilder(entry1HasRecentlyAlerted)
+ .setLastAudiblyAlertedMs(clock.currentTimeMillis() - 500)
+ .apply(entry1HasRecentlyAlerted)
+ updateEntries(entry1HasRecentlyAlerted)
+ assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
+
+ // WHEN we advance beyond the original exception window
+ clock.advanceTime(25000)
+
+ // THEN the original exception window doesn't fire
+ verifyPipelinesNotInvalidated()
+ assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
+
+ // WHEN we advance beyond the new exception window
+ clock.advanceTime(4500)
+
+ // THEN the pipelines are invalidated and no more timeouts are scheduled
+ verifyPipelinesInvalidated()
+ assertExecutorIsClear()
+ assertTrue(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
+ }
+
+ @Test
+ fun testReplacementIsCanceled() {
+ // GIVEN a single notif and smartspace target
+ addEntries(entry1HasRecentlyAlerted)
+ sendTargets(target1)
+ clearPipelineInvocations()
+
+ // WHEN a higher-ranked target arrives
+ val newerEntry = NotificationEntryBuilder()
+ .setPkg(PACKAGE_2)
+ .setId(55)
+ .setLastAudiblyAlertedMs(now - 1000)
+ .build()
+ val newerTarget = buildTargetFor(newerEntry)
+ sendTargets(newerTarget, target1)
+
+ // THEN the timeout of the other target is canceled and it is no longer filtered
+ assertExecutorIsClear()
+ assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
+ verifyPipelinesInvalidated()
+ clearPipelineInvocations()
+
+ // WHEN the entry associated with the newer target later arrives
+ addEntries(newerEntry)
+
+ // THEN the entry is not filtered out (because it recently alerted)
+ assertFalse(filter.shouldFilterOut(newerEntry, clock.uptimeMillis()))
+
+ // WHEN its exception window passes
+ clock.advanceTime(ALERT_WINDOW)
+
+ // THEN we go back to filtering it
+ verifyPipelinesInvalidated()
+ assertExecutorIsClear()
+ assertTrue(filter.shouldFilterOut(newerEntry, clock.uptimeMillis()))
+ }
+
+ @Test
+ fun testRetractedIsCanceled() {
+ // GIVEN A recently alerted target
+ addEntries(entry1HasRecentlyAlerted)
+ sendTargets(target1)
+
+ // WHEN the entry is removed
+ removeEntries(entry1HasRecentlyAlerted)
+
+ // THEN its pending timeout is canceled
+ assertExecutorIsClear()
+ clock.advanceTime(ALERT_WINDOW)
+ verifyPipelinesNotInvalidated()
+ }
+
+ @Test
+ fun testTargetBeforeEntryFunctionsProperly() {
+ // WHEN targets are added before their entries exist
+ sendTargets(target2, target1)
+
+ // THEN neither is filtered out
+ assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
+ assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, now))
+
+ // WHEN the entries are later added
+ addEntries(entry2HasNotRecentlyAlerted, entry1HasRecentlyAlerted)
+
+ // THEN the pipelines are not invalidated (because they're already going to be rerun)
+ // but the first entry is still filtered out properly.
+ verifyPipelinesNotInvalidated()
+ assertTrue(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
+ }
+
+ @Test
+ fun testLockscreenTracking() {
+ // GIVEN a couple of smartspace targets that haven't alerted recently
+ addEntries(entry2HasNotRecentlyAlerted, entry4HasNotRecentlyAlerted)
+ sendTargets(target2, target4)
+ clearPipelineInvocations()
+
+ assertTrue(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
+
+ // WHEN we are no longer on the keyguard
+ statusBarListener.onStateChanged(StatusBarState.SHADE)
+
+ // THEN the new pipeline is invalidated (but the old one isn't because it's not
+ // necessary) because the notif should no longer be filtered out
+ verify(pluggableListener).onPluggableInvalidated(filter)
+ verify(notificationEntryManager, never()).updateNotifications(anyString())
+ assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
+ }
+
+ private fun buildTargetFor(entry: NotificationEntry): SmartspaceTarget {
+ return SmartspaceTarget
+ .Builder("test", ComponentName("test", "class"), UserHandle.CURRENT)
+ .setSourceNotificationKey(entry.key)
+ .build()
+ }
+
+ private fun addEntries(vararg entries: NotificationEntry) {
+ for (entry in entries) {
+ `when`(notifPipeline.getEntry(entry.key)).thenReturn(entry)
+ collectionListener.onEntryAdded(entry)
+ }
+ }
+
+ private fun updateEntries(vararg entries: NotificationEntry) {
+ for (entry in entries) {
+ `when`(notifPipeline.getEntry(entry.key)).thenReturn(entry)
+ collectionListener.onEntryUpdated(entry)
+ }
+ }
+
+ private fun removeEntries(vararg entries: NotificationEntry) {
+ for (entry in entries) {
+ `when`(notifPipeline.getEntry(entry.key)).thenReturn(null)
+ collectionListener.onEntryRemoved(entry, 0)
+ }
+ }
+
+ private fun sendTargets(vararg targets: SmartspaceTarget) {
+ newTargetListener.onSmartspaceTargetsUpdated(targets.toMutableList())
+ }
+
+ private fun verifyPipelinesInvalidated() {
+ verify(pluggableListener).onPluggableInvalidated(filter)
+ verify(notificationEntryManager).updateNotifications(anyString())
+ }
+
+ private fun assertExecutorIsClear() {
+ assertEquals(0, executor.numPending())
+ }
+
+ private fun verifyPipelinesNotInvalidated() {
+ verify(pluggableListener, never()).onPluggableInvalidated(filter)
+ verify(notificationEntryManager, never()).updateNotifications(anyString())
+ }
+
+ private fun clearPipelineInvocations() {
+ clearInvocations(pluggableListener)
+ clearInvocations(notificationEntryManager)
+ }
+}
+
+private val ALERT_WINDOW = TimeUnit.SECONDS.toMillis(30)
+private const val PACKAGE_1 = "com.test.package.1"
+private const val PACKAGE_2 = "com.test.package.2"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 7b0c067..cea49b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -181,16 +181,6 @@
mEntryManager = new NotificationEntryManager(
mock(NotificationEntryManagerLogger.class),
mGroupMembershipManager,
- () -> new NotificationRankingManager(
- () -> mock(NotificationMediaManager.class),
- mGroupMembershipManager,
- mHeadsUpManager,
- mock(NotificationFilter.class),
- mock(NotificationEntryManagerLogger.class),
- mock(NotificationSectionsFeatureManager.class),
- mock(PeopleNotificationIdentifier.class),
- mock(HighPriorityProvider.class)),
- mEnvironment,
mFeatureFlags,
() -> mRowBinder,
() -> mRemoteInputManager,
@@ -198,6 +188,17 @@
mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.class)
);
+ mEntryManager.setRanker(
+ new NotificationRankingManager(
+ () -> mock(NotificationMediaManager.class),
+ mGroupMembershipManager,
+ mHeadsUpManager,
+ mock(NotificationFilter.class),
+ mock(NotificationEntryManagerLogger.class),
+ mock(NotificationSectionsFeatureManager.class),
+ mock(PeopleNotificationIdentifier.class),
+ mock(HighPriorityProvider.class),
+ mEnvironment));
NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager);
NotifBindPipeline pipeline = new NotifBindPipeline(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 84fb368..8758e16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -142,7 +142,6 @@
mNotificationSectionsManager,
mGroupMembershipManger,
mGroupExpansionManager,
- mStatusBarStateController,
mAmbientState,
mFeatureFlags);
mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index 895339f..f376e88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -50,6 +50,7 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -121,6 +122,7 @@
@Mock private NotificationEntryManager mEntryManager;
@Mock private IStatusBarService mIStatusBarService;
@Mock private UiEventLogger mUiEventLogger;
+ @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock private ForegroundServiceDismissalFeatureController mFgFeatureController;
@Mock private ForegroundServiceSectionController mFgServicesSectionController;
@Mock private ForegroundServiceDungeonView mForegroundServiceDungeonView;
@@ -173,6 +175,7 @@
mNotifPipeline,
mNotifCollection,
mEntryManager,
+ mLockscreenShadeTransitionController,
mIStatusBarService,
mUiEventLogger,
mFgFeatureController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index a63d509..b54f923 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -42,6 +42,8 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -90,6 +92,12 @@
private DozeParameters mDozeParameters;
@Mock
private MetricsLogger mMetricsLogger;
+ @Mock
+ private NotificationMediaManager mNotificationMediaManager;
+ @Mock
+ private WakefulnessLifecycle mWakefulnessLifecycle;
+ @Mock
+ private ScreenLifecycle mScreenLifecycle;
private BiometricUnlockController mBiometricUnlockController;
@Before
@@ -109,7 +117,8 @@
mKeyguardViewMediator, mScrimController, mShadeController,
mNotificationShadeWindowController, mKeyguardStateController, mHandler,
mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
- mMetricsLogger, mDumpManager);
+ mMetricsLogger, mDumpManager, mPowerManager,
+ mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
}
@@ -121,8 +130,6 @@
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).showBouncer(eq(false));
- verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(),
- anyFloat());
verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
@@ -161,7 +168,7 @@
}
@Test
- public void onBiometricAuthenticated_whenFingerprint_dismissKeyguard() {
+ public void onBiometricAuthenticated_whenFingerprint_notifyKeyguardAuthenticated() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
// the value of isStrongBiometric doesn't matter here since we only care about the returned
// value of isUnlockingWithBiometricAllowed()
@@ -169,8 +176,6 @@
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
- verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(),
- anyFloat());
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_UNLOCK_COLLAPSING);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 7c8b413..88852f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -74,6 +74,7 @@
private HeadsUpManager mHeadsUpManager;
@Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private RowContentBindStage mBindStage;
+ @Mock PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@Captor private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
private NotificationEntryListener mNotificationEntryListener;
private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>();
@@ -91,7 +92,7 @@
mGroupManager = new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
- () -> mock(PeopleNotificationIdentifier.class),
+ () -> mPeopleNotificationIdentifier,
Optional.of(mock(Bubbles.class)));
mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
@@ -107,15 +108,31 @@
mHeadsUpManager.addListener(mGroupAlertTransferHelper);
}
+ private void mockHasHeadsUpContentView(NotificationEntry entry,
+ boolean hasHeadsUpContentView) {
+ RowContentBindParams params = new RowContentBindParams();
+ if (hasHeadsUpContentView) {
+ params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
+ }
+ when(mBindStage.getStageParams(eq(entry))).thenReturn(params);
+ }
+
+ private void mockHasHeadsUpContentView(NotificationEntry entry) {
+ mockHasHeadsUpContentView(entry, true);
+ }
+
+ private void mockIsPriority(NotificationEntry priorityEntry) {
+ when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
+ .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
+ }
+
@Test
public void testSuppressedSummaryHeadsUpTransfersToChild() {
NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- RowContentBindParams params = new RowContentBindParams();
- params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry);
// Summary will be suppressed because there is only one child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -180,8 +197,7 @@
NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry, false);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -198,8 +214,7 @@
NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry, false);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -250,8 +265,7 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry, false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
@@ -270,8 +284,7 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry, false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
@@ -294,8 +307,7 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY, 47);
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry, false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
@@ -311,4 +323,160 @@
assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
}
+
+ @Test
+ public void testOverriddenSummaryHeadsUpTransfersToPriority() {
+ // Creation order is oldest to newest, meaning the priority will be deemed newest
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ mockIsPriority(priorityEntry);
+
+ // summary gets heads up
+ mHeadsUpManager.showNotification(summaryEntry);
+
+ mockHasHeadsUpContentView(summaryEntry);
+ mockHasHeadsUpContentView(priorityEntry);
+ mockHasHeadsUpContentView(childEntry);
+
+ // Summary will have an alertOverride.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(priorityEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // An overridden summary should transfer its alert state to the priority.
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+ }
+
+ @Test
+ public void testOverriddenSummaryHeadsUpTransferDoesNotAlertPriorityIfUninflated() {
+ // Creation order is oldest to newest, meaning the priority will be deemed newest
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ mockIsPriority(priorityEntry);
+
+ // summary gets heads up
+ mHeadsUpManager.showNotification(summaryEntry);
+
+ mockHasHeadsUpContentView(summaryEntry);
+ mockHasHeadsUpContentView(priorityEntry, false);
+ mockHasHeadsUpContentView(childEntry);
+
+ // Summary will have an alertOverride.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(priorityEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Alert is immediately removed from summary, but we do not show priority yet either as its
+ // content is not inflated.
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+ assertTrue(mGroupAlertTransferHelper.isAlertTransferPending(priorityEntry));
+ }
+
+ @Test
+ public void testOverriddenSummaryHeadsUpTransfersToPriorityButBackAgain() {
+ // Creation order is oldest to newest, meaning the child2 will ultimately be deemed newest
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry childEntry2 = mGroupTestHelper.createChildNotification(groupAlert);
+ mockIsPriority(priorityEntry);
+
+ // summary gets heads up
+ mHeadsUpManager.showNotification(summaryEntry);
+
+ mockHasHeadsUpContentView(summaryEntry);
+ mockHasHeadsUpContentView(priorityEntry);
+ mockHasHeadsUpContentView(childEntry);
+ mockHasHeadsUpContentView(childEntry2);
+
+ // Summary will have an alertOverride.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(priorityEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // An overridden summary should transfer its alert state to the priority.
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+
+ mGroupManager.onEntryAdded(childEntry2);
+
+ // An overridden summary should transfer its alert state to the priority.
+ assertTrue(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry2.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+ }
+
+ @Test
+ public void testOverriddenSuppressedSummaryHeadsUpTransfersToChildThenToPriority() {
+ // Creation order is oldest to newest, meaning the priority will ultimately be deemed newest
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ mockIsPriority(priorityEntry);
+
+ // summary gets heads up
+ mHeadsUpManager.showNotification(summaryEntry);
+
+ mockHasHeadsUpContentView(summaryEntry);
+ mockHasHeadsUpContentView(priorityEntry);
+ mockHasHeadsUpContentView(childEntry);
+
+ // Summary will be suppressed, and the child will receive the alert
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
+
+ // Alert should be transferred "back" from the child to the priority
+ mGroupManager.onEntryAdded(priorityEntry);
+
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+ }
+
+ @Test
+ public void testOverriddenSuppressedSummaryHeadsUpTransfersToPriorityThenToChild() {
+ // Creation order is oldest to newest, meaning the child will ultimately be deemed newest
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ mockIsPriority(priorityEntry);
+
+ // summary gets heads up
+ mHeadsUpManager.showNotification(summaryEntry);
+
+ mockHasHeadsUpContentView(summaryEntry);
+ mockHasHeadsUpContentView(priorityEntry);
+ mockHasHeadsUpContentView(childEntry);
+
+ // Summary will have alert override of the priority
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(priorityEntry);
+
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+
+ // Alert should be transferred "back" from the priority to the child (which is newer)
+ mGroupManager.onEntryAdded(childEntry);
+
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 3e9fd51..0110d7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -21,9 +21,12 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Notification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -33,6 +36,8 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.wm.shell.bubbles.Bubbles;
@@ -58,6 +63,7 @@
private final NotificationGroupTestHelper mGroupTestHelper =
new NotificationGroupTestHelper(mContext);
+ @Mock PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@Mock HeadsUpManager mHeadsUpManager;
@Before
@@ -69,7 +75,7 @@
private void initializeGroupManager() {
mGroupManager = new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
- () -> mock(PeopleNotificationIdentifier.class),
+ () -> mPeopleNotificationIdentifier,
Optional.of(mock(Bubbles.class)));
mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
@@ -153,4 +159,72 @@
assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry));
assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry));
}
+
+ @Test
+ public void testAlertOverrideWithSiblings_0() {
+ helpTestAlertOverrideWithSiblings(0);
+ }
+
+ @Test
+ public void testAlertOverrideWithSiblings_1() {
+ helpTestAlertOverrideWithSiblings(1);
+ }
+
+ @Test
+ public void testAlertOverrideWithSiblings_2() {
+ helpTestAlertOverrideWithSiblings(2);
+ }
+
+ /**
+ * This tests, for a group with a priority entry and the given number of siblings, that:
+ * 1) the priority entry is identified as the alertOverride for the group
+ * 2) the onAlertOverrideChanged method is called at that time
+ * 3) when the priority entry is removed, these are reversed
+ */
+ private void helpTestAlertOverrideWithSiblings(int numSiblings) {
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ // Create entries in an order so that the priority entry can be deemed the newest child.
+ NotificationEntry[] siblings = new NotificationEntry[numSiblings];
+ for (int i = 0; i < numSiblings; i++) {
+ siblings[i] = mGroupTestHelper.createChildNotification(groupAlert);
+ }
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+
+ // The priority entry is an important conversation.
+ when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
+ .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
+
+ // Register a listener so we can verify that the event is sent.
+ OnGroupChangeListener groupChangeListener = mock(OnGroupChangeListener.class);
+ mGroupManager.registerGroupChangeListener(groupChangeListener);
+
+ // Add all the entries. The order here shouldn't matter.
+ mGroupManager.onEntryAdded(summaryEntry);
+ for (int i = 0; i < numSiblings; i++) {
+ mGroupManager.onEntryAdded(siblings[i]);
+ }
+ mGroupManager.onEntryAdded(priorityEntry);
+
+ // Verify that the summary group has the priority child as its alertOverride
+ NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
+ assertEquals(priorityEntry, summaryGroup.alertOverride);
+ verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+
+ // Verify that only the priority notification is isolated from the group
+ assertEquals(priorityEntry, mGroupManager.getGroupSummary(priorityEntry));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(priorityEntry));
+ // Verify that the siblings are NOT isolated from the group
+ for (int i = 0; i < numSiblings; i++) {
+ assertEquals(summaryEntry, mGroupManager.getGroupSummary(siblings[i]));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(siblings[i]));
+ }
+
+ // Remove the priority notification to validate that it is removed as the alertOverride
+ mGroupManager.onEntryRemoved(priorityEntry);
+
+ // verify that the alertOverride is removed when the priority notification is
+ assertNull(summaryGroup.alertOverride);
+ verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+ }
}
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 4bac762..6b4797f 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
@@ -82,6 +82,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -89,6 +90,7 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -101,6 +103,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -224,6 +227,8 @@
@Mock
private NotificationShadeDepthController mNotificationShadeDepthController;
@Mock
+ private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Mock
private AuthController mAuthController;
@Mock
private ScrimController mScrimController;
@@ -243,6 +248,10 @@
private QuickAccessWalletClient mQuickAccessWalletClient;
@Mock
private KeyguardMediaController mKeyguardMediaController;
+ @Mock
+ private PrivacyDotViewController mPrivacyDotViewController;
+ @Mock
+ private SecureSettings mSecureSettings;
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -308,7 +317,9 @@
mKeyguardBypassController, mHeadsUpManager,
mock(NotificationRoundnessManager.class),
mStatusBarStateController,
- new FalsingManagerFake(), new FalsingCollectorFake());
+ new FalsingManagerFake(),
+ mLockscreenShadeTransitionController,
+ new FalsingCollectorFake());
when(mKeyguardStatusViewComponentFactory.build(any()))
.thenReturn(mKeyguardStatusViewComponent);
when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
@@ -324,7 +335,7 @@
mResources,
mLayoutInflater,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
- new FalsingManagerFake(), new FalsingCollectorFake(), mShadeController,
+ new FalsingManagerFake(), new FalsingCollectorFake(),
mNotificationLockscreenUserManager, mNotificationEntryManager,
mKeyguardStateController, mStatusBarStateController, mDozeLog,
mDozeParameters, mCommandQueue, mVibratorHelper,
@@ -338,6 +349,7 @@
mKeyguardQsUserSwitchComponentFactory,
mKeyguardUserSwitcherComponentFactory,
mKeyguardStatusBarViewComponentFactory,
+ mLockscreenShadeTransitionController,
mQSDetailDisplayer,
mGroupManager,
mNotificationAreaController,
@@ -351,7 +363,9 @@
mFeatureFlags,
mQuickAccessWalletClient,
mKeyguardMediaController,
- new FakeExecutor(new FakeSystemClock()));
+ mPrivacyDotViewController,
+ new FakeExecutor(new FakeSystemClock()),
+ mSecureSettings);
mNotificationPanelViewController.initDependencies(
mStatusBar,
mNotificationShelfController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 0a3eec4d..6c1a3c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -31,12 +31,12 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -89,6 +89,7 @@
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Before
public void setUp() {
@@ -112,7 +113,7 @@
mPulseExpansionHandler,
mDynamicPrivacyController,
mBypassController,
- new FalsingManagerFake(),
+ mLockscreenShadeTransitionController,
new FalsingCollectorFake(),
mPluginManager,
mTunerService,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index f98f00c..a431a78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -41,6 +41,7 @@
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.MathUtils;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -1121,6 +1122,32 @@
assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.8f, /* expansion */ 0.2f);
}
+ @Test
+ public void testNotificationTransparency_followsTransitionToFullShade() {
+ mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ mScrimController.setPanelExpansion(1.0f);
+ finishAnimationsImmediately();
+ float shadeLockedAlpha = mNotificationsScrim.getViewAlpha();
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ mScrimController.setPanelExpansion(1.0f);
+ finishAnimationsImmediately();
+ float keyguardAlpha = mNotificationsScrim.getViewAlpha();
+
+ mScrimController.setClipsQsScrim(true);
+ float progress = 0.5f;
+ mScrimController.setTransitionToFullShadeProgress(progress);
+ assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
+ mNotificationsScrim.getViewAlpha(), 0.2);
+ progress = 0.0f;
+ mScrimController.setTransitionToFullShadeProgress(progress);
+ assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
+ mNotificationsScrim.getViewAlpha(), 0.2);
+ progress = 1.0f;
+ mScrimController.setTransitionToFullShadeProgress(progress);
+ assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
+ mNotificationsScrim.getViewAlpha(), 0.2);
+ }
+
private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
mScrimController.setPanelExpansion(expansion);
finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 8601de5..ce45f26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -40,6 +40,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -127,7 +128,8 @@
mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class),
mock(KeyguardStateController.class),
mock(KeyguardIndicationController.class), mStatusBar,
- mock(ShadeControllerImpl.class), mCommandQueue, mInitController,
+ mock(ShadeControllerImpl.class), mock(LockscreenShadeTransitionController.class),
+ mCommandQueue, mInitController,
mNotificationInterruptStateProvider);
mInitController.executePostInitTasks();
ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
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 b3d52b8..5a3683e 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
@@ -101,6 +101,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -267,6 +268,7 @@
@Mock private OngoingCallController mOngoingCallController;
@Mock private SystemStatusAnimationScheduler mAnimationScheduler;
@Mock private StatusBarLocationPublisher mLocationPublisher;
+ @Mock private LockscreenShadeTransitionController mLockscreenTransitionController;
@Mock private FeatureFlags mFeatureFlags;
@Mock private IWallpaperManager mWallpaperManager;
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@@ -437,6 +439,7 @@
mOngoingCallController,
mAnimationScheduler,
mLocationPublisher,
+ mLockscreenTransitionController,
mFeatureFlags,
mKeyguardUnlockAnimationController);
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
index 0e77bb3..e32af60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
@@ -20,7 +20,6 @@
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
-import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
@@ -48,8 +47,7 @@
fun setUp() {
allowTestableLooperAsMainThread()
TestableLooper.get(this).runWithLooper {
- val chipView = LayoutInflater.from(mContext)
- .inflate(R.layout.ongoing_call_chip, null) as LinearLayout
+ val chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null)
textView = chipView.findViewById(R.id.ongoing_call_chip_time)!!
measureTextView()
calculateDoesNotFixText()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 896e330..3a71ecf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -27,8 +27,10 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
+import android.view.View
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
@@ -70,6 +72,7 @@
private val clock = FakeSystemClock()
private val mainExecutor = FakeExecutor(clock)
+ private val uiEventLoggerFake = UiEventLoggerFake()
private lateinit var controller: OngoingCallController
private lateinit var notifCollectionListener: NotifCollectionListener
@@ -78,14 +81,13 @@
@Mock private lateinit var mockActivityStarter: ActivityStarter
@Mock private lateinit var mockIActivityManager: IActivityManager
- private lateinit var chipView: LinearLayout
+ private lateinit var chipView: View
@Before
fun setUp() {
allowTestableLooperAsMainThread()
TestableLooper.get(this).runWithLooper {
- chipView = LayoutInflater.from(mContext)
- .inflate(R.layout.ongoing_call_chip, null) as LinearLayout
+ chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null)
}
MockitoAnnotations.initMocks(this)
@@ -99,7 +101,8 @@
clock,
mockActivityStarter,
mainExecutor,
- mockIActivityManager)
+ mockIActivityManager,
+ OngoingCallLogger(uiEventLoggerFake))
controller.init()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
@@ -113,7 +116,7 @@
}
@Test
- fun onEntryUpdated_isOngoingCallNotif_listenerNotifiedWithRightCallTime() {
+ fun onEntryUpdated_isOngoingCallNotif_listenerNotified() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
@@ -127,6 +130,15 @@
}
@Test
+ fun onEntryUpdated_ongoingCallNotifThenScreeningCallNotif_listenerNotifiedTwice() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+ notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
+
+ verify(mockOngoingCallListener, times(2))
+ .onOngoingCallStateChanged(anyBoolean())
+ }
+
+ @Test
fun onEntryRemoved_ongoingCallNotif_listenerNotified() {
notifCollectionListener.onEntryRemoved(createOngoingCallNotifEntry(), REASON_USER_STOPPED)
@@ -185,6 +197,22 @@
assertThat(controller.hasOngoingCall()).isFalse()
}
+ @Test
+ fun hasOngoingCall_ongoingCallNotifSentThenScreeningCallNotifSent_returnsFalse() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+ notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
+
+ assertThat(controller.hasOngoingCall()).isFalse()
+ }
+
+ @Test
+ fun hasOngoingCall_ongoingCallNotifSentThenUnrelatedNotifSent_returnsTrue() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+ notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
+
+ assertThat(controller.hasOngoingCall()).isTrue()
+ }
+
/**
* This test fakes a theme change during an ongoing call.
*
@@ -197,10 +225,9 @@
// Start an ongoing call.
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- lateinit var newChipView: LinearLayout
+ lateinit var newChipView: View
TestableLooper.get(this).runWithLooper {
- newChipView = LayoutInflater.from(mContext)
- .inflate(R.layout.ongoing_call_chip, null) as LinearLayout
+ newChipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null)
}
// Change the chip view associated with the controller.
@@ -256,9 +283,35 @@
.onOngoingCallStateChanged(anyBoolean())
}
- private fun createOngoingCallNotifEntry(): NotificationEntry {
+ @Test
+ fun chipClicked_clickEventLogged() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ chipView.performClick()
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
+ }
+
+ @Test
+ fun notifyChipVisibilityChanged_visibleEventLogged() {
+ controller.notifyChipVisibilityChanged(true)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
+ }
+ // Other tests for notifyChipVisibilityChanged are in [OngoingCallLogger], since
+ // [OngoingCallController.notifyChipVisibilityChanged] just delegates to that class.
+
+ private fun createOngoingCallNotifEntry() = createCallNotifEntry(ongoingCallStyle)
+
+ private fun createScreeningCallNotifEntry() = createCallNotifEntry(screeningCallStyle)
+
+ private fun createCallNotifEntry(callStyle: Notification.CallStyle): NotificationEntry {
val notificationEntryBuilder = NotificationEntryBuilder()
- notificationEntryBuilder.modifyNotification(context).style = ongoingCallStyle
+ notificationEntryBuilder.modifyNotification(context).style = callStyle
val contentIntent = mock(PendingIntent::class.java)
`when`(contentIntent.intent).thenReturn(mock(Intent::class.java))
@@ -270,6 +323,9 @@
private fun createNotCallNotifEntry() = NotificationEntryBuilder().build()
}
-private val ongoingCallStyle = Notification.CallStyle.forOngoingCall(
- Person.Builder().setName("name").build(),
- /* hangUpIntent= */ mock(PendingIntent::class.java))
+private val person = Person.Builder().setName("name").build()
+private val hangUpIntent = mock(PendingIntent::class.java)
+
+private val ongoingCallStyle = Notification.CallStyle.forOngoingCall(person, hangUpIntent)
+private val screeningCallStyle = Notification.CallStyle.forScreeningCall(
+ person, hangUpIntent, /* answerIntent= */ mock(PendingIntent::class.java))
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
new file mode 100644
index 0000000..ecec124
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SmallTest
+class OngoingCallLoggerTest : SysuiTestCase() {
+ private val uiEventLoggerFake = UiEventLoggerFake()
+ private val ongoingCallLogger = OngoingCallLogger(uiEventLoggerFake)
+
+ @Test
+ fun logChipClicked_clickEventLogged() {
+ ongoingCallLogger.logChipClicked()
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
+ }
+
+ @Test
+ fun logChipVisibilityChanged_changeFromInvisibleToVisible_visibleEventLogged() {
+ ongoingCallLogger.logChipVisibilityChanged(false)
+ ongoingCallLogger.logChipVisibilityChanged(true)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
+ }
+
+ @Test
+ fun logChipVisibilityChanged_changeFromVisibleToInvisible_eventNotLogged() {
+ // Setting the chip to visible here will trigger a log
+ ongoingCallLogger.logChipVisibilityChanged(true)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+
+ ongoingCallLogger.logChipVisibilityChanged(false)
+
+ // Expect that there were no new logs
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ }
+
+ @Test
+ fun logChipVisibilityChanged_visibleThenVisibleAgain_eventNotLogged() {
+ // Setting the chip to visible here will trigger a log
+ ongoingCallLogger.logChipVisibilityChanged(true)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+
+ ongoingCallLogger.logChipVisibilityChanged(true)
+
+ // Expect that there were no new logs
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 0e4b053..d72f432 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -25,12 +26,16 @@
import static org.mockito.Mockito.doReturn;
import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -49,6 +54,7 @@
private AccessibilityManagerWrapper mAccessibilityMgr;
private HeadsUpManager mHeadsUpManager;
private boolean mLivesPastNormalTime;
+ private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
private final class TestableHeadsUpManager extends HeadsUpManager {
TestableHeadsUpManager(Context context) {
@@ -65,6 +71,7 @@
@Before
public void setUp() {
mAccessibilityMgr = mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
+ mDependency.injectTestDependency(UiEventLogger.class, mUiEventLoggerFake);
mHeadsUpManager = new TestableHeadsUpManager(mContext);
super.setUp();
@@ -108,5 +115,28 @@
assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0);
assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0);
}
-}
+ @Test
+ public void testPinEntry_logsPeek() {
+ // Needs full screen intent in order to be pinned
+ final PendingIntent fullScreenIntent = PendingIntent.getActivity(mContext, 0,
+ new Intent(), PendingIntent.FLAG_MUTABLE);
+
+ HeadsUpManager.HeadsUpEntry entryToPin = mHeadsUpManager.new HeadsUpEntry();
+ entryToPin.setEntry(new NotificationEntryBuilder()
+ .setSbn(createNewSbn(0,
+ new Notification.Builder(mContext, "")
+ .setFullScreenIntent(fullScreenIntent, true)))
+ .build());
+ // Note: the standard way to show a notification would be calling showNotification rather
+ // than onAlertEntryAdded. However, in practice showNotification in effect adds
+ // the notification and then updates it; in order to not log twice, the entry needs
+ // to have a functional ExpandableNotificationRow that can keep track of whether it's
+ // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+ mHeadsUpManager.onAlertEntryAdded(entryToPin);
+
+ assertEquals(1, mUiEventLoggerFake.numLogs());
+ assertEquals(HeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
+ mUiEventLoggerFake.eventId(0));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 687ca60..521b958 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -43,6 +43,7 @@
public void setUp() throws Exception {
super.setUp();
when(mWifiInfo.makeCopy(anyLong())).thenReturn(mWifiInfo);
+ when(mWifiInfo.isPrimary()).thenReturn(true);
}
@Test
@@ -277,6 +278,20 @@
}
}
+ @Test
+ public void testNonPrimaryWiFi() {
+ String testSsid = "Test SSID";
+ setWifiEnabled(true);
+ setWifiState(true, testSsid);
+ // Set the ImsType to be IMS_TYPE_WLAN
+ setImsType(2);
+ setWifiLevel(1);
+ verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
+ when(mWifiInfo.isPrimary()).thenReturn(false);
+ setWifiLevel(3);
+ verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
+ }
+
protected void setWifiActivity(int activity) {
// TODO: Not this, because this variable probably isn't sticking around.
mNetworkController.mWifiSignalController.setActivity(activity);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 3d07eb1..32aee2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -20,7 +20,6 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
@@ -100,8 +99,6 @@
private Icon mActionIcon;
- private int mSingleLinePaddingHorizontal;
- private int mDoubleLinePaddingHorizontal;
private int mSpacing;
private NotificationEntry mEntry;
@@ -123,7 +120,7 @@
MockitoAnnotations.initMocks(this);
mReceiver = new BlockingQueueIntentReceiver();
mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
- mKeyguardDismissUtil.setDismissHandler((action, unused) -> action.onDismiss());
+ mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone) -> action.onDismiss());
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mDependency.injectMockDependency(ShadeController.class);
mDependency.injectMockDependency(NotificationRemoteInputManager.class);
@@ -141,10 +138,6 @@
mView = SmartReplyView.inflate(mContext, mConstants);
final Resources res = mContext.getResources();
- mSingleLinePaddingHorizontal = res.getDimensionPixelSize(
- R.dimen.smart_reply_button_padding_horizontal_single_line);
- mDoubleLinePaddingHorizontal = res.getDimensionPixelSize(
- R.dimen.smart_reply_button_padding_horizontal_double_line);
mSpacing = res.getDimensionPixelSize(R.dimen.smart_reply_button_spacing);
mNotification = new Notification.Builder(mContext, "")
@@ -190,7 +183,7 @@
@Test
public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
- mKeyguardDismissUtil.setDismissHandler((action, unused) -> { });
+ mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone) -> { });
setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
@@ -202,7 +195,8 @@
public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
- mKeyguardDismissUtil.setDismissHandler((action, unused) -> actionRef.set(action));
+ mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone)
+ -> actionRef.set(action));
setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
@@ -588,18 +582,6 @@
layout.setBaselineAligned(false);
final boolean isRtl = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
- final int paddingHorizontal;
- switch (lineCount) {
- case 1:
- paddingHorizontal = mSingleLinePaddingHorizontal;
- break;
- case 2:
- paddingHorizontal = mDoubleLinePaddingHorizontal;
- break;
- default:
- fail("Invalid line count " + lineCount);
- return null;
- }
// Add smart replies
Button previous = null;
@@ -617,8 +599,6 @@
true /* delayOnClickListener */))
.iterator()));
for (Button current : inflatedReplies) {
- current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
- current.getPaddingBottom());
if (previous != null) {
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) previous.getLayoutParams();
@@ -647,8 +627,6 @@
// Add smart actions
for (Button current : inflatedSmartActions) {
- current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
- current.getPaddingBottom());
if (previous != null) {
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) previous.getLayoutParams();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 2d9d715..9f1dad8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -185,7 +185,8 @@
Color.valueOf(Color.BLUE), null);
String jsonString =
- "{\"android.theme.customization.system_palette\":\"override.package.name\"}";
+ "{\"android.theme.customization.system_palette\":\"override.package.name\","
+ + "\"android.theme.customization.color_source\":\"preset\"}";
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
@@ -203,6 +204,32 @@
}
@Test
+ public void onWallpaperColorsChanged_resetThemeIfNotPreset() {
+ // Should ask for a new theme when wallpaper colors change
+ WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+ Color.valueOf(Color.BLUE), null);
+
+ String jsonString =
+ "{\"android.theme.customization.system_palette\":\"override.package.name\","
+ + "\"android.theme.customization.color_source\":\"home_wallpaper\"}";
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
+
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+
+ ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+ verify(mSecureSettings).putString(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
+
+ assertThat(updatedSetting.getValue().contains("android.theme.customization.system_palette"))
+ .isFalse();
+
+ verify(mThemeOverlayApplier)
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ }
+
+ @Test
public void onProfileAdded_setsTheme() {
mBroadcastReceiver.getValue().onReceive(null,
new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java
index ce71ac88..570e1d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui.util.concurrency;
+import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;
@@ -25,11 +26,21 @@
*/
public class FakeThreadFactory implements ThreadFactory {
private final FakeExecutor mFakeExecutor;
+ private Handler mHandler;
public FakeThreadFactory(FakeExecutor fakeExecutor) {
mFakeExecutor = fakeExecutor;
}
+ public void setHandler(Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public Handler builderHandlerOnNewThread(String threadName) {
+ return mHandler;
+ }
+
@Override
public Executor buildExecutorOnNewThread(String threadName) {
return mFakeExecutor;
@@ -41,6 +52,11 @@
}
@Override
+ public DelayableExecutor buildDelayableExecutorOnHandler(Handler handler) {
+ return mFakeExecutor;
+ }
+
+ @Override
public DelayableExecutor buildDelayableExecutorOnLooper(Looper looper) {
return mFakeExecutor;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index ac5da17..cd1eb1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -182,6 +182,80 @@
}
@Test
+ public void queryCards_hasCards_showCarousel_badCard_parseLabel_notCrash() {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ Collections.singletonList(createCrazyWalletCard(mContext, true)), 0);
+
+ mController.queryWalletCards();
+ mTestableLooper.processAllMessages();
+
+ verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+
+ QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
+ mCallbackCaptor.getValue();
+
+ assertEquals(mController, callback);
+
+ callback.onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility());
+ assertEquals("This\nis\ncrazy!!", mWalletView.getCardLabel().getText().toString());
+ assertEquals(GONE, mWalletView.getActionButton().getVisibility());
+ assertEquals(GONE, mWalletView.getErrorView().getVisibility());
+ }
+
+ @Test
+ public void queryCards_hasCards_showCarousel_badCard_noLabel_notCrash() {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ Collections.singletonList(createCrazyWalletCard(mContext, false)), 0);
+
+ mController.queryWalletCards();
+ mTestableLooper.processAllMessages();
+
+ verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+
+ QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
+ mCallbackCaptor.getValue();
+
+ assertEquals(mController, callback);
+
+ callback.onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility());
+ assertEquals("", mWalletView.getCardLabel().getText().toString());
+ assertEquals(GONE, mWalletView.getActionButton().getVisibility());
+ assertEquals(GONE, mWalletView.getErrorView().getVisibility());
+ }
+
+ @Test
+ public void queryCards_hasCards_showCarousel_invalidSelectedIndex_notCrash() {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ Collections.singletonList(createCrazyWalletCard(mContext, true)), 8);
+
+ mController.queryWalletCards();
+ mTestableLooper.processAllMessages();
+
+ verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+
+ QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
+ mCallbackCaptor.getValue();
+
+ assertEquals(mController, callback);
+
+ callback.onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(GONE, mWalletView.getCardCarouselContainer().getVisibility());
+ assertEquals(VISIBLE, mWalletView.getEmptyStateView().getVisibility());
+ assertEquals(GONE, mWalletView.getErrorView().getVisibility());
+ }
+
+ @Test
public void queryCards_noCards_showEmptyState() {
GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.EMPTY_LIST, 0);
@@ -329,6 +403,15 @@
.build();
}
+ private WalletCard createCrazyWalletCard(Context context, boolean hasLabel) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder("BadCard", createIcon(), "•••• 1234", pendingIntent)
+ .setCardIcon(null)
+ .setCardLabel(hasLabel ? "This\nis\ncrazy!!" : null)
+ .build();
+ }
+
private static Icon createIcon() {
return Icon.createWithBitmap(Bitmap.createBitmap(70, 44, Bitmap.Config.ARGB_8888));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 79641db..1dd0b21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -37,7 +37,6 @@
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
-import com.android.wm.shell.onehanded.OneHandedGestureHandler;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
@@ -106,11 +105,6 @@
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
- verify(mNavigationModeController).addListener(
- any(NavigationModeController.ModeChangedListener.class));
-
- verify(mOneHanded).registerGestureCallback(any(
- OneHandedGestureHandler.OneHandedGestureEventCallback.class));
verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class));
}
diff --git a/packages/VpnDialogs/res/values-af/strings.xml b/packages/VpnDialogs/res/values-af/strings.xml
index ac82b0e..88ccbd9 100644
--- a/packages/VpnDialogs/res/values-af/strings.xml
+++ b/packages/VpnDialogs/res/values-af/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindingversoek"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil \'n VPN-verbinding opstel wat dit sal toelaat om netwerkverkeer te monitor. Aanvaar dit net as jy die bron vertrou. <br /> <br /> <img src=vpn_icon /> verskyn boaan jou skerm as VPN aktief is."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil \'n VPN-verbinding opstel wat dit toelaat om netwerkverkeer te monitor. Aanvaar dit net as jy die bron vertrou. <br /> <br /> <img src=vpn_icon /> verskyn op jou skerm wanneer VPN aktief is."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is gekoppel"</string>
<string name="session" msgid="6470628549473641030">"Sessie:"</string>
<string name="duration" msgid="3584782459928719435">"Tydsduur:"</string>
diff --git a/packages/VpnDialogs/res/values-am/strings.xml b/packages/VpnDialogs/res/values-am/strings.xml
index ad9773b..9fc5ff4 100644
--- a/packages/VpnDialogs/res/values-am/strings.xml
+++ b/packages/VpnDialogs/res/values-am/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"የግንኙነት ጥያቄ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> የአውታረ መረብ መከታተል የሚያስችል የVPN ግንኑነት ማዋቀር ይፈልጋል። ምንጩን የሚያምኑት ብቻ ከሆኑ ይቀበሉ። <br /> <br /> <img src=vpn_icon /> VPN ገቢር ሲሆን በማያ ገጽዎ ላይኛው ክፍል ላይ ይታያል።"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> የአውታረ መረብ ትራፊክን ለመቆጣጠር የሚያስችል የVPN ግንኙነትን ማዋቀር ይፈልጋል። ምንጩን የሚያምኑ ከሆነ ብቻ ይቀበሉ። <br /> <br /> <img src=vpn_icon /> ማያ ገጹ ላይ VPN ገቢር ሲሆን ይታያል።"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ተያይዟል"</string>
<string name="session" msgid="6470628549473641030">"ክፍለ ጊዜ፡"</string>
<string name="duration" msgid="3584782459928719435">"ጊዜ"</string>
diff --git a/packages/VpnDialogs/res/values-ar/strings.xml b/packages/VpnDialogs/res/values-ar/strings.xml
index 808cde9..b99d761 100644
--- a/packages/VpnDialogs/res/values-ar/strings.xml
+++ b/packages/VpnDialogs/res/values-ar/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"طلب الاتصال"</string>
<string name="warning" msgid="809658604548412033">"يريد <xliff:g id="APP">%s</xliff:g> إعداد الاتصال بالشبكة الافتراضية الخاصة التي تتيح له مراقبة حركة المرور على الشبكة. فلا توافق إلا إذا كنت تثق في المصدر. <br /> <br /> <img src=vpn_icon /> يظهر في الجزء العلوي من الشاشة عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN متصلة"</string>
<string name="session" msgid="6470628549473641030">"الجلسة"</string>
<string name="duration" msgid="3584782459928719435">"المدة:"</string>
diff --git a/packages/VpnDialogs/res/values-as/strings.xml b/packages/VpnDialogs/res/values-as/strings.xml
index 45d8458..ad404cf 100644
--- a/packages/VpnDialogs/res/values-as/strings.xml
+++ b/packages/VpnDialogs/res/values-as/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"সংযোগৰ অনুৰোধ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>এ নেটৱৰ্ক ট্ৰেফিক নিৰীক্ষণ কৰিবলৈ এটা ভিপিএন সংযোগ ছেট আপ কৰিবলৈ বিচাৰিছে৷ আপুনি কেৱল উৎসটোক বিশ্বাস কৰিলেহে অনুৰোধ স্বীকাৰ কৰিব৷ ভিপিএন সক্ৰিয় থকাৰ সময়ত আপোনাৰ স্ক্ৰীণৰ ওপৰত <br /> <br /> <img src=vpn_icon /> দৃশ্যমান হয়৷"</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"ভিপিএন সংযোগ হৈ আছে"</string>
<string name="session" msgid="6470628549473641030">"ছেশ্বন:"</string>
<string name="duration" msgid="3584782459928719435">"সময়সীমা:"</string>
diff --git a/packages/VpnDialogs/res/values-az/strings.xml b/packages/VpnDialogs/res/values-az/strings.xml
index 2bdf23e..09428b8 100644
--- a/packages/VpnDialogs/res/values-az/strings.xml
+++ b/packages/VpnDialogs/res/values-az/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Bağlantı Sorğusu"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN bağlantı yaratmaq istəyir ki, bu da şəbəkə trafikini izləyə bilər. Yalnız mənbəyə güvəndiyiniz halda qəbul edin. VPN aktiv olan zaman <br /> <br /> <img src=vpn_icon /> ekranın yuxarısında görünür."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN qoşuludur"</string>
<string name="session" msgid="6470628549473641030">"Sessiya:"</string>
<string name="duration" msgid="3584782459928719435">"Müddət:"</string>
diff --git a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
index f40e406..a1075d2 100644
--- a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
+++ b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja omogućava praćenje saobraćaja na mreži. Prihvatite samo ako verujete izvoru. <br /> <br /> <img src=vpn_icon /> se prikazuje u vrhu ekrana kada je VPN aktivan."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja joj omogućava da prati mrežni saobraćaj. Prihvatite ovo samo ako imate poverenja u izvor. <br /> <br /> <img src=vpn_icon /> se prikazuje na ekranu kada je VPN aktivan."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN je povezan"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-be/strings.xml b/packages/VpnDialogs/res/values-be/strings.xml
index 0903c8e..88e6a61 100644
--- a/packages/VpnDialogs/res/values-be/strings.xml
+++ b/packages/VpnDialogs/res/values-be/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запыт на падлучэнне"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> спрабуе наладзіць падлучэнне VPN, якое дазваляе сачыць за сеткавым трафікам. Прымайце толькі тады, калі вы давяраеце гэтай крыніцы. Калі VPN актыўны, у верхняй частцы экрана адлюстроўваецца <br /> <br /> <img src=vpn_icon />."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN падключаны"</string>
<string name="session" msgid="6470628549473641030">"Сессія"</string>
<string name="duration" msgid="3584782459928719435">"Працягласць:"</string>
diff --git a/packages/VpnDialogs/res/values-bg/strings.xml b/packages/VpnDialogs/res/values-bg/strings.xml
index 9ac853d..6345f1d 100644
--- a/packages/VpnDialogs/res/values-bg/strings.xml
+++ b/packages/VpnDialogs/res/values-bg/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Заявка за свързване"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> иска да настрои връзка с виртуална частна мрежа (VPN), за да може да наблюдава мрежовия трафик. Приемете само ако източникът е надежден. Иконата <br /> <br /> <img src=vpn_icon /> се показва в долната част на екрана при активирана VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> иска да настрои връзка с VPN, за да може да наблюдава трафика в мрежата. Приемете само ако източникът е надежден. <br /> <br /> <img src=vpn_icon /> се показва на екрана при активирана VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN е свързана"</string>
<string name="session" msgid="6470628549473641030">"Сесия:"</string>
<string name="duration" msgid="3584782459928719435">"Продължителност:"</string>
diff --git a/packages/VpnDialogs/res/values-bn/strings.xml b/packages/VpnDialogs/res/values-bn/strings.xml
index 5e11fd9..041e46c 100644
--- a/packages/VpnDialogs/res/values-bn/strings.xml
+++ b/packages/VpnDialogs/res/values-bn/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"সংযোগের অনুরোধ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> এমন একটি VPN সংযোগ সেট-আপ করতে চাচ্ছে যেটি দিয়ে এটি নেটওয়ার্ক ট্রাফিক নিরীক্ষণ করতে পারবে। আপনি যদি উৎসটিকে বিশ্বাস করেন, তাহলেই কেবল এতে সম্মতি দিন। VPN সক্রিয় থাকলে আপনার স্ক্রীনের উপরে <br /> <br /> <img src=vpn_icon /> দেখা যাবে।"</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN সংযুক্ত হয়েছে"</string>
<string name="session" msgid="6470628549473641030">"অধিবেশন:"</string>
<string name="duration" msgid="3584782459928719435">"সময়কাল:"</string>
diff --git a/packages/VpnDialogs/res/values-bs/strings.xml b/packages/VpnDialogs/res/values-bs/strings.xml
index 56812d5..fa5f4ea 100644
--- a/packages/VpnDialogs/res/values-bs/strings.xml
+++ b/packages/VpnDialogs/res/values-bs/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtjev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi podesiti VPN vezu koja joj omogućava praćenje mrežnog saobraćaja. Prihvatite samo ako je izvor pouzdan. <br /> <br /> <img src=vpn_icon /> se pojavi na vrhu ekrana kada je VPN aktivna."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu koja joj omogućava praćenje mrežnog saobraćaja. Prihvatite samo ako vjerujete izvoru. Kada je VPN aktivan, na ekranu se prikazuje ikona <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN veza uspostavljena"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-ca/strings.xml b/packages/VpnDialogs/res/values-ca/strings.xml
index 97738c3..7674c2c 100644
--- a/packages/VpnDialogs/res/values-ca/strings.xml
+++ b/packages/VpnDialogs/res/values-ca/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Sol·licitud de connexió"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vol configurar una connexió VPN que li permeti controlar el trànsit de xarxa. Accepta la sol·licitud només si prové d\'una font de confiança. <br /> <br /> <img src=vpn_icon /> es mostra a la part superior de la pantalla quan la VPN està activada."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"La VPN està connectada"</string>
<string name="session" msgid="6470628549473641030">"Sessió:"</string>
<string name="duration" msgid="3584782459928719435">"Durada:"</string>
diff --git a/packages/VpnDialogs/res/values-cs/strings.xml b/packages/VpnDialogs/res/values-cs/strings.xml
index 5cc809c..c06f6ff 100644
--- a/packages/VpnDialogs/res/values-cs/strings.xml
+++ b/packages/VpnDialogs/res/values-cs/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Žádost o připojení"</string>
<string name="warning" msgid="809658604548412033">"Aplikace <xliff:g id="APP">%s</xliff:g> žádá o nastavení připojení VPN, pomocí kterého bude moci sledovat síťový provoz. Povolte, jen pokud zdroji důvěřujete. <br /> <br /> <img src=vpn_icon /> – když je síť VPN aktivní, v horní části obrazovky se zobrazuje tato ikona."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikace <xliff:g id="APP">%s</xliff:g> chce nastavit připojení VPN, které umožňuje sledovat síťový provoz. Povolte, jen pokud zdroji důvěřujete. <br /> <br /> <img src=vpn_icon /> – když je síť VPN aktivní, na obrazovce se zobrazuje tato ikona."</string>
<string name="legacy_title" msgid="192936250066580964">"Síť VPN je připojena"</string>
<string name="session" msgid="6470628549473641030">"Relace:"</string>
<string name="duration" msgid="3584782459928719435">"Doba trvání:"</string>
diff --git a/packages/VpnDialogs/res/values-da/strings.xml b/packages/VpnDialogs/res/values-da/strings.xml
index 7641158..a4ddc19 100644
--- a/packages/VpnDialogs/res/values-da/strings.xml
+++ b/packages/VpnDialogs/res/values-da/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Forbindelsesanmodning"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vil konfigurere en VPN-forbindelse, der giver appen mulighed for at registrere netværkstrafik. Du bør kun acceptere dette, hvis du har tillid til kilden. <br /> <br /> <img src=vpn_icon /> vises øverst på din skærm, når VPN-forbindelsen er aktiv."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> anmoder om at konfigurere en VPN-forbindelse, der giver appen tilladelse til at holde øje med netværkstrafik. Du bør kun acceptere dette, hvis du har tillid til appen. <br /> <br /> <img src=vpn_icon /> vises på din skærm, når VPN-forbindelsen er aktiv."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN er tilsluttet"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Varighed:"</string>
diff --git a/packages/VpnDialogs/res/values-de/strings.xml b/packages/VpnDialogs/res/values-de/strings.xml
index 0f1e009..f38e395 100644
--- a/packages/VpnDialogs/res/values-de/strings.xml
+++ b/packages/VpnDialogs/res/values-de/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindungsanfrage"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. Wenn VPN aktiv ist, wird oben im Display <br /> <br /> <img src=vpn_icon /> angezeigt."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. <br /> <br /> <img src=vpn_icon /> wird auf dem Display angezeigt, wenn VPN aktiv ist."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ist verbunden"</string>
<string name="session" msgid="6470628549473641030">"Sitzung:"</string>
<string name="duration" msgid="3584782459928719435">"Dauer:"</string>
diff --git a/packages/VpnDialogs/res/values-el/strings.xml b/packages/VpnDialogs/res/values-el/strings.xml
index 78bcc43..e3eb460 100644
--- a/packages/VpnDialogs/res/values-el/strings.xml
+++ b/packages/VpnDialogs/res/values-el/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Αίτημα σύνδεσης"</string>
<string name="warning" msgid="809658604548412033">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί να ρυθμίσει μια σύνδεση VPN που της επιτρέπει να παρακολουθεί την επισκεψιμότητα του δικτύου. Αποδεχτείτε το αίτημα μόνο εάν εμπιστεύεστε την πηγή. Το εικονίδιο <br /> <br /> <img src=vpn_icon /> εμφανίζεται στο επάνω μέρος της οθόνης σας όταν είναι ενεργό το VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί να ρυθμίσει μια σύνδεση VPN που της επιτρέπει να παρακολουθεί την επισκεψιμότητα του δικτύου. Αποδεχτείτε το αίτημα μόνο εάν εμπιστεύεστε την πηγή. Το εικονίδιο <br /> <br /> <img src=vpn_icon /> εμφανίζεται στην οθόνη σας όταν είναι ενεργό το VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Το VPN συνδέθηκε"</string>
<string name="session" msgid="6470628549473641030">"Περίοδος σύνδεσης"</string>
<string name="duration" msgid="3584782459928719435">"Διάρκεια:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rAU/strings.xml b/packages/VpnDialogs/res/values-en-rAU/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rAU/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rAU/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rCA/strings.xml b/packages/VpnDialogs/res/values-en-rCA/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rCA/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rGB/strings.xml b/packages/VpnDialogs/res/values-en-rGB/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rGB/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rGB/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rIN/strings.xml b/packages/VpnDialogs/res/values-en-rIN/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rIN/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rIN/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rXC/strings.xml b/packages/VpnDialogs/res/values-en-rXC/strings.xml
index 9d010e6..f5e2deb 100644
--- a/packages/VpnDialogs/res/values-en-rXC/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rXC/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-es-rUS/strings.xml b/packages/VpnDialogs/res/values-es-rUS/strings.xml
index 21cfc04..108a24e 100644
--- a/packages/VpnDialogs/res/values-es-rUS/strings.xml
+++ b/packages/VpnDialogs/res/values-es-rUS/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN capaz de controlar el tráfico de la red. Acéptala solo si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparece en la parte superior de la pantalla cuando se activa la VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita supervisar el tráfico de red. Solo acéptala si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparecerá en tu pantalla cuando se active la VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"La VPN está conectada."</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-es/strings.xml b/packages/VpnDialogs/res/values-es/strings.xml
index 372147f..0eaf359 100644
--- a/packages/VpnDialogs/res/values-es/strings.xml
+++ b/packages/VpnDialogs/res/values-es/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN para controlar el tráfico de red. Solo debes aceptarla si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparece en la parte superior de la pantalla cuando se active la conexión VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita monitorizar el tráfico de red. Acéptalo solo si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparecerá en la pantalla cuando la VPN esté activa."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN conectada"</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-et/strings.xml b/packages/VpnDialogs/res/values-et/strings.xml
index c328cd7..3c7b3f3 100644
--- a/packages/VpnDialogs/res/values-et/strings.xml
+++ b/packages/VpnDialogs/res/values-et/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ühendamise taotlus"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> tahab seadistada VPN-i ühenduse, mis võimaldab jälgida võrguliiklust. Nõustuge ainult siis, kui usaldate seda allikat. <br /> <br /> <img src=vpn_icon /> kuvatakse ekraani ülaservas, kui VPN on aktiivne."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN on ühendatud"</string>
<string name="session" msgid="6470628549473641030">"Seansid"</string>
<string name="duration" msgid="3584782459928719435">"Kestus:"</string>
diff --git a/packages/VpnDialogs/res/values-eu/strings.xml b/packages/VpnDialogs/res/values-eu/strings.xml
index a3b7716e..74c5378 100644
--- a/packages/VpnDialogs/res/values-eu/strings.xml
+++ b/packages/VpnDialogs/res/values-eu/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Konektatzeko eskaera"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexioa ezarri nahi du sareko trafikoa kontrolatzeko. Iturburua fidagarria bada bakarrik baimendu. <br /> <br /> VPN konexioa aktibo dagoenean, <img src=vpn_icon /> agertuko da pantailaren goialdean."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexio bat konfiguratu nahi du sareko trafikoa gainbegiratzeko. Onartu soilik iturburuaz fidatzen bazara. <br /> <br /> <img src=vpn_icon /> agertzen da pantailan, VPNa aktibo dagoenean."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN sarera konektatuta dago"</string>
<string name="session" msgid="6470628549473641030">"Saioa:"</string>
<string name="duration" msgid="3584782459928719435">"Iraupena:"</string>
diff --git a/packages/VpnDialogs/res/values-fa/strings.xml b/packages/VpnDialogs/res/values-fa/strings.xml
index 56f847c..77223cb8 100644
--- a/packages/VpnDialogs/res/values-fa/strings.xml
+++ b/packages/VpnDialogs/res/values-fa/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"درخواست اتصال"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> میخواهد یک اتصال VPN راهاندازی کند که به آن امکان نظارت بر ترافیک شبکه را میدهد. فقط در صورتی بپذیرید که به منبع آن اطمینان دارید. هنگامی که VPN فعال شد، <br /> <br /> <img src=vpn_icon /> در بالای صفحه نمایش شما نشان داده میشود."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN متصل است"</string>
<string name="session" msgid="6470628549473641030">"جلسه:"</string>
<string name="duration" msgid="3584782459928719435">"مدت زمان:"</string>
diff --git a/packages/VpnDialogs/res/values-fi/strings.xml b/packages/VpnDialogs/res/values-fi/strings.xml
index 91c918a..8abca06 100644
--- a/packages/VpnDialogs/res/values-fi/strings.xml
+++ b/packages/VpnDialogs/res/values-fi/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Yhteyspyyntö"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> haluaa tehdä asetukset VPN-yhteydellä, jonka kautta sovellus voi valvoa verkkoliikennettä. Hyväksy vain, jos lähde on luotettava. <br /> <br /> <img src=vpn_icon /> näkyy ruudun yläreunassa, kun VPN on käytössä."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> haluaa muodostaa VPN-yhteyden, jonka avulla se voi valvoa verkkoliikennettä. Salli tämä vain, jos luotat lähteeseen. <br /> <br /> <img src=vpn_icon /> näkyy näytölläsi, kun VPN on aktiivinen."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN on yhdistetty"</string>
<string name="session" msgid="6470628549473641030">"Käyttökerta"</string>
<string name="duration" msgid="3584782459928719435">"Kesto:"</string>
diff --git a/packages/VpnDialogs/res/values-fr-rCA/strings.xml b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
index aa86c7c..876111c 100644
--- a/packages/VpnDialogs/res/values-fr-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Demande de connexion"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> veut configurer une connexion RPV qui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. <br /><br /><img src=vpn_icon/> s\'affiche dans le haut de votre écran lorsqu\'une connexion RPV est active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> veut configurer une connexion RPV qui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. <br /> <br /> <img src=vpn_icon /> s\'affiche dans le haut de votre écran lorsqu\'une connexion RPV est active."</string>
<string name="legacy_title" msgid="192936250066580964">"RPV connecté"</string>
<string name="session" msgid="6470628549473641030">"Session :"</string>
<string name="duration" msgid="3584782459928719435">"Durée :"</string>
diff --git a/packages/VpnDialogs/res/values-fr/strings.xml b/packages/VpnDialogs/res/values-fr/strings.xml
index 7180119..7b6a950 100644
--- a/packages/VpnDialogs/res/values-fr/strings.xml
+++ b/packages/VpnDialogs/res/values-fr/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Demande de connexion"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> souhaite configurer une connexion VPN qui lui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. <br /> <br /> <img src=vpn_icon /> s\'affiche en haut de votre écran lorsqu\'une connexion VPN est active."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN connecté"</string>
<string name="session" msgid="6470628549473641030">"Session :"</string>
<string name="duration" msgid="3584782459928719435">"Durée :"</string>
diff --git a/packages/VpnDialogs/res/values-gl/strings.xml b/packages/VpnDialogs/res/values-gl/strings.xml
index 8a66d08..cd8ee8d 100644
--- a/packages/VpnDialogs/res/values-gl/strings.xml
+++ b/packages/VpnDialogs/res/values-gl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitude de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quere configurar unha conexión VPN que lle permite controlar o tráfico da rede. Acepta soamente se confías na fonte. <br /> <br /> <img src=vpn_icon /> aparece na parte superior da pantalla cando se activa a VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A aplicación <xliff:g id="APP">%s</xliff:g> quere configurar unha conexión VPN que lle permita supervisar o tráfico de rede. Acepta só se confías nela. <br /> <br /> <img src=vpn_icon /> aparece na pantalla cando a VPN está activa."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN está conectada"</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-gu/strings.xml b/packages/VpnDialogs/res/values-gu/strings.xml
index 961711c..fd6e116 100644
--- a/packages/VpnDialogs/res/values-gu/strings.xml
+++ b/packages/VpnDialogs/res/values-gu/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"કનેક્શન વિનંતી"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN કનેક્શન સેટ કરવા માગે છે જે તેને નેટવર્ક ટ્રાફિક મૉનિટર કરવાની મંજૂરી આપે છે. જો તમને સ્રોત પર વિશ્વાસ હોય તો જ સ્વીકારો. <br /> <br /> <img src=vpn_icon /> તમારી સ્ક્રીનની ટોચ પર ત્યારે દેખાય છે જ્યારે VPN સક્રિય હોય છે."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN કનેક્ટ કરેલું છે"</string>
<string name="session" msgid="6470628549473641030">"સત્ર:"</string>
<string name="duration" msgid="3584782459928719435">"અવધિ:"</string>
diff --git a/packages/VpnDialogs/res/values-hi/strings.xml b/packages/VpnDialogs/res/values-hi/strings.xml
index eed0858..916c25d 100644
--- a/packages/VpnDialogs/res/values-hi/strings.xml
+++ b/packages/VpnDialogs/res/values-hi/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"कनेक्शन अनुरोध"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> वीपीएन कनेक्शन सेट अप करना चाहता है, जिससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. इसकी मंज़ूरी तभी दें जब आपको इस पर भरोसा हो. वीपीएन चालू होने पर <br /> <br /> <img src=vpn_icon /> आपकी स्क्रीन के सबसे ऊपर दिखाई देता है."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN कनेक्ट है"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"अवधि:"</string>
diff --git a/packages/VpnDialogs/res/values-hr/strings.xml b/packages/VpnDialogs/res/values-hr/strings.xml
index aa9e436..576d997 100644
--- a/packages/VpnDialogs/res/values-hr/strings.xml
+++ b/packages/VpnDialogs/res/values-hr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtjev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu pomoću koje će moći nadzirati mrežni promet. Prihvatite samo ako smatrate izvor pouzdanim. Kada je VPN aktivan, pri vrhu zaslona prikazuje se <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu pomoću koje će moći nadzirati mrežni promet. Prihvatite samo ako smatrate izvor pouzdanim. Kad je VPN aktivan, na zaslonu se prikazuje ikona <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN je spojen"</string>
<string name="session" msgid="6470628549473641030">"Sesija"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-hu/strings.xml b/packages/VpnDialogs/res/values-hu/strings.xml
index 703aa79..4864484 100644
--- a/packages/VpnDialogs/res/values-hu/strings.xml
+++ b/packages/VpnDialogs/res/values-hu/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kapcsolódási kérés"</string>
<string name="warning" msgid="809658604548412033">"A(z) <xliff:g id="APP">%s</xliff:g> VPN kapcsolatot akar beállítani, amelynek segítségével figyelheti a hálózati forgalmat. Csak akkor fogadja el, ha megbízik a forrásban. <br /> <br /> Amikor a VPN aktív, <img src=vpn_icon /> ikon jelenik meg a képernyő tetején."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"A VPN csatlakoztatva van"</string>
<string name="session" msgid="6470628549473641030">"Munkamenet:"</string>
<string name="duration" msgid="3584782459928719435">"Időtartam:"</string>
diff --git a/packages/VpnDialogs/res/values-hy/strings.xml b/packages/VpnDialogs/res/values-hy/strings.xml
index c296c85..64053a4 100644
--- a/packages/VpnDialogs/res/values-hy/strings.xml
+++ b/packages/VpnDialogs/res/values-hy/strings.xml
@@ -17,7 +17,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Միացման հայց"</string>
- <string name="warning" msgid="809658604548412033">"«<xliff:g id="APP">%s</xliff:g>» հավելվածը ցանկանում է VPN կապ հաստատել՝ ցանցային երթևեկը հսկելու համար: Թույլատրեք, միայն եթե վստահում եք աղբյուրին: Երբ VPN-ն ակտիվ լինի, ձեր էկրանի վերին հատվածում կհայտնվի <br /> <br /> <img src=vpn_icon /> պատկերը:"</string>
+ <string name="warning" msgid="809658604548412033">"«<xliff:g id="APP">%s</xliff:g>» հավելվածը ցանկանում է VPN կապ հաստատել՝ ցանցային երթևեկը հսկելու համար: Թույլատրեք, միայն եթե վստահում եք աղբյուրին։ Երբ VPN-ն ակտիվ լինի, ձեր էկրանի վերին հատվածում կհայտնվի <br /> <br /> <img src=vpn_icon /> պատկերը:"</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN-ը կապակցված է"</string>
<string name="session" msgid="6470628549473641030">"Աշխատաշրջան`"</string>
<string name="duration" msgid="3584782459928719435">"Տևողությունը՝"</string>
@@ -25,7 +27,7 @@
<string name="data_received" msgid="4062776929376067820">"Ստացվել է՝"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> բայթ / <xliff:g id="NUMBER_1">%2$s</xliff:g> փաթեթ"</string>
<string name="always_on_disconnected_title" msgid="1906740176262776166">"Չի հաջողվում միանալ միշտ միացված VPN-ին"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Ձեր հեռախոսը կօգտագործի հանրային ցանցը, մինչև նորից կարողանա միանալ <xliff:g id="VPN_APP_1">%1$s</xliff:g>-ին:"</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Ձեր հեռախոսը կօգտագործի հանրային ցանցը, մինչև նորից կարողանա միանալ <xliff:g id="VPN_APP_1">%1$s</xliff:g>-ին։"</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Մինչև VPN-ը նորից չմիանա, դուք կապ չեք ունենա:"</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Փոխել VPN-ի կարգավորումները"</string>
diff --git a/packages/VpnDialogs/res/values-in/strings.xml b/packages/VpnDialogs/res/values-in/strings.xml
index 18ef372a..059008f 100644
--- a/packages/VpnDialogs/res/values-in/strings.xml
+++ b/packages/VpnDialogs/res/values-in/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Permintaan sambungan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ingin menyiapkan sambungan VPN yang memungkinkannya memantau traffic jaringan. Terima hanya jika Anda memercayai sumber. <br /> <br /> <img src=vpn_icon /> muncul di bagian atas layar Anda saat VPN aktif."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ingin menyiapkan koneksi VPN yang memungkinkannya memantau traffic jaringan. Hanya terima jika Anda memercayai sumbernya. <br /> <br /> <img src=vpn_icon /> muncul di layar bila VPN aktif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN tersambung"</string>
<string name="session" msgid="6470628549473641030">"Sesi:"</string>
<string name="duration" msgid="3584782459928719435">"Durasi:"</string>
diff --git a/packages/VpnDialogs/res/values-is/strings.xml b/packages/VpnDialogs/res/values-is/strings.xml
index 70fb40f..53a5c02 100644
--- a/packages/VpnDialogs/res/values-is/strings.xml
+++ b/packages/VpnDialogs/res/values-is/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Beiðni um tengingu"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vill setja upp VPN-tengingu til þess að geta fylgst með netumferð. Samþykktu þetta aðeins ef þú treystir upprunanum. <br /> <br /> <img src=vpn_icon /> birtist efst á skjánum þegar VPN er virkt."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN er tengt"</string>
<string name="session" msgid="6470628549473641030">"Lota:"</string>
<string name="duration" msgid="3584782459928719435">"Tímalengd:"</string>
diff --git a/packages/VpnDialogs/res/values-it/strings.xml b/packages/VpnDialogs/res/values-it/strings.xml
index 2602493..c443c51 100644
--- a/packages/VpnDialogs/res/values-it/strings.xml
+++ b/packages/VpnDialogs/res/values-it/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Richiesta di connessione"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vuole impostare una connessione VPN che le consenta di monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, nella parte superiore dello schermo viene visualizzata l\'icona <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vuole configurare una connessione VPN che le consenta di monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, sullo schermo viene visualizzata l\'icona <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN connessa"</string>
<string name="session" msgid="6470628549473641030">"Sessione:"</string>
<string name="duration" msgid="3584782459928719435">"Durata:"</string>
diff --git a/packages/VpnDialogs/res/values-iw/strings.xml b/packages/VpnDialogs/res/values-iw/strings.xml
index ebabd4e..4156944 100644
--- a/packages/VpnDialogs/res/values-iw/strings.xml
+++ b/packages/VpnDialogs/res/values-iw/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"בקשת חיבור"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> רוצה להגדיר חיבור VPN שיאפשר לו לפקח על תעבורת הרשת. אשר את הבקשה רק אם אתה נותן אמון במקור. <br /> <br /> <img src=vpn_icon /> מופיע בחלק העליון של המסך כאשר VPN פעיל."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN מחובר"</string>
<string name="session" msgid="6470628549473641030">"הפעלה"</string>
<string name="duration" msgid="3584782459928719435">"משך:"</string>
diff --git a/packages/VpnDialogs/res/values-ja/strings.xml b/packages/VpnDialogs/res/values-ja/strings.xml
index 8480692..e03e9d3 100644
--- a/packages/VpnDialogs/res/values-ja/strings.xml
+++ b/packages/VpnDialogs/res/values-ja/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"接続リクエスト"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> がネットワーク トラフィックを監視するため VPN 接続をセットアップしようとしています。信頼できるソースである場合にのみ許可してください。<br /> <br /> VPN がアクティブになると画面の上部に <img src=vpn_icon /> が表示されます。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> は、ネットワーク トラフィックを監視できるよう、VPN 接続を設定するよう求めています。ソースを信頼できる場合のみ、許可してください。VPN が有効になると、画面に <br /> <br /> <img src=vpn_icon /> が表示されます。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN接続済み"</string>
<string name="session" msgid="6470628549473641030">"セッション:"</string>
<string name="duration" msgid="3584782459928719435">"期間:"</string>
diff --git a/packages/VpnDialogs/res/values-ka/strings.xml b/packages/VpnDialogs/res/values-ka/strings.xml
index e5a0753..9c4388e 100644
--- a/packages/VpnDialogs/res/values-ka/strings.xml
+++ b/packages/VpnDialogs/res/values-ka/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"კავშირის მოთხოვნა"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> სურს დააყენოს VPN კავშირი, რაც ქსელის ტრაფიკის მონიტორინგის საშუალებას იძლევა. მიიღოთ მხოლოდ ისეთ შემთხვევაში, თუ წყაროს ენდობით. <br /> <br /> <img src=vpn_icon /> თქვენი ეკრანის სიის თავში გამოჩნდება, როდესაც VPN აქტიურია."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>-ს სურს დააყენოს VPN კავშირი, რაც ქსელის ტრაფიკის მონიტორინგის საშუალებას იძლევა. დათანხმდით მხოლოდ იმ შემთხვევაში, თუ წყაროს ენდობით. <br /> <br /> <img src=vpn_icon /> თქვენს ეკრანზე გამოჩნდება, როდესაც VPN აქტიურია."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN დაკავშირებულია"</string>
<string name="session" msgid="6470628549473641030">"სესია:"</string>
<string name="duration" msgid="3584782459928719435">"ხანგრძლივობა:"</string>
diff --git a/packages/VpnDialogs/res/values-kk/strings.xml b/packages/VpnDialogs/res/values-kk/strings.xml
index 79f79c3..4948217 100644
--- a/packages/VpnDialogs/res/values-kk/strings.xml
+++ b/packages/VpnDialogs/res/values-kk/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Байланысты сұрау"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN байланысын орнатқысы келеді, бұл оған желілік трафикті бақылауға мүмкіндік береді. Көзге сенсеңіз ғана қабылдаңыз. VPN белсенді болғанда экранның жоғарғы жағында <br /> <br /> <img src=vpn_icon /> көрсетіледі."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"ВЖЖ қосылған"</string>
<string name="session" msgid="6470628549473641030">"Сессия:"</string>
<string name="duration" msgid="3584782459928719435">"Ұзақтығы:"</string>
diff --git a/packages/VpnDialogs/res/values-km/strings.xml b/packages/VpnDialogs/res/values-km/strings.xml
index 06f34db..0ed2e84b 100644
--- a/packages/VpnDialogs/res/values-km/strings.xml
+++ b/packages/VpnDialogs/res/values-km/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"សំណើសុំការតភ្ជាប់"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ចង់បង្កើតការតភ្ជាប់ VPN ដែលអនុញ្ញាតឲ្យវាត្រួតពិនិត្យចរាចរបណ្ដាញ។ ព្រមទទួល ប្រសិនបើអ្នកទុកចិត្តលើប្រភពតែប៉ុណ្ណោះ។ <br /> <br /> <img src=vpn_icon /> នឹងលេចឡើងនៅផ្នែកខាងលើនៃអេក្រង់របស់អ្នក ពេល VPN សកម្ម។"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ចង់រៀបចំការតភ្ជាប់ VPN ដែលអនុញ្ញាតឱ្យវាត្រួតពិនិត្យចរាចរណ៍បណ្តាញ។ យល់ព្រម ប្រសិនបើអ្នកជឿទុកចិត្តលើប្រភពនេះតែប៉ុណ្ណោះ។ <br /> <br /> <img src=vpn_icon /> បង្ហាញនៅលើអេក្រង់របស់អ្នក នៅពេល VPN កំពុងដំណើរការ។"</string>
<string name="legacy_title" msgid="192936250066580964">"បានភ្ជាប់ VPN"</string>
<string name="session" msgid="6470628549473641030">"សម័យ៖"</string>
<string name="duration" msgid="3584782459928719435">"ថិរវេលា៖"</string>
diff --git a/packages/VpnDialogs/res/values-kn/strings.xml b/packages/VpnDialogs/res/values-kn/strings.xml
index 040cd6c..3ebabe3 100644
--- a/packages/VpnDialogs/res/values-kn/strings.xml
+++ b/packages/VpnDialogs/res/values-kn/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ಸಂಪರ್ಕ ವಿನಂತಿ"</string>
<string name="warning" msgid="809658604548412033">"ನೆಟ್ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ಅನುಮತಿಸುವಂತಹ VPN ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಸಲು <xliff:g id="APP">%s</xliff:g> ಬಯಸುತ್ತದೆ. ನೀವು ಮೂಲವನ್ನು ನಂಬಿದರೆ ಮಾತ್ರ ಸಮ್ಮತಿಸಿ. VPN ಸಕ್ರಿಯವಾಗಿರುವಾಗ ನಿಮ್ಮ ಪರದೆಯ ಮೇಲ್ಭಾಗದಲ್ಲಿ <br /> <br /> <img src=vpn_icon /> ಗೋರಿಸುತ್ತದೆ."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
<string name="session" msgid="6470628549473641030">"ಸೆಷನ್:"</string>
<string name="duration" msgid="3584782459928719435">"ಅವಧಿ:"</string>
diff --git a/packages/VpnDialogs/res/values-ko/strings.xml b/packages/VpnDialogs/res/values-ko/strings.xml
index 6ad4976..6e179bb 100644
--- a/packages/VpnDialogs/res/values-ko/strings.xml
+++ b/packages/VpnDialogs/res/values-ko/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"연결 요청"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>에서 네트워크 트래픽을 모니터링하도록 허용하는 VPN 연결을 설정하려고 합니다. 출처를 신뢰할 수 있는 경우에만 수락하세요. VPN이 활성화되면 <br /> <br /> <img src=vpn_icon />이 화면 위에 표시됩니다."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>에서 네트워크 트래픽을 모니터링할 수 있도록 VPN 연결을 설정하려고 합니다. 소스를 신뢰할 수 있는 경우에만 수락하세요. VPN이 활성 상태일 때는 <br /> <br /> <img src=vpn_icon /> 아이콘이 화면에 표시됩니다."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN이 연결되었습니다."</string>
<string name="session" msgid="6470628549473641030">"세션:"</string>
<string name="duration" msgid="3584782459928719435">"기간:"</string>
diff --git a/packages/VpnDialogs/res/values-ky/strings.xml b/packages/VpnDialogs/res/values-ky/strings.xml
index 23c9be8..4083665 100644
--- a/packages/VpnDialogs/res/values-ky/strings.xml
+++ b/packages/VpnDialogs/res/values-ky/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Туташуу сурамы"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> тармактык трафикти көзөмөлдөөгө уруксат берген VPN туташуусун орноткусу келет. Аны булакка ишенсеңиз гана кабыл алыңыз. <br /> <br /> <img src=vpn_icon /> VPN иштеп турганда экраныңыздын жогору жагынан көрүнөт."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN байланышта"</string>
<string name="session" msgid="6470628549473641030">"Сессия:"</string>
<string name="duration" msgid="3584782459928719435">"Узактыгы:"</string>
diff --git a/packages/VpnDialogs/res/values-lo/strings.xml b/packages/VpnDialogs/res/values-lo/strings.xml
index c591308..cec69f0 100644
--- a/packages/VpnDialogs/res/values-lo/strings.xml
+++ b/packages/VpnDialogs/res/values-lo/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ການຮ້ອງຂໍການເຊື່ອມຕໍ່"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ຕ້ອງການຕັ້ງຄ່າການເຊື່ອມຕໍ່ VPN ທີ່ອະນຸຍາດໃຫ້ຕິດຕາມທຣາບຟິກເຄືອຂ່າຍໄດ້. ທ່ານຄວນຍິນຍອມສະເພາະໃນກໍລະນີທີ່ທ່ານເຊື່ອຖືແຫລ່ງຂໍ້ມູນເທົ່ານັ້ນ. <br /> <br /> <img src=vpn_icon /> ຈະປາກົດຢູ່ດ້ານເທິງຂອງໜ້າຈໍເມື່ອມີການເປີດໃຊ້ VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ຕ້ອງການຕັ້ງຄ່າການເຊື່ອມຕໍ່ VPN ທີ່ອະນຸຍາດໃຫ້ມັນກວດກາການຈາລະຈອນເຄືອຂ່າຍໄດ້. ໃຫ້ຍອມຮັບສະເພາະໃນກໍລະນີທີ່ທ່ານເຊື່ອຖືແຫຼ່ງທີ່ມາເທົ່ານັ້ນ. <br /> <br /> <img src=vpn_icon /> ຈະປາກົດຢູ່ໜ້າຈໍຂອງທ່ານເມື່ອເປີດໃຊ້ VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"ເຊື່ອມຕໍ່ VPN ແລ້ວ"</string>
<string name="session" msgid="6470628549473641030">"ເຊສຊັນ:"</string>
<string name="duration" msgid="3584782459928719435">"ໄລຍະເວລາ:"</string>
diff --git a/packages/VpnDialogs/res/values-lt/strings.xml b/packages/VpnDialogs/res/values-lt/strings.xml
index 8846310..1f86180d 100644
--- a/packages/VpnDialogs/res/values-lt/strings.xml
+++ b/packages/VpnDialogs/res/values-lt/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ryšio užklausa"</string>
<string name="warning" msgid="809658604548412033">"„<xliff:g id="APP">%s</xliff:g>“ nori nustatyti VPN ryšį, kad galėtų stebėti tinklo srautą. Sutikite, tik jei pasitikite šaltiniu. <br /> <br /> <img src=vpn_icon /> rodoma ekrano viršuje, kai VPN aktyvus."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN prijungtas"</string>
<string name="session" msgid="6470628549473641030">"Sesija"</string>
<string name="duration" msgid="3584782459928719435">"Trukmė:"</string>
diff --git a/packages/VpnDialogs/res/values-lv/strings.xml b/packages/VpnDialogs/res/values-lv/strings.xml
index 07625b6..71da870 100644
--- a/packages/VpnDialogs/res/values-lv/strings.xml
+++ b/packages/VpnDialogs/res/values-lv/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Savienojuma pieprasījums"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vēlas izveidot VPN savienojumu, kas ļaus pārraudzīt tīkla datplūsmu. Piekrītiet tikai tad, ja uzticaties avotam. <br /> <br /> <img src=vpn_icon /> tiek rādīta ekrāna augšdaļā, kad darbojas VPN."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"Ir izveidots savienojums ar VPN"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Ilgums:"</string>
diff --git a/packages/VpnDialogs/res/values-mk/strings.xml b/packages/VpnDialogs/res/values-mk/strings.xml
index b5a64f2..689d028 100644
--- a/packages/VpnDialogs/res/values-mk/strings.xml
+++ b/packages/VpnDialogs/res/values-mk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Барање за поврзување"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> сака да постави поврзување со ВПН коешто му дозволува да го набљудува сообраќајот на мрежата. Прифатете само доколку му верувате на изворот. <br /> <br /> <img src=vpn_icon /> се појавува на врвот на екранот кога ВПН е активна."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> сака да постави поврзување со VPN што ќе дозволи да го набљудува сообраќајот на мрежата. Прифатете само ако му верувате на изворот. <br /> <br /> <img src=vpn_icon /> ќе се појави на екранот кога ќе се активира VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN е поврзана"</string>
<string name="session" msgid="6470628549473641030">"Сесија:"</string>
<string name="duration" msgid="3584782459928719435">"Времетраење:"</string>
@@ -30,7 +31,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Променете ги поставките за VPN"</string>
<string name="configure" msgid="4905518375574791375">"Конфигурирај"</string>
- <string name="disconnect" msgid="971412338304200056">"Исклучи"</string>
+ <string name="disconnect" msgid="971412338304200056">"Прекини врска"</string>
<string name="open_app" msgid="3717639178595958667">"Отвори ја апликацијата"</string>
<string name="dismiss" msgid="6192859333764711227">"Отфрли"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-ml/strings.xml b/packages/VpnDialogs/res/values-ml/strings.xml
index 680d0ef..a298caa 100644
--- a/packages/VpnDialogs/res/values-ml/strings.xml
+++ b/packages/VpnDialogs/res/values-ml/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"കണക്ഷൻ അഭ്യർത്ഥന"</string>
<string name="warning" msgid="809658604548412033">"നെറ്റ്വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കാൻ അനുവദിക്കുന്ന ഒരു VPN കണക്ഷൻ <xliff:g id="APP">%s</xliff:g> സജ്ജീകരിക്കേണ്ടതുണ്ട്. ഉറവിടം പരിചിതമാണെങ്കിൽ മാത്രം അംഗീകരിക്കുക. VPN സജീവമാകുമ്പോൾ <br /> <br /> <img src=vpn_icon /> നിങ്ങളുടെ സ്ക്രീനിന്റെ മുകളിൽ ദൃശ്യമാകുന്നു."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN കണക്റ്റുചെയ്തു"</string>
<string name="session" msgid="6470628549473641030">"സെഷൻ:"</string>
<string name="duration" msgid="3584782459928719435">"സമയദൈര്ഘ്യം:"</string>
diff --git a/packages/VpnDialogs/res/values-mn/strings.xml b/packages/VpnDialogs/res/values-mn/strings.xml
index 9aa104a..1dd4c15 100644
--- a/packages/VpnDialogs/res/values-mn/strings.xml
+++ b/packages/VpnDialogs/res/values-mn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Холболтын хүсэлт"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> нь сүлжээний трафикыг хянах боломж бүхий VPN холболт үүсгэхийг хүсэж байна. Та зөвхөн эх үүсвэрт итгэж байгаа бол зөвшөөрнө үү. <br /> <br /> <img src=vpn_icon /> таны дэлгэц дээр VPN идэвхтэй үед гарч ирнэ."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> нь түүнд сүлжээний ачааллыг хянах боломжийг олгодог VPN холболт тохируулахыг хүсэж байна. Зөвхөн та эх сурвалжид итгэдэг тохиолдолд зөвшөөрнө үү. VPN идэвхтэй үед таны дэлгэц дээр <br /> <br /> <img src=vpn_icon /> харагдана."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN холбогдов"</string>
<string name="session" msgid="6470628549473641030">"Сешн:"</string>
<string name="duration" msgid="3584782459928719435">"Үргэлжлэх хугацаа:"</string>
diff --git a/packages/VpnDialogs/res/values-mr/strings.xml b/packages/VpnDialogs/res/values-mr/strings.xml
index 41d7429..6caccf7 100644
--- a/packages/VpnDialogs/res/values-mr/strings.xml
+++ b/packages/VpnDialogs/res/values-mr/strings.xml
@@ -18,18 +18,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"कनेक्शन विनंती"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> नेटवर्क रहदारीचे परीक्षण करण्यासाठी त्यास अनुमती देणारे VPN कनेक्शन सेट करू इच्छितो. तुम्हाला स्रोत विश्वसनीय वाटत असेल तरच स्वीकार करा. <br /> <br /> <img src=vpn_icon /> VPN सक्रिय असताना आपल्या स्क्रीनच्या शीर्षावर दिसते."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN कनेक्ट केले"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"कालावधी:"</string>
<string name="data_transmitted" msgid="7988167672982199061">"प्रेषित:"</string>
<string name="data_received" msgid="4062776929376067820">"प्राप्त झाले:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> बाइट / <xliff:g id="NUMBER_1">%2$s</xliff:g> पॅकेट"</string>
- <string name="always_on_disconnected_title" msgid="1906740176262776166">"कायम चालू असलेल्या VPN शी कनेक्ट करू शकत नाही"</string>
+ <string name="always_on_disconnected_title" msgid="1906740176262776166">"कायम सुरू असलेल्या VPN शी कनेक्ट करू शकत नाही"</string>
<string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. <xliff:g id="VPN_APP_1">%1$s</xliff:g> शी पुन्हा कनेक्ट होईपर्यंत तुमचा फोन सार्वजनिक नेटवर्क वापरेल."</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. VPN पुन्हा कनेक्ट होईपर्यंत तुमच्याकडे कनेक्शन नसेल."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सेटिंग्ज बदला"</string>
- <string name="configure" msgid="4905518375574791375">"कॉन्फिगर करा"</string>
+ <string name="configure" msgid="4905518375574791375">"कॉंफिगर करा"</string>
<string name="disconnect" msgid="971412338304200056">"डिस्कनेक्ट करा"</string>
<string name="open_app" msgid="3717639178595958667">"अॅप उघडा"</string>
<string name="dismiss" msgid="6192859333764711227">"डिसमिस करा"</string>
diff --git a/packages/VpnDialogs/res/values-ms/strings.xml b/packages/VpnDialogs/res/values-ms/strings.xml
index b489f2e..c9961d2 100644
--- a/packages/VpnDialogs/res/values-ms/strings.xml
+++ b/packages/VpnDialogs/res/values-ms/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Permintaan sambungan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ingin menyediakan sambungan VPN yang membenarkan apl memantau trafik rangkaian. Terima hanya jika anda mempercayai sumber. <br /> <br /> <img src=vpn_icon /> terpapar pada bahagian atas skrin anda apabila VPN aktif."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ingin menyediakan sambungan VPN yang membenarkan apl tersebut memantau trafik rangkaian. Hanya terima jika anda mempercayai sumber tersebut. <br /> <br /> <img src=vpn_icon /> muncul pada skrin anda apabila VPN aktif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN telah disambungkan"</string>
<string name="session" msgid="6470628549473641030">"Sesi:"</string>
<string name="duration" msgid="3584782459928719435">"Tempoh:"</string>
diff --git a/packages/VpnDialogs/res/values-my/strings.xml b/packages/VpnDialogs/res/values-my/strings.xml
index 9d60ff4..bda925f 100644
--- a/packages/VpnDialogs/res/values-my/strings.xml
+++ b/packages/VpnDialogs/res/values-my/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ချိတ်ဆက်ရန် တောင်းဆိုချက်"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> က ကွန်ရက် လုပ်ငန်းကို စောင့်ကြည့်ခွင့် ပြုမည့် VPN ချိတ်ဆက်မှုကို ထူထောင်လိုသည်။ ရင်းမြစ်ကို သင်က ယုံကြည်မှသာ လက်ခံပါ။ <br /> <br /> <img src=vpn_icon /> မှာ VPN အလုပ်လုပ်နေလျှင် သင်၏ မျက်နှာပြင် ထိပ်မှာ ပေါ်လာမည်။"</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPNနှင့်ချိတ်ဆက်ထားသည်"</string>
<string name="session" msgid="6470628549473641030">"သတ်မှတ်ပေးထားသည့်အချိန်:"</string>
<string name="duration" msgid="3584782459928719435">"အချိန်ကာလ-"</string>
@@ -30,7 +32,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ဆက်တင်များ ပြောင်းရန်"</string>
<string name="configure" msgid="4905518375574791375">"ပုံပေါ်စေသည်"</string>
- <string name="disconnect" msgid="971412338304200056">"ချိတ်ဆက်ခြင်းရပ်ရန်"</string>
+ <string name="disconnect" msgid="971412338304200056">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
<string name="open_app" msgid="3717639178595958667">"အက်ပ်ကို ဖွင့်ရန်"</string>
<string name="dismiss" msgid="6192859333764711227">"ပယ်ရန်"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-nb/strings.xml b/packages/VpnDialogs/res/values-nb/strings.xml
index be572d4..eaa2c17 100644
--- a/packages/VpnDialogs/res/values-nb/strings.xml
+++ b/packages/VpnDialogs/res/values-nb/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Tilkoblingsforespørsel"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ønsker å bruke en VPN-tilkobling som tillater at appen overvåker nettverkstrafikken. Du bør bare godta dette hvis du stoler på kilden. <br /> <br /> <img src=vpn_icon /> vises øverst på skjermen din når VPN er aktivert."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN er tilkoblet"</string>
<string name="session" msgid="6470628549473641030">"Økt:"</string>
<string name="duration" msgid="3584782459928719435">"Varighet:"</string>
diff --git a/packages/VpnDialogs/res/values-ne/strings.xml b/packages/VpnDialogs/res/values-ne/strings.xml
index b716c35..a248d6d8 100644
--- a/packages/VpnDialogs/res/values-ne/strings.xml
+++ b/packages/VpnDialogs/res/values-ne/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"जडान अनुरोध"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ले नेटवर्क यातायात अनुगमन गर्न अनुमति दिने VPN जडान स्थापना गर्न चाहन्छ। तपाईँले स्रोत भरोसा छ भने मात्र स्वीकार गर्नुहोस्। <br /> <br /> <img src=vpn_icon /> जब VPN सक्रिय हुन्छ आफ्नो स्क्रिनको माथि देखा पर्छन्।"</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN जोडिएको छ"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"अवधि:"</string>
@@ -30,7 +32,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सम्बन्धी सेटिङहरू परिवर्तन गर्नुहोस्"</string>
<string name="configure" msgid="4905518375574791375">"कन्फिगर गर्नुहोस्"</string>
- <string name="disconnect" msgid="971412338304200056">"विच्छेदन गर्नुहोस्"</string>
- <string name="open_app" msgid="3717639178595958667">"अनुप्रयोग खोल्नुहोस्"</string>
+ <string name="disconnect" msgid="971412338304200056">"डिस्कनेक्ट गर्नुहोस्"</string>
+ <string name="open_app" msgid="3717639178595958667">"एप खोल्नुहोस्"</string>
<string name="dismiss" msgid="6192859333764711227">"खारेज गर्नुहोस्"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-nl/strings.xml b/packages/VpnDialogs/res/values-nl/strings.xml
index 8073b09..33f8a89 100644
--- a/packages/VpnDialogs/res/values-nl/strings.xml
+++ b/packages/VpnDialogs/res/values-nl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindingsverzoek"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding opzetten om netwerkverkeer te controleren. Accepteer het verzoek alleen als je de bron vertrouwt. <br /> <br /> <img src=vpn_icon /> wordt boven aan je scherm weergegeven wanneer VPN actief is."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding instellen waarmee de app het netwerkverkeer kan bijhouden. Accepteer dit alleen als je de bron vertrouwt. <br /> <br /> <img src=vpn_icon /> verschijnt op je scherm als het VPN actief is."</string>
<string name="legacy_title" msgid="192936250066580964">"Verbinding met VPN"</string>
<string name="session" msgid="6470628549473641030">"Sessie:"</string>
<string name="duration" msgid="3584782459928719435">"Duur:"</string>
diff --git a/packages/VpnDialogs/res/values-or/strings.xml b/packages/VpnDialogs/res/values-or/strings.xml
index f1122eb..f37814e 100644
--- a/packages/VpnDialogs/res/values-or/strings.xml
+++ b/packages/VpnDialogs/res/values-or/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ସଂଯୋଗ ଅନୁରୋଧ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ଏକ VPN ସଂଯୋଗ ସେଟ୍ ଅପ୍ କରିବାକୁ ଚାହେଁ, ଯାହା ଏହି ନେଟ୍ୱର୍କର ଟ୍ରାଫିକକୁ ମନିଟର୍ କରିବାକୁ ଅନୁମତି ଦିଏ। ଆପଣ ସୋର୍ସ ଉପରେ ବିଶ୍ୱାସ କରିବା ବଦଳରେ କେବଳ ସ୍ୱୀକାର କରନ୍ତୁ। <br /> <br /> <img src=vpn_icon /> VPN ସକ୍ରିୟ ଥିବାବେଳେ ଏହା ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍ର ଉପରେ ଦେଖାଯାଏ।"</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN ସଂଯୋଗ ହେଲା"</string>
<string name="session" msgid="6470628549473641030">"ସେସନ୍:"</string>
<string name="duration" msgid="3584782459928719435">"ଅବଧି:"</string>
@@ -28,7 +30,7 @@
<string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> ସବୁ ସମୟରେ କନେକ୍ଟ ହୋଇ ରହିବା ପାଇଁ ସେଟଅପ୍ କରାଯାଇଛି। ଆପଣଙ୍କ ଫୋନ୍, <xliff:g id="VPN_APP_1">%1$s</xliff:g> ସହ କନେକ୍ଟ ନହେବା ପର୍ଯ୍ୟନ୍ତ ଏକ ପବ୍ଲିକ୍ ନେଟ୍ୱର୍କ ବ୍ୟବହାର କରିବ।"</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> ସବୁ ସମୟରେ କନେକ୍ଟ ହୋଇରହିବାକୁ ସେଟଅପ୍ କରାଯାଇଛି, କିନ୍ତୁ ଏହା ବର୍ତ୍ତମାନ କନେକ୍ଟ କରିପାରୁ ନାହିଁ। VPN ପୁଣି କନେକ୍ଟ ନହେବା ପର୍ଯ୍ୟନ୍ତ ଆପଣଙ୍କର କୌଣସି କନେକ୍ସନ୍ ରହିବନାହିଁ।"</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
- <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଙ୍ଗ ବଦଳାନ୍ତୁ"</string>
+ <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଂସ୍ ବଦଳାନ୍ତୁ"</string>
<string name="configure" msgid="4905518375574791375">"କନଫିଗର୍ କରନ୍ତୁ"</string>
<string name="disconnect" msgid="971412338304200056">"ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
<string name="open_app" msgid="3717639178595958667">"ଆପ୍ ଖୋଲନ୍ତୁ"</string>
diff --git a/packages/VpnDialogs/res/values-pa/strings.xml b/packages/VpnDialogs/res/values-pa/strings.xml
index 1815f4f..d2eba0f 100644
--- a/packages/VpnDialogs/res/values-pa/strings.xml
+++ b/packages/VpnDialogs/res/values-pa/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ਕਨੈਕਸ਼ਨ ਬੇਨਤੀ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ਇੱਕ VPN ਕਨੈਕਸ਼ਨ ਸੈਟ ਅਪ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ ਜੋ ਇਸਨੂੰ ਨੈੱਟਵਰਕ ਟ੍ਰੈਫਿਕ ਦਾ ਨਿਰੀਖਣ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਕੇਵਲ ਤਾਂ ਹੀ ਸਵੀਕਾਰ ਕਰੋ ਜੇਕਰ ਤੁਸੀਂ ਸਰੋਤ ਤੇ ਭਰੋਸਾ ਕਰਦੇ ਹੋ। <br /> <br /> <img src=vpn_icon /> ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਦੇ ਟੌਪ ਤੇ ਪ੍ਰਗਟ ਹੁੰਦਾ ਹੈ ਜਦੋਂ VPN ਸਕਿਰਿਆ ਹੁੰਦਾ ਹੈ।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ਕਿਸੇ ਅਜਿਹੇ VPN ਕਨੈਕਸ਼ਨ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ ਜੋ ਇਸਨੂੰ ਨੈੱਟਵਰਕ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ। ਸਿਰਫ਼ ਉਦੋਂ ਹੀ ਸਵੀਕਾਰ ਕਰੋ ਜੇ ਤੁਹਾਨੂੰ ਸਰੋਤ \'ਤੇ ਭਰੋਸਾ ਹੈ। VPN ਦੇ ਕਿਰਿਆਸ਼ੀਲ ਹੋਣ \'ਤੇ <br /> <br /> <img src=vpn_icon /> ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਸਦਾ ਹੈ।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
<string name="session" msgid="6470628549473641030">"ਸੈਸ਼ਨ:"</string>
<string name="duration" msgid="3584782459928719435">"ਮਿਆਦ:"</string>
diff --git a/packages/VpnDialogs/res/values-pl/strings.xml b/packages/VpnDialogs/res/values-pl/strings.xml
index d5201d7..bbcbaaf 100644
--- a/packages/VpnDialogs/res/values-pl/strings.xml
+++ b/packages/VpnDialogs/res/values-pl/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Żądanie połączenia"</string>
<string name="warning" msgid="809658604548412033">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu sieciowego. Zaakceptuj, tylko jeśli masz zaufanie do źródła. <br /> <br />Gdy sieć VPN jest aktywna, u góry ekranu pojawia się <img src=vpn_icon />."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"Połączono z VPN"</string>
<string name="session" msgid="6470628549473641030">"Sesja:"</string>
<string name="duration" msgid="3584782459928719435">"Czas trwania:"</string>
diff --git a/packages/VpnDialogs/res/values-pt-rBR/strings.xml b/packages/VpnDialogs/res/values-pt-rBR/strings.xml
index 75c1406..0d6dd0b 100644
--- a/packages/VpnDialogs/res/values-pt-rBR/strings.xml
+++ b/packages/VpnDialogs/res/values-pt-rBR/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitação de conexão"</string>
<string name="warning" msgid="809658604548412033">"O <xliff:g id="APP">%s</xliff:g> quer configurar uma conexão VPN que permite monitorar o tráfego da rede. Aceite somente se confiar na origem. <br /> <br /> <img src=vpn_icon /> é exibido na parte superior da tela quando a rede VPN estiver ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"O app <xliff:g id="APP">%s</xliff:g> quer definir uma conexão VPN para monitorar o tráfego da rede. Aceite apenas se você confiar na fonte. O ícone <br /> <br /> <img src=vpn_icon /> aparecerá na tela quando a VPN estiver ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"O VPN está conectado"</string>
<string name="session" msgid="6470628549473641030">"Sessão:"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
diff --git a/packages/VpnDialogs/res/values-pt-rPT/strings.xml b/packages/VpnDialogs/res/values-pt-rPT/strings.xml
index 01beddb..a310104 100644
--- a/packages/VpnDialogs/res/values-pt-rPT/strings.xml
+++ b/packages/VpnDialogs/res/values-pt-rPT/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Pedido de ligação"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> pretende configurar uma ligação VPN que lhe permita monitorizar o tráfego de rede. Aceite apenas se confiar na fonte. <br /> <br /> <img src=vpn_icon /> aparece na parte superior do seu ecrã quando a VPN está ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A app <xliff:g id="APP">%s</xliff:g> pretende configurar uma ligação VPN que lhe permita monitorizar o tráfego de rede. Aceite apenas se confiar na origem. <br /> <br /> <img src=vpn_icon /> aparece no ecrã quando a VPN está ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN está ligada"</string>
<string name="session" msgid="6470628549473641030">"Sessão"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
@@ -25,12 +26,12 @@
<string name="data_received" msgid="4062776929376067820">"Recebidos:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> bytes / <xliff:g id="NUMBER_1">%2$s</xliff:g> pacotes"</string>
<string name="always_on_disconnected_title" msgid="1906740176262776166">"Não é possível estabelecer ligação à VPN sempre ativada"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"A aplicação <xliff:g id="VPN_APP_0">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. O seu telemóvel irá utilizar uma rede pública até conseguir restabelecer ligação à aplicação <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
- <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"A aplicação <xliff:g id="VPN_APP">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. Não terá ligação até que a VPN a consiga restabelecer."</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"A app <xliff:g id="VPN_APP_0">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. O seu telemóvel irá utilizar uma rede pública até conseguir restabelecer ligação à app <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
+ <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"A app <xliff:g id="VPN_APP">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. Não terá ligação até que a VPN a consiga restabelecer."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Alterar as definições da VPN"</string>
<string name="configure" msgid="4905518375574791375">"Configurar"</string>
<string name="disconnect" msgid="971412338304200056">"Desligar"</string>
- <string name="open_app" msgid="3717639178595958667">"Abrir aplicação"</string>
+ <string name="open_app" msgid="3717639178595958667">"Abrir app"</string>
<string name="dismiss" msgid="6192859333764711227">"Ignorar"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-pt/strings.xml b/packages/VpnDialogs/res/values-pt/strings.xml
index 75c1406..0d6dd0b 100644
--- a/packages/VpnDialogs/res/values-pt/strings.xml
+++ b/packages/VpnDialogs/res/values-pt/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitação de conexão"</string>
<string name="warning" msgid="809658604548412033">"O <xliff:g id="APP">%s</xliff:g> quer configurar uma conexão VPN que permite monitorar o tráfego da rede. Aceite somente se confiar na origem. <br /> <br /> <img src=vpn_icon /> é exibido na parte superior da tela quando a rede VPN estiver ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"O app <xliff:g id="APP">%s</xliff:g> quer definir uma conexão VPN para monitorar o tráfego da rede. Aceite apenas se você confiar na fonte. O ícone <br /> <br /> <img src=vpn_icon /> aparecerá na tela quando a VPN estiver ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"O VPN está conectado"</string>
<string name="session" msgid="6470628549473641030">"Sessão:"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
diff --git a/packages/VpnDialogs/res/values-ro/strings.xml b/packages/VpnDialogs/res/values-ro/strings.xml
index 4e60df2..5bda87e 100644
--- a/packages/VpnDialogs/res/values-ro/strings.xml
+++ b/packages/VpnDialogs/res/values-ro/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitare de conexiune"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> dorește să configureze o conexiune VPN care să îi permită să monitorizeze traficul în rețea. Acceptați numai dacă aveți încredere în sursă. Atunci când conexiunea VPN este activă, <br /> <br /> <img src=vpn_icon /> se afișează în partea de sus a ecranului."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> solicită permisiunea de a configura o conexiune VPN care să îi permită să monitorizeze traficul de rețea. Acceptați numai dacă aveți încredere în sursă. <br /> <br /> <img src=vpn_icon /> va apărea pe ecran atunci când conexiunea VPN este activă."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN este conectat"</string>
<string name="session" msgid="6470628549473641030">"Sesiune:"</string>
<string name="duration" msgid="3584782459928719435">"Durată:"</string>
diff --git a/packages/VpnDialogs/res/values-ru/strings.xml b/packages/VpnDialogs/res/values-ru/strings.xml
index f8fcfb8..ce099562 100644
--- a/packages/VpnDialogs/res/values-ru/strings.xml
+++ b/packages/VpnDialogs/res/values-ru/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запрос на подключение"</string>
<string name="warning" msgid="809658604548412033">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. <br /><br />Когда подключение к сети VPN активно, в верхней части экрана появляется значок <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. Когда подключение к сети VPN активно, на экране появляется значок <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-подключение установлено"</string>
<string name="session" msgid="6470628549473641030">"Сеанс:"</string>
<string name="duration" msgid="3584782459928719435">"Продолжительность:"</string>
diff --git a/packages/VpnDialogs/res/values-si/strings.xml b/packages/VpnDialogs/res/values-si/strings.xml
index bb97a5d..a836bae 100644
--- a/packages/VpnDialogs/res/values-si/strings.xml
+++ b/packages/VpnDialogs/res/values-si/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"සම්බන්ධතා ඉල්ලීම"</string>
<string name="warning" msgid="809658604548412033">"ජාල තදබදය නිරීක්ෂණය කිරීමට ඉඩ දෙන VPN සම්බන්ධතාවක් සැකසීමට <xliff:g id="APP">%s</xliff:g> අවශ්යය වේ. ප්රභවය ඔබ විශ්වාස කරන්නේ නම් පමණක් පිළිගන්න. VPN සක්රිය විට <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"ජාල තදබදය නිරීක්ෂණය කිරීමට ඉඩ දෙන VPN සම්බන්ධතාවක් සැකසීමට <xliff:g id="APP">%s</xliff:g> හට අවශ්ය වේ. ඔබ මූලාශ්රය විශ්වාස කරන්නේ නම් පමණක් පිළිගන්න. <br /> <br /> <img src=vpn_icon /> VPN සක්රිය විට ඔබගේ තිරයෙහි දිස් වේ."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN සම්බන්ධිතයි"</string>
<string name="session" msgid="6470628549473641030">"සැසිය:"</string>
<string name="duration" msgid="3584782459928719435">"කාල සීමාව:"</string>
diff --git a/packages/VpnDialogs/res/values-sk/strings.xml b/packages/VpnDialogs/res/values-sk/strings.xml
index a08117a..766c139 100644
--- a/packages/VpnDialogs/res/values-sk/strings.xml
+++ b/packages/VpnDialogs/res/values-sk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Žiadosť o pripojenie"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> žiada o nastavenie pripojenia VPN, pomocou ktorého bude môcť sledovať sieťové prenosy. Povoľte iba v prípade, že zdroju dôverujete. <br /> <br /> <img src=vpn_icon /> sa zobrazuje v hornej časti obrazovky, keď je pripojenie VPN aktívne."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikácia <xliff:g id="APP">%s</xliff:g> chce nastaviť pripojenie k sieti VPN, ktoré jej umožňuje sledovať sieťovú premávku. Povoľte to iba v prípade, ak zdroju dôverujete. <br /> <br /> Keď je sieť VPN aktívna, na obrazovke sa zobrazí ikona <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"Sieť VPN je pripojená"</string>
<string name="session" msgid="6470628549473641030">"Relácia"</string>
<string name="duration" msgid="3584782459928719435">"Trvanie:"</string>
diff --git a/packages/VpnDialogs/res/values-sl/strings.xml b/packages/VpnDialogs/res/values-sl/strings.xml
index d5014fa34..0b3fd93 100644
--- a/packages/VpnDialogs/res/values-sl/strings.xml
+++ b/packages/VpnDialogs/res/values-sl/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahteva za povezavo"</string>
<string name="warning" msgid="809658604548412033">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi nastaviti povezavo VPN, ki omogoča nadzor omrežnega prometa. To sprejmite samo, če zaupate viru. Ko je povezava VPN aktivna, se na vrhu zaslona prikaže ikona <br /> <br /> <img src=vpn_icon />."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"Povezava z navideznim zasebnim omrežjem je vzpostavljena"</string>
<string name="session" msgid="6470628549473641030">"Seja:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-sq/strings.xml b/packages/VpnDialogs/res/values-sq/strings.xml
index 4a96e7b..f2cccbc 100644
--- a/packages/VpnDialogs/res/values-sq/strings.xml
+++ b/packages/VpnDialogs/res/values-sq/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kërkesë për lidhje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> kërkon të vendosë një lidhje VPN-je që e lejon të monitorojë trafikun e rrjetit. Prano vetëm nëse i beson burimit. <br /> <br /> <img src=vpn_icon /> shfaqet në krye të ekranit kur VPN-ja është aktive."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN-ja është e lidhur"</string>
<string name="session" msgid="6470628549473641030">"Sesioni:"</string>
<string name="duration" msgid="3584782459928719435">"Kohëzgjatja:"</string>
diff --git a/packages/VpnDialogs/res/values-sr/strings.xml b/packages/VpnDialogs/res/values-sr/strings.xml
index 8ce8060..01bd4df 100644
--- a/packages/VpnDialogs/res/values-sr/strings.xml
+++ b/packages/VpnDialogs/res/values-sr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Захтев за повезивање"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која омогућава праћење саобраћаја на мрежи. Прихватите само ако верујете извору. <br /> <br /> <img src=vpn_icon /> се приказује у врху екрана када је VPN активан."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Апликација <xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која јој омогућава да прати мрежни саобраћај. Прихватите ово само ако имате поверења у извор. <br /> <br /> <img src=vpn_icon /> се приказује на екрану када је VPN активан."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN је повезан"</string>
<string name="session" msgid="6470628549473641030">"Сесија:"</string>
<string name="duration" msgid="3584782459928719435">"Трајање:"</string>
diff --git a/packages/VpnDialogs/res/values-sv/strings.xml b/packages/VpnDialogs/res/values-sv/strings.xml
index 16b6a31..6c608a5 100644
--- a/packages/VpnDialogs/res/values-sv/strings.xml
+++ b/packages/VpnDialogs/res/values-sv/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Anslutningsförfrågan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vill starta en VPN-anslutning som tillåter att appen övervakar nätverkstrafiken. Godkänn endast detta om du litar på källan. <br /> <br /> <img src=vpn_icon /> visas längst upp på skärmen när VPN-anslutningen är aktiv."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN är anslutet"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Längd:"</string>
diff --git a/packages/VpnDialogs/res/values-sw/strings.xml b/packages/VpnDialogs/res/values-sw/strings.xml
index ea26884..c4f4662 100644
--- a/packages/VpnDialogs/res/values-sw/strings.xml
+++ b/packages/VpnDialogs/res/values-sw/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ombi la muunganisho"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> inataka kusanidi muunganisho wa VPN utakaoiruhusu kufuatilia shughuli kwenye mtandao. Kubali ikiwa tu unakiamini chanzo. <br /> <br /> <img src=vpn_icon /> huonekana sehemu ya juu ya skrini yako VPN inapofanya kazi."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> inagependa kuweka mipangilio ya muunganisho wa VPN inayoiruhusu kufuatilia trafiki ya mtandao. Kubali tu iwapo unaamini chanzo. <br /> <br /> <img src=vpn_icon /> huonekana kwenye skrini yako VPN inapotumika."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN imeunganishwa"</string>
<string name="session" msgid="6470628549473641030">"Kipindi:"</string>
<string name="duration" msgid="3584782459928719435">"Muda:"</string>
diff --git a/packages/VpnDialogs/res/values-ta/strings.xml b/packages/VpnDialogs/res/values-ta/strings.xml
index 3b4cc57..73eae20 100644
--- a/packages/VpnDialogs/res/values-ta/strings.xml
+++ b/packages/VpnDialogs/res/values-ta/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"இணைப்புக் கோரிக்கை"</string>
<string name="warning" msgid="809658604548412033">"நெட்வொர்க் டிராஃபிக்கைக் கண்காணிக்க வசதியாக VPN இணைப்பை அமைக்க <xliff:g id="APP">%s</xliff:g> கோருகிறது. நம்பகமான மூலத்தை மட்டுமே ஏற்கவும். <br /> <br /> VPN இயக்கத்தில் உள்ளபோது திரையின் மேல் பகுதியில் <img src=vpn_icon /> தோன்றும்."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN இணைக்கப்பட்டது"</string>
<string name="session" msgid="6470628549473641030">"அமர்வு:"</string>
<string name="duration" msgid="3584782459928719435">"காலஅளவு:"</string>
diff --git a/packages/VpnDialogs/res/values-te/strings.xml b/packages/VpnDialogs/res/values-te/strings.xml
index 864c926..f6d19ff 100644
--- a/packages/VpnDialogs/res/values-te/strings.xml
+++ b/packages/VpnDialogs/res/values-te/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"కనెక్షన్ అభ్యర్థన"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> నెట్వర్క్ ట్రాఫిక్ని పర్యవేక్షించగలగడానికి VPN కనెక్షన్ను సెటప్ చేయాలనుకుంటోంది. మీరు మూలాన్ని విశ్వసిస్తే మాత్రమే ఆమోదించండి. VPN సక్రియంగా ఉన్నప్పుడు మీ స్క్రీన్ ఎగువన <br /> <br /> <img src=vpn_icon /> కనిపిస్తుంది."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN కనెక్ట్ చేయబడింది"</string>
<string name="session" msgid="6470628549473641030">"సెషన్:"</string>
<string name="duration" msgid="3584782459928719435">"వ్యవధి:"</string>
diff --git a/packages/VpnDialogs/res/values-th/strings.xml b/packages/VpnDialogs/res/values-th/strings.xml
index 333ff5f..2e174cd 100644
--- a/packages/VpnDialogs/res/values-th/strings.xml
+++ b/packages/VpnDialogs/res/values-th/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ขอการเชื่อมต่อ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ต้องการสร้างการเชื่อมต่อ VPN เพื่อให้แอปสามารถตรวจสอบการเข้าใช้งานเครือข่าย โปรดยอมรับหากคุณเชื่อถือแหล่งที่มานี้เท่านั้น <br /> <br /> <img src=vpn_icon /> จะปรากฏที่ด้านบนหน้าจอเมื่อมีการใช้งาน VPN อยู่"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ต้องการตั้งค่าการเชื่อมต่อ VPN เพื่อให้ตรวจสอบการจราจรของข้อมูลในเครือข่ายได้ ยอมรับต่อเมื่อคุณไว้วางใจแหล่งที่มานี้เท่านั้น <br /> <br /> <img src=vpn_icon /> จะปรากฏบนหน้าจอเมื่อใช้งาน VPN อยู่"</string>
<string name="legacy_title" msgid="192936250066580964">"เชื่อมต่อ VPN แล้ว"</string>
<string name="session" msgid="6470628549473641030">"เซสชัน"</string>
<string name="duration" msgid="3584782459928719435">"ระยะเวลา:"</string>
diff --git a/packages/VpnDialogs/res/values-tl/strings.xml b/packages/VpnDialogs/res/values-tl/strings.xml
index 9c01c32..ea69fba 100644
--- a/packages/VpnDialogs/res/values-tl/strings.xml
+++ b/packages/VpnDialogs/res/values-tl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kahilingan sa koneksyon"</string>
<string name="warning" msgid="809658604548412033">"Gusto ng <xliff:g id="APP">%s</xliff:g> na mag-set up ng koneksyon sa VPN na nagbibigay-daan ditong masubaybayan ang trapiko ng network. Tanggapin lang kung pinagkakatiwalaan mo ang pinagmulan. Lalabas ang <br /> <br /> <img src=vpn_icon /> sa itaas ng iyong screen kapag aktibo ang VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Gusto ng <xliff:g id="APP">%s</xliff:g> na mag-set up ng koneksyon sa VPN na nagbibigay-daan ditong masubaybayan ang trapiko sa network. Tanggapin lang kung pinagkakatiwalaan mo ang pinagmulan. Lalabas ang <br /> <br /> <img src=vpn_icon /> sa iyong screen kapag aktibo ang VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Nakakonekta ang VPN"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Tagal:"</string>
diff --git a/packages/VpnDialogs/res/values-tr/strings.xml b/packages/VpnDialogs/res/values-tr/strings.xml
index 8665a47..7ffa4bc1 100644
--- a/packages/VpnDialogs/res/values-tr/strings.xml
+++ b/packages/VpnDialogs/res/values-tr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Bağlantı isteği"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ağ trafiğini izlemesine olanak veren bir VPN bağlantısı oluşturmak istiyor. Sadece, ilgili kaynağa güveniyorsanız kabul edin. <br /> <br /> VPN aktif olduğunda ekranınızın üst tarafında <img src=vpn_icon /> simgesi görünür."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>, ağ trafiğini izlemesine izin veren bir VPN bağlantısı oluşturmak istiyor. Yalnızca kaynağa güveniyorsanız kabul edin. VPN etkin olduğunda ekranınızda <br /> <br /> <img src=vpn_icon /> görünür."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN bağlı"</string>
<string name="session" msgid="6470628549473641030">"Oturum:"</string>
<string name="duration" msgid="3584782459928719435">"Süre:"</string>
diff --git a/packages/VpnDialogs/res/values-uk/strings.xml b/packages/VpnDialogs/res/values-uk/strings.xml
index 8f91abf..6411d7c 100644
--- a/packages/VpnDialogs/res/values-uk/strings.xml
+++ b/packages/VpnDialogs/res/values-uk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запит на під’єднання"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> хоче під’єднатися до мережі VPN, щоб контролювати мережевий трафік. Дозволяйте, якщо довіряєте джерелу. Коли мережа VPN активна, угорі екрана відображається значок <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> хоче під’єднатися до мережі VPN, щоб контролювати мережевий трафік. Надавайте дозвіл, лише якщо довіряєте джерелу. Коли мережа VPN активна, на екрані з’являється значок <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"Мережу VPN під’єднано"</string>
<string name="session" msgid="6470628549473641030">"Сеанс:"</string>
<string name="duration" msgid="3584782459928719435">"Тривалість:"</string>
diff --git a/packages/VpnDialogs/res/values-ur/strings.xml b/packages/VpnDialogs/res/values-ur/strings.xml
index db0c297..10dc56c 100644
--- a/packages/VpnDialogs/res/values-ur/strings.xml
+++ b/packages/VpnDialogs/res/values-ur/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"کنکشن کی درخواست"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ایک ایسا VPN کنکشن ترتیب دینا چاہتی ہے جو اسے نیٹ ورک ٹریفک کو مانیٹر کرنے کی اجازت دیتا ہے۔ اگر آپ کو ماخذ پر بھروسہ ہے تبھی قبول کریں۔ <br /> <br /> <img src=vpn_icon /> آپ کی اسکرین کے اوپر اس وقت ظاہر ہوتا ہے جب VPN فعال ہوتا ہے۔"</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN مربوط ہے"</string>
<string name="session" msgid="6470628549473641030">"سیشن:"</string>
<string name="duration" msgid="3584782459928719435">"دورانیہ:"</string>
diff --git a/packages/VpnDialogs/res/values-uz/strings.xml b/packages/VpnDialogs/res/values-uz/strings.xml
index 5a348a0..a3256e7 100644
--- a/packages/VpnDialogs/res/values-uz/strings.xml
+++ b/packages/VpnDialogs/res/values-uz/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ulanish uchun so‘rov"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ilovasi trafikni kuzatish uchun VPN tarmog‘iga ulanmoqchi. Agar ilovaga ishonsangiz, so‘rovga rozi bo‘ling.<br /> <br />VPN faol bo‘lsa, ekranning yuqori qismida <img src=vpn_icon /> belgisi paydo bo‘ladi."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ilovasi tarmoqdagi trafikni kuzatish uchun VPN aloqasini sozlamoqchi. Agar unga ishonsangiz, ruxsat bering. VPN aloqa faolligida ekranda <br /> <br /> <img src=vpn_icon /> chiqadi."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ulangan"</string>
<string name="session" msgid="6470628549473641030">"Seans:"</string>
<string name="duration" msgid="3584782459928719435">"Davomiyligi:"</string>
diff --git a/packages/VpnDialogs/res/values-vi/strings.xml b/packages/VpnDialogs/res/values-vi/strings.xml
index 097c9ae..ed338aa 100644
--- a/packages/VpnDialogs/res/values-vi/strings.xml
+++ b/packages/VpnDialogs/res/values-vi/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Yêu cầu kết nối"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Chỉ chấp nhận nếu bạn tin tưởng nguồn. <br /> <br /> Biểu tượng <img src=vpn_icon /> xuất hiện ở đầu màn hình của bạn khi VPN đang hoạt động."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN được kết nối"</string>
<string name="session" msgid="6470628549473641030">"Phiên"</string>
<string name="duration" msgid="3584782459928719435">"Thời lượng:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rCN/strings.xml b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
index 7e528bd..6992d2e 100644
--- a/packages/VpnDialogs/res/values-zh-rCN/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"网络连接请求"</string>
<string name="warning" msgid="809658604548412033">"“<xliff:g id="APP">%s</xliff:g>”想要设置一个 VPN 连接,以便监控网络流量。除非您信任该来源,否则请勿接受此请求。<br /> <br />启用 VPN 后,屏幕顶部会出现一个 <img src=vpn_icon /> 图标。"</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"已连接VPN"</string>
<string name="session" msgid="6470628549473641030">"会话:"</string>
<string name="duration" msgid="3584782459928719435">"时长:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rHK/strings.xml b/packages/VpnDialogs/res/values-zh-rHK/strings.xml
index 49605b0..e4e6432 100644
--- a/packages/VpnDialogs/res/values-zh-rHK/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rHK/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"連線要求"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> 要求設定 VPN 連線以監控網絡流量。除非您信任要求來源,否則請勿隨意接受要求。<br /> <br />VPN 啟用時,畫面頂端會顯示 <img src=vpn_icon />。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"「<xliff:g id="APP">%s</xliff:g>」要求設定 VPN 連線以監控網絡流量。除非您信任要求來源,否則請勿隨意接受要求。VPN 啟用時,畫面會顯示 <br /> <br /> <img src=vpn_icon />。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN 已連線"</string>
<string name="session" msgid="6470628549473641030">"時段:"</string>
<string name="duration" msgid="3584782459928719435">"持續時間︰"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rTW/strings.xml b/packages/VpnDialogs/res/values-zh-rTW/strings.xml
index edd8e61..e704e03 100644
--- a/packages/VpnDialogs/res/values-zh-rTW/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rTW/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"連線要求"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> 要求設定 VPN 連線,允許此要求即開放該來源監控網路流量。除非你信任該來源,否則請勿任意接受要求。<br /> <br />VPN 啟用時,畫面頂端會顯示 <img src=vpn_icon />。"</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"VPN 已連線"</string>
<string name="session" msgid="6470628549473641030">"工作階段:"</string>
<string name="duration" msgid="3584782459928719435">"持續時間:"</string>
diff --git a/packages/VpnDialogs/res/values-zu/strings.xml b/packages/VpnDialogs/res/values-zu/strings.xml
index 4ab1225..ddfed40 100644
--- a/packages/VpnDialogs/res/values-zu/strings.xml
+++ b/packages/VpnDialogs/res/values-zu/strings.xml
@@ -18,6 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Isicelo soxhumo"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ifuna ukusetha uxhumo lwe-VPN eyivumela ukwengamela ithrafikhi yenethiwekhi. Yamukela kuphela uma wethemba umthombo. <br /> <br /> <img src=vpn_icon /> ibonakala phezu kwesikrini sakho uma i-VPN isebenza."</string>
+ <!-- no translation found for warning (5188957997628124947) -->
+ <skip />
<string name="legacy_title" msgid="192936250066580964">"I-VPN ixhunyiwe"</string>
<string name="session" msgid="6470628549473641030">"Iseshini:"</string>
<string name="duration" msgid="3584782459928719435">"Ubude besikhathi:"</string>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
new file mode 100644
index 0000000..8c47bcc
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Versteek (vermy programme in uitsnede-area)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
new file mode 100644
index 0000000..0f1edf6
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"ደብቅ (በተቆራረጠ ክልል ውስጥ መተግበሪያዎችን ያስወግዱ)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..c21fcda
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Sakrij (izbegavaj aplikacije u izrezanoj oblasti)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
new file mode 100644
index 0000000..8b81d6a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Скриване (избягване на приложенията в областта на прореза на екрана)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
new file mode 100644
index 0000000..eb2b8d2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Sakrij (izbjegavaj aplikacije u izrezanoj oblasti)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
new file mode 100644
index 0000000..67ed6aff
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Skrýt (nezobrazovat aplikace v oblasti výřezu)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
new file mode 100644
index 0000000..dcf70bf
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Skjul (undgå apps i cutout-område)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
new file mode 100644
index 0000000..86e3732
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Ausblenden (Apps im Bereich der Display-Aussparung vermeiden)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
new file mode 100644
index 0000000..9806966
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Απόκρυψη (αποφυγή εφαρμογών στην περιοχή εγκοπής)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..a7700b9
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Hide (avoid apps in cutout region)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..a7700b9
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Hide (avoid apps in cutout region)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..a7700b9
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Hide (avoid apps in cutout region)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..a7700b9
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Hide (avoid apps in cutout region)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..e9b76fb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Hide (avoid apps in cutout region)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..ee5f8ea
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Ocultar (evitar apps en la región excluida)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
new file mode 100644
index 0000000..c244e49
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Ocultar (evitar aplicaciones en la zona recortada)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
new file mode 100644
index 0000000..f1a6fda
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Ezkutatu (saihestu aplikazioak agertzea pantailaren mozketa-eremuan)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
new file mode 100644
index 0000000..5e626ee
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Piilota (vältä lovialueella olevia sovelluksia)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..5c9194e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Masquer (éviter les applications dans la forme découpée)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
new file mode 100644
index 0000000..a05a5fd
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Ocultar (non mostrar as aplicacións que aparezan na zona recortada)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
new file mode 100644
index 0000000..a2c1feb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Sakrij (izbjegavaj aplikacije u području ureza)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
new file mode 100644
index 0000000..d40d73b
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Sembunyikan (hindari aplikasi di wilayah cutout)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
new file mode 100644
index 0000000..90b9810
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Nascondi (evita le app nell\'area di ritaglio)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
new file mode 100644
index 0000000..69b9f24
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"非表示(カットアウト領域にアプリを表示しない)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
new file mode 100644
index 0000000..1ee2fae
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"დამალვა (აპების არდაშვება ჭრილის უბანში)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
new file mode 100644
index 0000000..ea0a9d0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"លាក់ (ជៀសវាងបង្ហាញកម្មវិធីនៅផ្នែកឆក)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
new file mode 100644
index 0000000..97856d3
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"숨기기(컷아웃 영역에 앱을 표시하지 않음)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
new file mode 100644
index 0000000..29ec224
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"ເຊື່ອງ (ຫຼີກເວັ້ນແອັບໃນພື້ນທີ່ຕັດອອກ)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
new file mode 100644
index 0000000..67b45d3
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Сокриј (избегнувај апликации во отсечен регион)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
new file mode 100644
index 0000000..9bda419
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Нуух (тусгаарласан бүс дэх аппуудаас зайлсхийнэ)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
new file mode 100644
index 0000000..5864ff8
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Sembunyikan (elakkan apl dalam kawasan potongan)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
new file mode 100644
index 0000000..0e51514
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Verbergen (apps in cutout-regio vermijden)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
new file mode 100644
index 0000000..803a69d
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"ਲੁਕਾਓ (ਕੱਟਆਊਟ ਖੇਤਰ ਵਿਚਲੀਆਂ ਐਪਾਂ ਨੂੰ ਨਾ ਛੇੜੋ)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..b5364e9
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Ocultar (evitar apps na região recortada)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..e9467a2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Ocultar (evitar apps na área de recorte)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
new file mode 100644
index 0000000..b5364e9
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Ocultar (evitar apps na região recortada)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
new file mode 100644
index 0000000..9227ceb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Ascundeți (evitați aplicațiile din regiunea decupată)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
new file mode 100644
index 0000000..8335c77
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Скрыть (не показывать приложения в вырезе на экране)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
new file mode 100644
index 0000000..d21b02c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"සඟවන්න (කටවුට් කලාපයෙහි යෙදුම් වළක්වන්න)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
new file mode 100644
index 0000000..dfd01af
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Skryť (nezobrazovať aplikácie v oblasti výrezu)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
new file mode 100644
index 0000000..c835b35
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Сакриј (избегавај апликације у изрезаној области)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
new file mode 100644
index 0000000..57ef684
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Ficha (epuka programu katika eneo lenye pengo)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
new file mode 100644
index 0000000..09d597d
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"ซ่อน (หลีกเลี่ยงการแสดงแอปในภูมิภาคที่ถูกตัดออก)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
new file mode 100644
index 0000000..6b1c372
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Itago (iwasan ang mga app sa rehiyon ng cutout)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
new file mode 100644
index 0000000..991a840
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Gizle (kesim bölgesindeki uygulamalardan kaçının)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
new file mode 100644
index 0000000..7d6c068
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Сховати (не показувати додатки з вирізаних регіонів)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
new file mode 100644
index 0000000..13a56ac
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"Berkitish (qirqilgan hudud ilovalariga diqqat qiling)"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..7f37f3b
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1946296620328354129">"隱藏 (避免將應用程式置於凹口區域)"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
new file mode 100644
index 0000000..b021da7
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Versteek"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
new file mode 100644
index 0000000..0ca6135
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ደብቅ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..082e586
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
new file mode 100644
index 0000000..7c3170c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Скриване"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
new file mode 100644
index 0000000..082e586
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
new file mode 100644
index 0000000..068bb94
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skrýt"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
new file mode 100644
index 0000000..6ecb767
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skjul"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
new file mode 100644
index 0000000..44278e8
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ausblenden"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
new file mode 100644
index 0000000..96b1b86
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Απόκρυψη"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..1ab9b2d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..1ab9b2d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..1ab9b2d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..1ab9b2d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..a20e594
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
new file mode 100644
index 0000000..f33bb50
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ezkutatu"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
new file mode 100644
index 0000000..c42e52d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Piilota"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..31fa567
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Masquer"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
new file mode 100644
index 0000000..082e586
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
new file mode 100644
index 0000000..6c017b3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sembunyikan"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
new file mode 100644
index 0000000..07444aa
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Nascondi"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
new file mode 100644
index 0000000..3bf17fb
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"非表示"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
new file mode 100644
index 0000000..1600528
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"დამალვა"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
new file mode 100644
index 0000000..624b81f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"លាក់"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
new file mode 100644
index 0000000..efb6568
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"숨기기"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
new file mode 100644
index 0000000..8850dfb
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ເຊື່ອງ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
new file mode 100644
index 0000000..505c205
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сокриј"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
new file mode 100644
index 0000000..7e2719b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Нуух"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
new file mode 100644
index 0000000..6c017b3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sembunyikan"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
new file mode 100644
index 0000000..00900f8
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Verbergen"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
new file mode 100644
index 0000000..9f37e0b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ਲੁਕਾਓ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
new file mode 100644
index 0000000..e6281fd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ascundeți"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
new file mode 100644
index 0000000..9018396
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Скрыть"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
new file mode 100644
index 0000000..b06a52c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"සඟවන්න"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
new file mode 100644
index 0000000..965d95b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skryť"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
new file mode 100644
index 0000000..26afbf9
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сакриј"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
new file mode 100644
index 0000000..58e35e2
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ficha"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
new file mode 100644
index 0000000..1c8bd9d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ซ่อน"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
new file mode 100644
index 0000000..cc45f63
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Itago"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
new file mode 100644
index 0000000..b20f1f7
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Gizle"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
new file mode 100644
index 0000000..938b0e2
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сховати"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
new file mode 100644
index 0000000..5d22045
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Berkitish"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..abb8e81
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"隱藏"</string>
+</resources>
diff --git a/packages/services/CameraExtensionsProxy/AndroidManifest.xml b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
index 7416cba..e5f460e5 100644
--- a/packages/services/CameraExtensionsProxy/AndroidManifest.xml
+++ b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
@@ -2,8 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.camera">
- <uses-permission android:name="android.permission.CAMERA" />
-
<application
android:label="@string/app_name"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
index 0f05019..d44a417 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
@@ -25,19 +25,36 @@
import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.extension.CameraOutputConfig;
+import android.hardware.camera2.extension.CameraSessionConfig;
import android.hardware.camera2.extension.CaptureBundle;
+import android.hardware.camera2.extension.CaptureFailure;
import android.hardware.camera2.extension.CaptureStageImpl;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
+import android.hardware.camera2.extension.ICaptureCallback;
import android.hardware.camera2.extension.ICaptureProcessorImpl;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
+import android.hardware.camera2.extension.IRequestCallback;
+import android.hardware.camera2.extension.IRequestProcessorImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
-import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
+import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.ISessionProcessorImpl;
+import android.hardware.camera2.extension.LatencyRange;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.OutputSurface;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.extension.ParcelImage;
+import android.hardware.camera2.extension.ParcelTotalCaptureResult;
+import android.hardware.camera2.extension.Request;
import android.hardware.camera2.extension.SizeList;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.HardwareBuffer;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
import android.media.Image;
import android.media.ImageReader;
import android.os.ConditionVariable;
@@ -45,9 +62,11 @@
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
+import android.util.Range;
import android.util.Size;
import android.view.Surface;
@@ -69,10 +88,26 @@
import androidx.camera.extensions.impl.PreviewExtenderImpl.ProcessorType;
import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
import androidx.camera.extensions.impl.RequestUpdateProcessorImpl;
+import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.BeautyAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.BokehAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl;
+import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl;
+import androidx.camera.extensions.impl.advanced.HdrAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.ImageProcessorImpl;
+import androidx.camera.extensions.impl.advanced.ImageReaderOutputConfigImpl;
+import androidx.camera.extensions.impl.advanced.MultiResolutionImageReaderOutputConfigImpl;
+import androidx.camera.extensions.impl.advanced.NightAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.OutputSurfaceImpl;
+import androidx.camera.extensions.impl.advanced.RequestProcessorImpl;
+import androidx.camera.extensions.impl.advanced.SessionProcessorImpl;
+import androidx.camera.extensions.impl.advanced.SurfaceOutputConfigImpl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -83,13 +118,20 @@
private static final String CAMERA_EXTENSION_VERSION_NAME =
"androidx.camera.extensions.impl.ExtensionVersionImpl";
- private static final String LATEST_VERSION = "1.1.0";
- private static final String[] SUPPORTED_VERSION_PREFIXES = {"1.1.", "1.0."};
+ private static final String LATEST_VERSION = "1.2.0";
+ private static final String LEGACY_VERSION_PREFIX = "1.1";
+ private static final String ADVANCED_VERSION_PREFIX = "1.2";
+ private static final String[] SUPPORTED_VERSION_PREFIXES = {ADVANCED_VERSION_PREFIX,
+ LEGACY_VERSION_PREFIX, "1.0."};
private static final boolean EXTENSIONS_PRESENT = checkForExtensions();
private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ?
(new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null;
- private static final boolean LATEST_VERSION_SUPPORTED =
- EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(SUPPORTED_VERSION_PREFIXES[0]);
+ private static final boolean LEGACY_VERSION_SUPPORTED =
+ EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(LEGACY_VERSION_PREFIX);
+ private static final boolean ADVANCED_VERSION_SUPPORTED =
+ EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX);
+
+ private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>();
private static boolean checkForExtensions() {
try {
@@ -223,7 +265,7 @@
public long registerClient(Context ctx) {
synchronized (mLock) {
- if (LATEST_VERSION_SUPPORTED) {
+ if (LEGACY_VERSION_SUPPORTED) {
if (mActiveClients.isEmpty()) {
InitializerFuture status = new InitializerFuture();
InitializerImpl.init(EXTENSIONS_VERSION, ctx, new InitializeHandler(status),
@@ -257,7 +299,7 @@
public void unregisterClient(long clientId) {
synchronized (mLock) {
if (mActiveClients.remove(clientId) && mActiveClients.isEmpty() &&
- LATEST_VERSION_SUPPORTED) {
+ LEGACY_VERSION_SUPPORTED) {
InitializerFuture status = new InitializerFuture();
InitializerImpl.deinit(new ReleaseHandler(status),
new HandlerExecutor(mHandler));
@@ -321,17 +363,39 @@
}
}
+ /**
+ * @hide
+ */
+ public static AdvancedExtenderImpl initializeAdvancedExtensionImpl(int extensionType) {
+ switch (extensionType) {
+ case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC:
+ return new AutoAdvancedExtenderImpl();
+ case CameraExtensionCharacteristics.EXTENSION_BEAUTY:
+ return new BeautyAdvancedExtenderImpl();
+ case CameraExtensionCharacteristics.EXTENSION_BOKEH:
+ return new BokehAdvancedExtenderImpl();
+ case CameraExtensionCharacteristics.EXTENSION_HDR:
+ return new HdrAdvancedExtenderImpl();
+ case CameraExtensionCharacteristics.EXTENSION_NIGHT:
+ return new NightAdvancedExtenderImpl();
+ default:
+ throw new IllegalArgumentException("Unknown extension: " + extensionType);
+ }
+ }
+
@Override
public void onCreate() {
super.onCreate();
// This will setup the camera vendor tag descriptor in the service process
+ // along with all camera characteristics.
try {
CameraManager manager = getSystemService(CameraManager.class);
String [] cameraIds = manager.getCameraIdListNoLazy();
if (cameraIds != null) {
for (String cameraId : cameraIds) {
- CameraCharacteristics ch = manager.getCameraCharacteristics(cameraId);
+ mCharacteristicsHashMap.put(cameraId,
+ manager.getCameraCharacteristics(cameraId));
}
}
} catch (CameraAccessException e) {
@@ -372,6 +436,29 @@
return ret;
}
+ private static List<SizeList> initializeParcelable(
+ Map<Integer, List<android.util.Size>> sizes) {
+ if (sizes == null) {
+ return null;
+ }
+ ArrayList<SizeList> ret = new ArrayList<>(sizes.size());
+ for (Map.Entry<Integer, List<android.util.Size>> entry : sizes.entrySet()) {
+ SizeList sizeList = new SizeList();
+ sizeList.format = entry.getKey();
+ sizeList.sizes = new ArrayList<>();
+ for (android.util.Size size : entry.getValue()) {
+ android.hardware.camera2.extension.Size sz =
+ new android.hardware.camera2.extension.Size();
+ sz.width = size.getWidth();
+ sz.height = size.getHeight();
+ sizeList.sizes.add(sz);
+ }
+ ret.add(sizeList);
+ }
+
+ return ret;
+ }
+
private static CameraMetadataNative initializeParcelableMetadata(
List<Pair<CaptureRequest.Key, Object>> paramList) {
if (paramList == null) {
@@ -386,6 +473,20 @@
return ret;
}
+ private static CameraMetadataNative initializeParcelableMetadata(
+ Map<CaptureRequest.Key<?>, Object> paramMap) {
+ if (paramMap == null) {
+ return null;
+ }
+
+ CameraMetadataNative ret = new CameraMetadataNative();
+ for (Map.Entry<CaptureRequest.Key<?>, Object> param : paramMap.entrySet()) {
+ ret.set(((CaptureRequest.Key) param.getKey()), param.getValue());
+ }
+
+ return ret;
+ }
+
private static android.hardware.camera2.extension.CaptureStageImpl initializeParcelable(
androidx.camera.extensions.impl.CaptureStageImpl captureStage) {
if (captureStage == null) {
@@ -400,6 +501,20 @@
return ret;
}
+ private Request initializeParcelable(RequestProcessorImpl.Request request, int requestId) {
+ Request ret = new Request();
+ ret.targetOutputConfigIds = new ArrayList<>();
+ for (int id : request.getTargetOutputConfigIds()) {
+ OutputConfigId configId = new OutputConfigId();
+ configId.id = id;
+ ret.targetOutputConfigIds.add(configId);
+ }
+ ret.templateId = request.getTemplateId();
+ ret.parameters = initializeParcelableMetadata(request.getParameters());
+ ret.requestId = requestId;
+ return ret;
+ }
+
private class CameraExtensionsProxyServiceStub extends ICameraExtensionsProxyService.Stub {
@Override
public long registerClient() {
@@ -412,6 +527,23 @@
}
@Override
+ public boolean advancedExtensionsSupported() {
+ return ADVANCED_VERSION_SUPPORTED;
+ }
+
+ @Override
+ public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType) {
+ AdvancedExtenderImpl extension;
+ try {
+ extension = initializeAdvancedExtensionImpl(extensionType);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+
+ return new AdvancedExtenderImplStub(extension);
+ }
+
+ @Override
public IPreviewExtenderImpl initializePreviewExtension(int extensionType) {
Pair<PreviewExtenderImpl, ImageCaptureExtenderImpl> extension;
try {
@@ -436,6 +568,475 @@
}
}
+ private class AdvancedExtenderImplStub extends IAdvancedExtenderImpl.Stub {
+ private final AdvancedExtenderImpl mAdvancedExtender;
+
+ public AdvancedExtenderImplStub(AdvancedExtenderImpl advancedExtender) {
+ mAdvancedExtender = advancedExtender;
+ }
+
+ @Override
+ public boolean isExtensionAvailable(String cameraId) {
+ return mAdvancedExtender.isExtensionAvailable(cameraId, mCharacteristicsHashMap);
+ }
+
+ @Override
+ public void init(String cameraId) {
+ mAdvancedExtender.init(cameraId, mCharacteristicsHashMap);
+ }
+
+ @Override
+ public List<SizeList> getSupportedPreviewOutputResolutions(String cameraId) {
+ Map<Integer, List<Size>> supportedSizesMap =
+ mAdvancedExtender.getSupportedPreviewOutputResolutions(cameraId);
+ if (supportedSizesMap != null) {
+ return initializeParcelable(supportedSizesMap);
+ }
+
+ return null;
+ }
+
+ @Override
+ public List<SizeList> getSupportedCaptureOutputResolutions(String cameraId) {
+ Map<Integer, List<Size>> supportedSizesMap =
+ mAdvancedExtender.getSupportedCaptureOutputResolutions(cameraId);
+ if (supportedSizesMap != null) {
+ return initializeParcelable(supportedSizesMap);
+ }
+
+ return null;
+ }
+
+ @Override
+ public LatencyRange getEstimatedCaptureLatencyRange(String cameraId,
+ android.hardware.camera2.extension.Size outputSize, int format) {
+ Size sz = new Size(outputSize.width, outputSize.height);
+ Range<Long> latencyRange = mAdvancedExtender.getEstimatedCaptureLatencyRange(cameraId,
+ sz, format);
+ if (latencyRange != null) {
+ LatencyRange ret = new LatencyRange();
+ ret.min = latencyRange.getLower();
+ ret.max = latencyRange.getUpper();
+ return ret;
+ }
+
+ return null;
+ }
+
+ @Override
+ public ISessionProcessorImpl getSessionProcessor() {
+ return new SessionProcessorImplStub(mAdvancedExtender.createSessionProcessor());
+ }
+ }
+
+ private class CaptureCallbackStub implements SessionProcessorImpl.CaptureCallback {
+ private final ICaptureCallback mCaptureCallback;
+
+ private CaptureCallbackStub(ICaptureCallback captureCallback) {
+ mCaptureCallback = captureCallback;
+ }
+
+ @Override
+ public void onCaptureStarted(int captureSequenceId, long timestamp) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureStarted(captureSequenceId, timestamp);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture start due to remote " +
+ "exception!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureProcessStarted(int captureSequenceId) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureProcessStarted(captureSequenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture process start due to remote " +
+ "exception!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureFailed(int captureSequenceId) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureFailed(captureSequenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture failure due to remote " +
+ "exception!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int captureSequenceId) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureSequenceCompleted(captureSequenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture sequence end due to remote " +
+ "exception!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(int captureSequenceId) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureSequenceAborted(captureSequenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture sequence abort due to remote " +
+ "exception!");
+ }
+ }
+ }
+ }
+
+ private class RequestCallbackStub extends IRequestCallback.Stub {
+ private final List<RequestProcessorImpl.Request> mRequests;
+ private final RequestProcessorImpl.Callback mCallback;
+
+ public RequestCallbackStub(List<RequestProcessorImpl.Request> requests,
+ RequestProcessorImpl.Callback callback) {
+ mCallback = callback;
+ if (mCallback != null) {
+ mRequests = requests;
+ } else {
+ Log.w(TAG, "No valid request callbacks!");
+ mRequests = new ArrayList<>();
+ }
+ }
+
+ @Override
+ public void onCaptureStarted(int requestId, long frameNumber, long timestamp) {
+ if (mCallback != null) {
+ if (mRequests.get(requestId) != null) {
+ mCallback.onCaptureStarted(mRequests.get(requestId), frameNumber, timestamp);
+ } else {
+ Log.e(TAG,"Request id: " + requestId + " not found!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureProgressed(int requestId, ParcelCaptureResult partialResult) {
+ if (mCallback != null) {
+ if (mRequests.get(requestId) != null) {
+ CaptureResult result = new CaptureResult(partialResult.cameraId,
+ partialResult.results, partialResult.parent, partialResult.sequenceId,
+ partialResult.frameNumber);
+ mCallback.onCaptureProgressed(mRequests.get(requestId), result);
+ } else {
+ Log.e(TAG,"Request id: " + requestId + " not found!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureCompleted(int requestId, ParcelTotalCaptureResult totalCaptureResult) {
+ if (mCallback != null) {
+ if (mRequests.get(requestId) != null) {
+ PhysicalCaptureResultInfo[] physicalResults = new PhysicalCaptureResultInfo[0];
+ if ((totalCaptureResult.physicalResult != null) &&
+ (!totalCaptureResult.physicalResult.isEmpty())) {
+ int count = totalCaptureResult.physicalResult.size();
+ physicalResults = new PhysicalCaptureResultInfo[count];
+ physicalResults = totalCaptureResult.physicalResult.toArray(
+ physicalResults);
+ }
+ ArrayList<CaptureResult> partials = new ArrayList<>(
+ totalCaptureResult.partials.size());
+ for (ParcelCaptureResult parcelResult : totalCaptureResult.partials) {
+ partials.add(new CaptureResult(parcelResult.cameraId, parcelResult.results,
+ parcelResult.parent, parcelResult.sequenceId,
+ parcelResult.frameNumber));
+ }
+ TotalCaptureResult result = new TotalCaptureResult(
+ totalCaptureResult.logicalCameraId, totalCaptureResult.results,
+ totalCaptureResult.parent, totalCaptureResult.sequenceId,
+ totalCaptureResult.frameNumber, partials, totalCaptureResult.sessionId,
+ physicalResults);
+ mCallback.onCaptureCompleted(mRequests.get(requestId), result);
+ } else {
+ Log.e(TAG,"Request id: " + requestId + " not found!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureFailed(int requestId, CaptureFailure captureFailure) {
+ if (mCallback != null) {
+ if (mRequests.get(requestId) != null) {
+ android.hardware.camera2.CaptureFailure failure =
+ new android.hardware.camera2.CaptureFailure(captureFailure.request,
+ captureFailure.reason, captureFailure.dropped,
+ captureFailure.sequenceId, captureFailure.frameNumber,
+ captureFailure.errorPhysicalCameraId);
+ mCallback.onCaptureFailed(mRequests.get(requestId), failure);
+ } else {
+ Log.e(TAG,"Request id: " + requestId + " not found!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId) {
+ if (mCallback != null) {
+ if (mRequests.get(requestId) != null) {
+ mCallback.onCaptureBufferLost(mRequests.get(requestId), frameNumber,
+ outputStreamId);
+ } else {
+ Log.e(TAG,"Request id: " + requestId + " not found!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) {
+ if (mCallback != null) {
+ mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber);
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(int sequenceId) {
+ if (mCallback != null) {
+ mCallback.onCaptureSequenceAborted(sequenceId);
+ }
+ }
+ }
+
+ private class ImageProcessorImplStub extends IImageProcessorImpl.Stub {
+ private final ImageProcessorImpl mImageProcessor;
+
+ public ImageProcessorImplStub(ImageProcessorImpl imageProcessor) {
+ mImageProcessor = imageProcessor;
+ }
+
+ @Override
+ public void onNextImageAvailable(OutputConfigId outputConfigId, ParcelImage img) {
+ if (mImageProcessor != null) {
+ mImageProcessor.onNextImageAvailable(outputConfigId.id, img.timestamp,
+ new ImageReferenceImpl(img));
+ }
+ }
+ }
+
+ private class RequestProcessorStub implements RequestProcessorImpl {
+ private final IRequestProcessorImpl mRequestProcessor;
+
+ public RequestProcessorStub(IRequestProcessorImpl requestProcessor) {
+ mRequestProcessor = requestProcessor;
+ }
+
+ @Override
+ public void setImageProcessor(int outputConfigId,
+ ImageProcessorImpl imageProcessor) {
+ OutputConfigId configId = new OutputConfigId();
+ configId.id = outputConfigId;
+ try {
+ mRequestProcessor.setImageProcessor(configId,
+ new ImageProcessorImplStub(imageProcessor));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to set image processor due to remote exception!");
+ }
+ }
+
+ @Override
+ public boolean submit(Request request, Callback callback) {
+ ArrayList<Request> requests = new ArrayList<>();
+ requests.add(request);
+ return submit(requests, callback);
+ }
+
+ @Override
+ public boolean submit(List<Request> requests, Callback callback) {
+ ArrayList<android.hardware.camera2.extension.Request> captureRequests =
+ new ArrayList<>();
+ int requestId = 0;
+ for (Request request : requests) {
+ captureRequests.add(initializeParcelable(request, requestId));
+ requestId++;
+ }
+
+ try {
+ return mRequestProcessor.submitBurst(captureRequests,
+ new RequestCallbackStub(requests, callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to submit request due to remote exception!");
+ }
+ return false;
+ }
+
+ @Override
+ public boolean setRepeating(Request request, Callback callback) {
+ try {
+ ArrayList<Request> requests = new ArrayList<>();
+ requests.add(request);
+ return mRequestProcessor.setRepeating(initializeParcelable(request, 0),
+ new RequestCallbackStub(requests, callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to submit repeating request due to remote exception!");
+ }
+
+ return false;
+ }
+
+ @Override
+ public void abortCaptures() {
+ try {
+ mRequestProcessor.abortCaptures();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to abort requests due to remote exception!");
+ }
+ }
+
+ @Override
+ public void stopRepeating() {
+ try {
+ mRequestProcessor.stopRepeating();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to stop repeating request due to remote exception!");
+ }
+ }
+ }
+
+ private class SessionProcessorImplStub extends ISessionProcessorImpl.Stub {
+ private final SessionProcessorImpl mSessionProcessor;
+
+ public SessionProcessorImplStub(SessionProcessorImpl sessionProcessor) {
+ mSessionProcessor = sessionProcessor;
+ }
+
+ @Override
+ public CameraSessionConfig initSession(String cameraId, OutputSurface previewSurface,
+ OutputSurface burstSurface) {
+ OutputSurfaceImplStub outputPreviewSurfaceImpl =
+ new OutputSurfaceImplStub(previewSurface);
+ OutputSurfaceImplStub outputBurstSurfaceImpl =
+ new OutputSurfaceImplStub(burstSurface);
+
+ Camera2SessionConfigImpl sessionConfig = mSessionProcessor.initSession(cameraId,
+ mCharacteristicsHashMap, getApplicationContext(), outputPreviewSurfaceImpl,
+ outputBurstSurfaceImpl, null /*imageAnalysisSurfaceConfig*/);
+
+ List<Camera2OutputConfigImpl> outputConfigs = sessionConfig.getOutputConfigs();
+ CameraSessionConfig ret = new CameraSessionConfig();
+ ret.outputConfigs = new ArrayList<>();
+ for (Camera2OutputConfigImpl output : outputConfigs) {
+ CameraOutputConfig entry = new CameraOutputConfig();
+ entry.outputId = new OutputConfigId();
+ entry.outputId.id = output.getId();
+ entry.physicalCameraId = output.getPhysicalCameraId();
+ entry.surfaceGroupId = output.getSurfaceGroupId();
+ if (output instanceof SurfaceOutputConfigImpl) {
+ SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output;
+ entry.type = CameraOutputConfig.TYPE_SURFACE;
+ entry.surface = surfaceConfig.getSurface();
+ } else if (output instanceof ImageReaderOutputConfigImpl) {
+ ImageReaderOutputConfigImpl imageReaderOutputConfig =
+ (ImageReaderOutputConfigImpl) output;
+ entry.type = CameraOutputConfig.TYPE_IMAGEREADER;
+ entry.size = new android.hardware.camera2.extension.Size();
+ entry.size.width = imageReaderOutputConfig.getSize().getWidth();
+ entry.size.height = imageReaderOutputConfig.getSize().getHeight();
+ entry.imageFormat = imageReaderOutputConfig.getImageFormat();
+ entry.capacity = imageReaderOutputConfig.getMaxImages();
+ } else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) {
+ MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig =
+ (MultiResolutionImageReaderOutputConfigImpl) output;
+ entry.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER;
+ entry.imageFormat = multiResReaderConfig.getImageFormat();
+ entry.capacity = multiResReaderConfig.getMaxImages();
+ } else {
+ throw new IllegalStateException("Unknown output config type!");
+ }
+ List<Camera2OutputConfigImpl> sharedOutputs =
+ output.getSurfaceSharingOutputConfigs();
+ if ((sharedOutputs != null) && (!sharedOutputs.isEmpty())) {
+ entry.surfaceSharingOutputConfigs = new ArrayList<>();
+ for (Camera2OutputConfigImpl sharedOutput : sharedOutputs) {
+ OutputConfigId outputId = new OutputConfigId();
+ outputId.id = sharedOutput.getId();
+ entry.surfaceSharingOutputConfigs.add(outputId);
+ }
+ }
+ ret.outputConfigs.add(entry);
+ }
+ ret.sessionTemplateId = sessionConfig.getSessionTemplateId();
+ ret.sessionParameter = initializeParcelableMetadata(
+ sessionConfig.getSessionParameters());
+
+ return ret;
+ }
+
+ @Override
+ public void deInitSession() {
+ mSessionProcessor.deInitSession();
+ }
+
+ @Override
+ public void onCaptureSessionStart(IRequestProcessorImpl requestProcessor) {
+ mSessionProcessor.onCaptureSessionStart(new RequestProcessorStub(requestProcessor));
+ }
+
+ @Override
+ public void onCaptureSessionEnd() {
+ mSessionProcessor.onCaptureSessionEnd();
+ }
+
+ @Override
+ public int startRepeating(ICaptureCallback callback) {
+ return mSessionProcessor.startRepeating(new CaptureCallbackStub(callback));
+ }
+
+ @Override
+ public void stopRepeating() {
+ mSessionProcessor.stopRepeating();
+ }
+
+ @Override
+ public int startCapture(ICaptureCallback callback, int jpegRotation, int jpegQuality) {
+ HashMap<CaptureRequest.Key<?>, Object> paramMap = new HashMap<>();
+ paramMap.put(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
+ paramMap.put(CaptureRequest.JPEG_QUALITY, jpegQuality);
+ mSessionProcessor.setParameters(paramMap);
+ return mSessionProcessor.startCapture(new CaptureCallbackStub(callback));
+ }
+ }
+
+ private class OutputSurfaceImplStub implements OutputSurfaceImpl {
+ private final Surface mSurface;
+ private final Size mSize;
+ private final int mImageFormat;
+
+ public OutputSurfaceImplStub(OutputSurface outputSurface) {
+ mSurface = outputSurface.surface;
+ mSize = new Size(outputSurface.size.width, outputSurface.size.height);
+ mImageFormat = outputSurface.imageFormat;
+ }
+
+ @Override
+ public Surface getSurface() {
+ return mSurface;
+ }
+
+ @Override
+ public Size getSize() {
+ return mSize;
+ }
+
+ @Override
+ public int getImageFormat() {
+ return mImageFormat;
+ }
+ }
+
private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub {
private final PreviewExtenderImpl mPreviewExtender;
@@ -471,7 +1072,7 @@
@Override
public void init(String cameraId, CameraMetadataNative chars) {
- if (LATEST_VERSION_SUPPORTED) {
+ if (LEGACY_VERSION_SUPPORTED) {
mPreviewExtender.init(cameraId, new CameraCharacteristics(chars));
}
}
@@ -535,7 +1136,7 @@
@Override
public List<SizeList> getSupportedResolutions() {
- if (LATEST_VERSION_SUPPORTED) {
+ if (LEGACY_VERSION_SUPPORTED) {
List<Pair<Integer, android.util.Size[]>> sizes =
mPreviewExtender.getSupportedResolutions();
if ((sizes != null) && !sizes.isEmpty()) {
@@ -581,7 +1182,7 @@
@Override
public void init(String cameraId, CameraMetadataNative chars) {
- if (LATEST_VERSION_SUPPORTED) {
+ if (LEGACY_VERSION_SUPPORTED) {
mImageExtender.init(cameraId, new CameraCharacteristics(chars));
}
}
@@ -627,7 +1228,7 @@
@Override
public List<SizeList> getSupportedResolutions() {
- if (LATEST_VERSION_SUPPORTED) {
+ if (LEGACY_VERSION_SUPPORTED) {
List<Pair<Integer, android.util.Size[]>> sizes =
mImageExtender.getSupportedResolutions();
if ((sizes != null) && !sizes.isEmpty()) {
@@ -737,6 +1338,51 @@
}
}
+ private class ImageReferenceImpl extends ExtensionImage
+ implements androidx.camera.extensions.impl.advanced.ImageReferenceImpl {
+
+ private final Object mImageLock = new Object();
+ private int mReferenceCount;
+
+ private ImageReferenceImpl(ParcelImage parcelImage) {
+ super(parcelImage);
+ mReferenceCount = 1;
+ }
+
+ @Override
+ public boolean increment() {
+ synchronized (mImageLock) {
+ if (mReferenceCount <= 0) {
+ return false;
+ }
+ mReferenceCount++;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean decrement() {
+ synchronized (mImageLock) {
+ if (mReferenceCount <= 0) {
+ return false;
+ }
+ mReferenceCount--;
+
+ if (mReferenceCount <= 0) {
+ close();
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public Image get() {
+ return this;
+ }
+ }
+
private class ExtensionImage extends android.media.Image {
private final android.hardware.camera2.extension.ParcelImage mParcelImage;
private GraphicBuffer mGraphicBuffer;
diff --git a/services/api/current.txt b/services/api/current.txt
index 475dcf5..50f0052 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -60,6 +60,20 @@
}
+package com.android.server.usage {
+
+ public interface StorageStatsManagerLocal {
+ method public void registerStorageStatsAugmenter(@NonNull com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter, @NonNull String);
+ }
+
+ public static interface StorageStatsManagerLocal.StorageStatsAugmenter {
+ method public void augmentStatsForPackageForUser(@NonNull android.content.pm.PackageStats, @NonNull String, @NonNull android.os.UserHandle, boolean);
+ method public void augmentStatsForUid(@NonNull android.content.pm.PackageStats, int, boolean);
+ method public void augmentStatsForUser(@NonNull android.content.pm.PackageStats, @NonNull android.os.UserHandle);
+ }
+
+}
+
package com.android.server.wifi {
public class SupplicantManager {
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 3bb6e08..85084f4 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1839,8 +1839,6 @@
// For a full update we replace the RemoteViews completely.
widget.views = views;
}
- widget.views.setProviderInstanceId(UPDATE_COUNTER.get());
-
int memoryUsage;
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) &&
(widget.views != null) &&
@@ -1942,13 +1940,14 @@
return;
}
if (updateViews != null) {
+ updateViews = new RemoteViews(updateViews);
updateViews.setProviderInstanceId(requestId);
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = widget.host;
args.arg2 = widget.host.callbacks;
- args.arg3 = (updateViews != null) ? updateViews.clone() : null;
+ args.arg3 = updateViews;
args.arg4 = requestId;
args.argi1 = widget.appWidgetId;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5dc11c58..55b982b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -21,8 +21,7 @@
name: "services.core-sources",
srcs: ["java/**/*.java"],
exclude_srcs: [
- ":connectivity-service-srcs",
- ":services.core-sources-am-wm"
+ ":services.core-sources-am-wm",
],
path: "java",
visibility: [
@@ -39,13 +38,13 @@
],
tools: ["protologtool"],
cmd: "$(location protologtool) transform-protolog-calls " +
- "--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--protolog-impl-class com.android.internal.protolog.ProtoLogImpl " +
- "--protolog-cache-class 'com.android.server.wm.ProtoLogCache' " +
- "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
- "--loggroups-jar $(location :protolog-groups) " +
- "--output-srcjar $(out) " +
- "$(locations :services.core-sources-am-wm)",
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--protolog-impl-class com.android.internal.protolog.ProtoLogImpl " +
+ "--protolog-cache-class 'com.android.server.wm.ProtoLogCache' " +
+ "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
+ "--loggroups-jar $(location :protolog-groups) " +
+ "--output-srcjar $(out) " +
+ "$(locations :services.core-sources-am-wm)",
out: ["services.core.protolog.srcjar"],
}
@@ -57,11 +56,11 @@
],
tools: ["protologtool"],
cmd: "$(location protologtool) generate-viewer-config " +
- "--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
- "--loggroups-jar $(location :protolog-groups) " +
- "--viewer-conf $(out) " +
- "$(locations :services.core-sources-am-wm)",
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
+ "--loggroups-jar $(location :protolog-groups) " +
+ "--viewer-conf $(out) " +
+ "$(locations :services.core-sources-am-wm)",
out: ["services.core.protolog.json"],
}
@@ -72,12 +71,12 @@
":services.core.protolog.json",
],
cmd: "cp $(location :generate-protolog.json) $(out) && " +
- "{ ! (diff $(out) $(location :services.core.protolog.json) | grep -q '^<') || " +
- "{ echo -e '\\n\\n################################################################\\n#\\n" +
- "# ERROR: ProtoLog viewer config is stale. To update it, run:\\n#\\n" +
- "# cp $(location :generate-protolog.json) " +
- "$(location :services.core.protolog.json)\\n#\\n" +
- "################################################################\\n\\n' >&2 && false; } }",
+ "{ ! (diff $(out) $(location :services.core.protolog.json) | grep -q '^<') || " +
+ "{ echo -e '\\n\\n################################################################\\n#\\n" +
+ "# ERROR: ProtoLog viewer config is stale. To update it, run:\\n#\\n" +
+ "# cp $(location :generate-protolog.json) " +
+ "$(location :services.core.protolog.json)\\n#\\n" +
+ "################################################################\\n\\n' >&2 && false; } }",
out: ["services.core.protolog.json"],
}
@@ -85,7 +84,7 @@
name: "statslog-art-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module art" +
- " --javaPackage com.android.internal.art --javaClass ArtStatsLog --worksource",
+ " --javaPackage com.android.internal.art --javaClass ArtStatsLog --worksource",
out: ["com/android/internal/art/ArtStatsLog.java"],
}
@@ -186,7 +185,7 @@
java_library_host {
name: "core_cts_test_resources",
- srcs: ["java/com/android/server/notification/SmallHash.java"]
+ srcs: ["java/com/android/server/notification/SmallHash.java"],
}
prebuilt_etc {
@@ -210,36 +209,6 @@
filegroup {
name: "services.core-sources-deviceconfig-interface",
srcs: [
- "java/com/android/server/utils/DeviceConfigInterface.java"
- ],
-}
-
-// TODO: Move connectivity service sources to independent directory.
-filegroup {
- name: "connectivity-service-srcs",
- srcs: [
- "java/com/android/server/ConnectivityService.java",
- "java/com/android/server/ConnectivityServiceInitializer.java",
- "java/com/android/server/NetIdManager.java",
- "java/com/android/server/TestNetworkService.java",
- "java/com/android/server/connectivity/AutodestructReference.java",
- "java/com/android/server/connectivity/DnsManager.java",
- "java/com/android/server/connectivity/FullScore.java",
- "java/com/android/server/connectivity/KeepaliveTracker.java",
- "java/com/android/server/connectivity/LingerMonitor.java",
- "java/com/android/server/connectivity/MockableSystemProperties.java",
- "java/com/android/server/connectivity/Nat464Xlat.java",
- "java/com/android/server/connectivity/NetworkAgentInfo.java",
- "java/com/android/server/connectivity/NetworkDiagnostics.java",
- "java/com/android/server/connectivity/NetworkNotificationManager.java",
- "java/com/android/server/connectivity/NetworkOffer.java",
- "java/com/android/server/connectivity/NetworkRanker.java",
- "java/com/android/server/connectivity/OsCompat.java",
- "java/com/android/server/connectivity/PermissionMonitor.java",
- "java/com/android/server/connectivity/ProfileNetworkPreferences.java",
- "java/com/android/server/connectivity/ProxyTracker.java",
- "java/com/android/server/connectivity/QosCallbackAgentConnection.java",
- "java/com/android/server/connectivity/QosCallbackTracker.java",
- "java/com/android/server/connectivity/TcpKeepaliveController.java",
+ "java/com/android/server/utils/DeviceConfigInterface.java",
],
}
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index d574e74..d6ee951 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1112,7 +1112,7 @@
case IpSecManager.DIRECTION_IN:
return;
case IpSecManager.DIRECTION_FWD:
- // Only NETWORK_STACK or PERMISSION_NETWORK_STACK allowed to use forward policies
+ // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
PermissionUtils.enforceNetworkStackPermission(mContext);
return;
}
@@ -1358,6 +1358,16 @@
ikey,
0xffffffff,
resourceId);
+
+ // Add a forwarding policy on the tunnel interface. In order to support forwarding
+ // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
+ //
+ // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
+ // forwarding will be blocked by default (as would be the case if this policy was
+ // absent).
+ //
+ // This is necessary only on the tunnel interface, and not any the interface to
+ // which traffic will be forwarded to.
netd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index db36e62..c3543e7 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -361,10 +361,13 @@
try {
executeRescueLevelInternal(context, level, failedPackage);
EventLogTags.writeRescueSuccess(level);
- logCriticalInfo(Log.DEBUG,
- "Finished rescue level " + levelToString(level));
+ String successMsg = "Finished rescue level " + levelToString(level);
+ if (!TextUtils.isEmpty(failedPackage)) {
+ successMsg += " for package " + failedPackage;
+ }
+ logCriticalInfo(Log.DEBUG, successMsg);
} catch (Throwable t) {
- logRescueException(level, t);
+ logRescueException(level, failedPackage, t);
}
}
@@ -427,7 +430,7 @@
pm.reboot(TAG);
}
} catch (Throwable t) {
- logRescueException(level, t);
+ logRescueException(level, failedPackage, t);
}
};
thread = new Thread(runnable);
@@ -441,7 +444,7 @@
try {
RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
} catch (Throwable t) {
- logRescueException(level, t);
+ logRescueException(level, failedPackage, t);
}
}
};
@@ -455,11 +458,15 @@
}
}
- private static void logRescueException(int level, Throwable t) {
+ private static void logRescueException(int level, @Nullable String failedPackageName,
+ Throwable t) {
final String msg = ExceptionUtils.getCompleteMessage(t);
EventLogTags.writeRescueFailure(level, msg);
- logCriticalInfo(Log.ERROR,
- "Failed rescue level " + levelToString(level) + ": " + msg);
+ String failureMsg = "Failed rescue level " + levelToString(level);
+ if (!TextUtils.isEmpty(failedPackageName)) {
+ failureMsg += " for package " + failedPackageName;
+ }
+ logCriticalInfo(Log.ERROR, failureMsg + ": " + msg);
}
private static int mapRescueLevelToUserImpact(int rescueLevel) {
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 7763ad9..9314ed74 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -20,12 +20,17 @@
import static android.app.ActivityManager.RunningServiceInfo;
import static android.app.ActivityManager.RunningTaskInfo;
import static android.app.ActivityManager.getCurrentUser;
+import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
+import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS;
import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
@@ -60,6 +65,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -70,6 +76,7 @@
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.service.SensorPrivacyServiceDumpProto;
import android.service.SensorPrivacyUserProto;
+import android.service.voice.VoiceInteractionManagerInternal;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
@@ -141,6 +148,7 @@
private static final int VER0_INDIVIDUAL_ENABLED = 1;
private static final int VER1_ENABLED = 0;
private static final int VER1_INDIVIDUAL_ENABLED = 1;
+ public static final int REMINDER_DIALOG_DELAY_MILLIS = 500;
private final Context mContext;
private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl;
@@ -165,6 +173,7 @@
mActivityManager = context.getSystemService(ActivityManager.class);
mActivityTaskManager = context.getSystemService(ActivityTaskManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
+
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl();
}
@@ -206,6 +215,36 @@
private ArrayMap<Pair<String, UserHandle>, ArrayList<IBinder>> mSuppressReminders =
new ArrayMap<>();
+ private final ArrayMap<SensorUseReminderDialogInfo, ArraySet<Integer>>
+ mQueuedSensorUseReminderDialogs = new ArrayMap<>();
+
+ private class SensorUseReminderDialogInfo {
+ private int mTaskId;
+ private UserHandle mUser;
+ private String mPackageName;
+
+ SensorUseReminderDialogInfo(int taskId, UserHandle user, String packageName) {
+ mTaskId = taskId;
+ mUser = user;
+ mPackageName = packageName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof SensorUseReminderDialogInfo)) return false;
+ SensorUseReminderDialogInfo that = (SensorUseReminderDialogInfo) o;
+ return mTaskId == that.mTaskId
+ && Objects.equals(mUser, that.mUser)
+ && Objects.equals(mPackageName, that.mPackageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTaskId, mUser, mPackageName);
+ }
+ }
+
SensorPrivacyServiceImpl() {
mHandler = new SensorPrivacyHandler(FgThread.get().getLooper(), mContext);
File sensorPrivacyFile = new File(Environment.getDataSystemDirectory(),
@@ -228,7 +267,8 @@
}
}
- int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_CAMERA};
+ int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE,
+ OP_CAMERA, OP_PHONE_CALL_CAMERA};
mAppOpsManager.startWatchingNoted(micAndCameraOps, this);
mAppOpsManager.startWatchingStarted(micAndCameraOps, this);
@@ -254,15 +294,29 @@
public void onOpNoted(int code, int uid, String packageName,
String attributionTag, @AppOpsManager.OpFlags int flags,
@AppOpsManager.Mode int result) {
- if (result != MODE_IGNORED || (flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) {
+ if ((flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) {
return;
}
int sensor;
- if (code == OP_RECORD_AUDIO) {
- sensor = MICROPHONE;
+ if (result == MODE_IGNORED) {
+ if (code == OP_RECORD_AUDIO) {
+ sensor = MICROPHONE;
+ } else if (code == OP_CAMERA) {
+ sensor = CAMERA;
+ } else {
+ return;
+ }
+ } else if (result == MODE_ALLOWED) {
+ if (code == OP_PHONE_CALL_MICROPHONE) {
+ sensor = MICROPHONE;
+ } else if (code == OP_PHONE_CALL_CAMERA) {
+ sensor = CAMERA;
+ } else {
+ return;
+ }
} else {
- sensor = CAMERA;
+ return;
}
long token = Binder.clearCallingIdentity();
@@ -294,6 +348,11 @@
}
}
+ if (uid == Process.SYSTEM_UID) {
+ enqueueSensorUseReminderDialogAsync(-1, user, packageName, sensor);
+ return;
+ }
+
// TODO: Handle reminders with multiple sensors
// - If we have a likely activity that triggered the sensor use overlay a dialog over
@@ -312,7 +371,7 @@
if (task.isVisible && task.topActivity.getPackageName().equals(packageName)) {
if (task.isFocused) {
// There is the one focused activity
- showSensorUseReminderDialog(task.taskId, user, packageName, sensor);
+ enqueueSensorUseReminderDialogAsync(task.taskId, user, packageName, sensor);
return;
}
@@ -323,7 +382,7 @@
// TODO: Test this case
// There is one or more non-focused activity
if (tasksOfPackageUsingSensor.size() == 1) {
- showSensorUseReminderDialog(tasksOfPackageUsingSensor.get(0).taskId, user,
+ enqueueSensorUseReminderDialogAsync(tasksOfPackageUsingSensor.get(0).taskId, user,
packageName, sensor);
return;
} else if (tasksOfPackageUsingSensor.size() > 1) {
@@ -345,6 +404,15 @@
}
}
+ VoiceInteractionManagerInternal voiceInteractionManagerInternal =
+ LocalServices.getService(VoiceInteractionManagerInternal.class);
+
+ if (sensor == MICROPHONE && voiceInteractionManagerInternal != null
+ && voiceInteractionManagerInternal.hasActiveSession(packageName)) {
+ enqueueSensorUseReminderDialogAsync(-1, user, packageName, sensor);
+ return;
+ }
+
Log.i(TAG, packageName + "/" + uid + " started using sensor " + sensor
+ " but no activity or foreground service was running. The user will not be"
+ " informed. System components should check if sensor privacy is enabled for"
@@ -360,21 +428,60 @@
* @param packageName The name of the package using the sensor.
* @param sensor The sensor that is being used.
*/
- private void showSensorUseReminderDialog(int taskId, @NonNull UserHandle user,
+ private void enqueueSensorUseReminderDialogAsync(int taskId, @NonNull UserHandle user,
@NonNull String packageName, int sensor) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ this:: enqueueSensorUseReminderDialog, taskId, user, packageName, sensor));
+ }
+
+ private void enqueueSensorUseReminderDialog(int taskId, @NonNull UserHandle user,
+ @NonNull String packageName, int sensor) {
+ SensorUseReminderDialogInfo info =
+ new SensorUseReminderDialogInfo(taskId, user, packageName);
+ if (!mQueuedSensorUseReminderDialogs.containsKey(info)) {
+ ArraySet<Integer> sensors = new ArraySet<Integer>();
+ sensors.add(sensor);
+ mQueuedSensorUseReminderDialogs.put(info, sensors);
+ mHandler.sendMessageDelayed(
+ PooledLambda.obtainMessage(this::showSensorUserReminderDialog, info),
+ REMINDER_DIALOG_DELAY_MILLIS);
+ return;
+ }
+ ArraySet<Integer> sensors = mQueuedSensorUseReminderDialogs.get(info);
+ sensors.add(sensor);
+ }
+
+ private void showSensorUserReminderDialog(@NonNull SensorUseReminderDialogInfo info) {
+ ArraySet<Integer> sensors = mQueuedSensorUseReminderDialogs.get(info);
+ mQueuedSensorUseReminderDialogs.remove(info);
+ if (sensors == null) {
+ Log.e(TAG, "Unable to show sensor use dialog because sensor set is null."
+ + " Was the dialog queue modified from outside the handler thread?");
+ return;
+ }
Intent dialogIntent = new Intent();
dialogIntent.setComponent(ComponentName.unflattenFromString(
mContext.getResources().getString(
R.string.config_sensorUseStartedActivity)));
ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchTaskId(taskId);
+ options.setLaunchTaskId(info.mTaskId);
options.setTaskOverlay(true, true);
- dialogIntent.putExtra(EXTRA_PACKAGE_NAME, packageName);
- dialogIntent.putExtra(EXTRA_SENSOR, sensor);
+ dialogIntent.addFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- mContext.startActivityAsUser(dialogIntent, options.toBundle(), user);
+ dialogIntent.putExtra(EXTRA_PACKAGE_NAME, info.mPackageName);
+ if (sensors.size() == 1) {
+ dialogIntent.putExtra(EXTRA_SENSOR, sensors.valueAt(0));
+ } else if (sensors.size() == 2) {
+ dialogIntent.putExtra(EXTRA_ALL_SENSORS, true);
+ } else {
+ // Currently the only cases can be 1 or two
+ Log.e(TAG, "Attempted to show sensor use dialog for " + sensors.size()
+ + " sensors");
+ return;
+ }
+ mContext.startActivityAsUser(dialogIntent, options.toBundle(), info.mUser);
}
/**
@@ -469,7 +576,7 @@
@Override
public void setIndividualSensorPrivacy(@UserIdInt int userId, int sensor, boolean enable) {
enforceManageSensorPrivacyPermission();
- if (!canChangeIndividualSensorPrivacy(sensor)) {
+ if (!canChangeIndividualSensorPrivacy(userId, sensor)) {
return;
}
@@ -500,14 +607,14 @@
mHandler.onSensorPrivacyChanged(userId, sensor, enable);
}
- private boolean canChangeIndividualSensorPrivacy(int sensor) {
+ private boolean canChangeIndividualSensorPrivacy(@UserIdInt int userId, int sensor) {
if (sensor == MICROPHONE && mEmergencyCallHelper.isInEmergencyCall()) {
// During emergency call the microphone toggle managed automatically
Log.i(TAG, "Can't change mic toggle during an emergency call");
return false;
}
- if (mKeyguardManager != null && mKeyguardManager.isDeviceLocked()) {
+ if (mKeyguardManager != null && mKeyguardManager.isDeviceLocked(userId)) {
Log.i(TAG, "Can't change mic/cam toggle while device is locked");
return false;
}
@@ -1224,12 +1331,12 @@
private void setUserRestriction(int userId, int sensor, boolean enabled) {
if (sensor == CAMERA) {
mAppOpsManager.setUserRestrictionForUser(OP_CAMERA, enabled,
- mAppOpsRestrictionToken, new String[]{}, userId);
+ mAppOpsRestrictionToken, null, userId);
} else if (sensor == MICROPHONE) {
mAppOpsManager.setUserRestrictionForUser(OP_RECORD_AUDIO, enabled,
- mAppOpsRestrictionToken, new String[]{}, userId);
+ mAppOpsRestrictionToken, null, userId);
mAppOpsManager.setUserRestrictionForUser(OP_RECORD_AUDIO_HOTWORD, enabled,
- mAppOpsRestrictionToken, new String[]{}, userId);
+ mAppOpsRestrictionToken, null, userId);
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index a1a4418..0c785da 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1850,7 +1850,7 @@
public StorageManagerService(Context context) {
sSelf = this;
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mContext = context;
mResolver = mContext.getContentResolver();
mCallbacks = new Callbacks(FgThread.get().getLooper());
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 2bb9084..e7e3ce9 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -167,7 +167,6 @@
@NonNull private final VcnNetworkProvider mNetworkProvider;
@NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb;
@NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
- @NonNull private final VcnContext mVcnContext;
@NonNull private final BroadcastReceiver mPkgChangeReceiver;
@NonNull
@@ -212,7 +211,6 @@
mContext, mLooper, mTelephonySubscriptionTrackerCb);
mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
- mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider);
mPkgChangeReceiver = new BroadcastReceiver() {
@Override
@@ -336,8 +334,9 @@
public VcnContext newVcnContext(
@NonNull Context context,
@NonNull Looper looper,
- @NonNull VcnNetworkProvider vcnNetworkProvider) {
- return new VcnContext(context, looper, vcnNetworkProvider);
+ @NonNull VcnNetworkProvider vcnNetworkProvider,
+ boolean isInTestMode) {
+ return new VcnContext(context, looper, vcnNetworkProvider, isInTestMode);
}
/** Creates a new Vcn instance using the provided configuration */
@@ -419,6 +418,14 @@
"Carrier privilege required for subscription group to set VCN Config");
}
+ private void enforceManageTestNetworksForTestMode(@NonNull VcnConfig vcnConfig) {
+ if (vcnConfig.isTestModeProfile()) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.MANAGE_TEST_NETWORKS,
+ "Test-mode require the MANAGE_TEST_NETWORKS permission");
+ }
+ }
+
private class VcnSubscriptionTrackerCallback implements TelephonySubscriptionTrackerCallback {
/**
* Handles subscription group changes, as notified by {@link TelephonySubscriptionTracker}
@@ -542,8 +549,11 @@
final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup);
+ final VcnContext vcnContext =
+ mDeps.newVcnContext(
+ mContext, mLooper, mNetworkProvider, config.isTestModeProfile());
final Vcn newInstance =
- mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
+ mDeps.newVcn(vcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their
@@ -587,6 +597,7 @@
mContext.getSystemService(AppOpsManager.class)
.checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName());
+ enforceManageTestNetworksForTestMode(config);
enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName);
Binder.withCleanCallingIdentity(() -> {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 9835a9a..670f557 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -97,6 +97,7 @@
"/system/bin/audioserver",
"/system/bin/cameraserver",
"/system/bin/drmserver",
+ "/system/bin/keystore2",
"/system/bin/mediadrmserver",
"/system/bin/mediaserver",
"/system/bin/netd",
@@ -698,7 +699,7 @@
if (mActivity != null) {
mActivity.addErrorToDropBox(
"watchdog", null, "system_server", null, null, null,
- localSubject, report.toString(), stack, null, null, null);
+ localSubject, report.toString(), stack, null, null, null, null);
}
FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED,
localSubject);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1bd53ae..aadb25c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -24,6 +24,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD;
+import static android.os.PowerExemptionManager.REASON_OPT_OUT_REQUESTED;
import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_PLATFORM_VPN;
import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_VPN;
import static android.os.PowerExemptionManager.REASON_TEMP_ALLOWED_WHILE_IN_USE;
@@ -5874,7 +5875,12 @@
ret = REASON_OP_ACTIVATE_PLATFORM_VPN;
}
}
-
+ if (ret == REASON_DENIED) {
+ if (mAm.mConstants.mFgsAllowOptOut
+ && targetService.appInfo.hasRequestForegroundServiceExemption()) {
+ ret = REASON_OPT_OUT_REQUESTED;
+ }
+ }
return ret;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 0979585..0fff8be 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -110,6 +110,7 @@
static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout";
static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate";
static final String KEY_KILL_FAS_CACHED_IDLE = "kill_fas_cached_idle";
+ static final String KEY_FGS_ALLOW_OPT_OUT = "fgs_allow_opt_out";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -161,6 +162,7 @@
*/
private static final int
DEFAULT_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR = 1;
+ private static final boolean DEFAULT_FGS_ALLOW_OPT_OUT = false;
// Flag stored in the DeviceConfig API.
/**
@@ -493,6 +495,12 @@
*/
volatile boolean mKillForceAppStandByAndCachedIdle = DEFAULT_KILL_FAS_CACHED_IDLE;
+ /**
+ * Whether to allow "opt-out" from the foreground service restrictions.
+ * (https://developer.android.com/about/versions/12/foreground-services)
+ */
+ volatile boolean mFgsAllowOptOut = DEFAULT_FGS_ALLOW_OPT_OUT;
+
private final ActivityManagerService mService;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -701,6 +709,9 @@
case KEY_KILL_FAS_CACHED_IDLE:
updateKillFasCachedIdle();
break;
+ case KEY_FGS_ALLOW_OPT_OUT:
+ updateFgsAllowOptOut();
+ break;
default:
break;
}
@@ -1040,6 +1051,13 @@
DEFAULT_KILL_FAS_CACHED_IDLE);
}
+ private void updateFgsAllowOptOut() {
+ mFgsAllowOptOut = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_FGS_ALLOW_OPT_OUT,
+ DEFAULT_FGS_ALLOW_OPT_OUT);
+ }
+
private void updateImperceptibleKillExemptions() {
IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear();
IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
@@ -1260,6 +1278,8 @@
pw.print("="); pw.println(mFgsAtomSampleRate);
pw.print(" "); pw.print(KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR);
pw.print("="); pw.println(mPushMessagingOverQuotaBehavior);
+ pw.print(" "); pw.print(KEY_FGS_ALLOW_OPT_OUT);
+ pw.print("="); pw.println(mFgsAllowOptOut);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 70538bb..d3955eb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -351,6 +351,7 @@
import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.TriFunction;
import com.android.server.AlarmManagerInternal;
import com.android.server.DeviceIdleInternal;
@@ -420,6 +421,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
@@ -621,6 +623,8 @@
@GuardedBy("this")
BroadcastStats mCurBroadcastStats;
+ TraceErrorLogger mTraceErrorLogger;
+
BroadcastQueue broadcastQueueForIntent(Intent intent) {
if (isOnOffloadQueue(intent.getFlags())) {
if (DEBUG_BROADCAST_BACKGROUND) {
@@ -2336,6 +2340,7 @@
mInternal = new LocalService();
mPendingStartActivityUids = new PendingStartActivityUids(mContext);
+ mTraceErrorLogger = new TraceErrorLogger();
}
public void setSystemServiceManager(SystemServiceManager mgr) {
@@ -7810,7 +7815,7 @@
addErrorToDropBox(
eventType, r, processName, null, null, null, null, null, null, crashInfo,
- new Float(loadingProgress), incrementalMetrics);
+ new Float(loadingProgress), incrementalMetrics, null);
mAppErrors.crashApplication(r, crashInfo);
}
@@ -7993,7 +7998,7 @@
callingPid, (r != null) ? r.getProcessClassEnum() : 0);
addErrorToDropBox("wtf", r, processName, null, null, null, tag, null, null, crashInfo,
- null, null);
+ null, null, null);
return r;
}
@@ -8018,7 +8023,7 @@
for (Pair<String, ApplicationErrorReport.CrashInfo> p = list.poll();
p != null; p = list.poll()) {
addErrorToDropBox("wtf", proc, "system_server", null, null, null, p.first, null, null,
- p.second, null, null);
+ p.second, null, null, null);
}
}
@@ -8109,13 +8114,15 @@
* @param crashInfo giving an application stack trace, null if absent
* @param loadingProgress the loading progress of an installed package, range in [0, 1].
* @param incrementalMetrics metrics for apps installed on Incremental.
+ * @param errorId a unique id to append to the dropbox headers.
*/
public void addErrorToDropBox(String eventType,
ProcessRecord process, String processName, String activityShortComponentName,
String parentShortComponentName, ProcessRecord parentProcess,
String subject, final String report, final File dataFile,
final ApplicationErrorReport.CrashInfo crashInfo,
- @Nullable Float loadingProgress, @Nullable IncrementalMetrics incrementalMetrics) {
+ @Nullable Float loadingProgress, @Nullable IncrementalMetrics incrementalMetrics,
+ @Nullable UUID errorId) {
// NOTE -- this must never acquire the ActivityManagerService lock,
// otherwise the watchdog may be prevented from resetting the system.
@@ -8169,6 +8176,9 @@
if (subject != null) {
sb.append("Subject: ").append(subject).append("\n");
}
+ if (errorId != null) {
+ sb.append("ErrorId: ").append(errorId.toString()).append("\n");
+ }
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
if (Debug.isDebuggerConnected()) {
sb.append("Debugger: Connected\n");
@@ -9335,7 +9345,7 @@
pw.println(" mFgsStartTempAllowList:");
final long currentTimeNow = System.currentTimeMillis();
final long elapsedRealtimeNow = SystemClock.elapsedRealtime();
- final Set<Integer> uids = mFgsStartTempAllowList.keySet();
+ final Set<Integer> uids = new ArraySet<>(mFgsStartTempAllowList.keySet());
for (Integer uid : uids) {
final Pair<Long, FgsTempAllowListItem> entry = mFgsStartTempAllowList.get(uid);
if (entry == null) {
@@ -14621,7 +14631,9 @@
String reason, @TempAllowListType int type, int callingUid) {
synchronized (mProcLock) {
// The temp allowlist type could change according to the reasonCode.
- type = mLocalDeviceIdleController.getTempAllowListType(reasonCode, type);
+ if (mLocalDeviceIdleController != null) {
+ type = mLocalDeviceIdleController.getTempAllowListType(reasonCode, type);
+ }
if (type == TEMPORARY_ALLOW_LIST_TYPE_NONE) {
return;
}
@@ -16739,19 +16751,20 @@
}
@Override
- public int checkOperation(int code, int uid, String packageName, boolean raw,
- QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) {
+ public int checkOperation(int code, int uid, String packageName,
+ String attributionTag, boolean raw,
+ QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) {
if (uid == mTargetUid && isTargetOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, shellUid, "com.android.shell", raw);
+ return superImpl.apply(code, shellUid, "com.android.shell", null, raw);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(code, uid, packageName, raw);
+ return superImpl.apply(code, uid, packageName, attributionTag, raw);
}
@Override
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 074e8fe..74094e5 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1627,7 +1627,7 @@
dropBuilder.append(catSw.toString());
FrameworkStatsLog.write(FrameworkStatsLog.LOW_MEM_REPORTED);
mService.addErrorToDropBox("lowmem", null, "system_server", null,
- null, null, tag.toString(), dropBuilder.toString(), null, null, null, null);
+ null, null, tag.toString(), dropBuilder.toString(), null, null, null, null, null);
synchronized (mService) {
long now = SystemClock.uptimeMillis();
if (mLastMemUsageReportTime < now) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 5937a18..610884f 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -468,7 +468,7 @@
synchronized (mStats) {
mStats.notePowerSaveModeLocked(
powerMgr.getLowPowerState(ServiceType.BATTERY_STATS).batterySaverEnabled,
- SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
+ SystemClock.elapsedRealtime(), SystemClock.uptimeMillis(), true);
}
(new WakeupReasonThread()).start();
}
@@ -511,7 +511,7 @@
mHandler.post(() -> {
synchronized (mStats) {
mStats.notePowerSaveModeLocked(result.batterySaverEnabled,
- elapsedRealtime, uptime);
+ elapsedRealtime, uptime, false);
}
});
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index f0b116c..de2c11b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -828,6 +828,7 @@
} else {
r.receiverTime = SystemClock.uptimeMillis();
maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+ maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
@@ -911,9 +912,16 @@
return false;
}
- final void scheduleTempAllowlistLocked(int uid, long duration, BroadcastRecord r,
- @TempAllowListType int type, @ReasonCode int reasonCode,
- @Nullable String reason) {
+ void maybeScheduleTempAllowlistLocked(int uid, BroadcastRecord r,
+ @Nullable BroadcastOptions brOptions) {
+ if (brOptions == null || brOptions.getTemporaryAppAllowlistDuration() <= 0) {
+ return;
+ }
+ long duration = brOptions.getTemporaryAppAllowlistDuration();
+ final @TempAllowListType int type = brOptions.getTemporaryAppAllowlistType();
+ final @ReasonCode int reasonCode = brOptions.getTemporaryAppAllowlistReasonCode();
+ final String reason = brOptions.getTemporaryAppAllowlistReason();
+
if (duration > Integer.MAX_VALUE) {
duration = Integer.MAX_VALUE;
}
@@ -1344,13 +1352,6 @@
// r is guaranteed ordered at this point, so we know finishReceiverLocked()
// will get a callback and handle the activity start token lifecycle.
}
- if (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0) {
- scheduleTempAllowlistLocked(filter.owningUid,
- brOptions.getTemporaryAppAllowlistDuration(), r,
- brOptions.getTemporaryAppAllowlistType(),
- brOptions.getTemporaryAppAllowlistReasonCode(),
- brOptions.getTemporaryAppAllowlistReason());
- }
}
return;
}
@@ -1646,16 +1647,9 @@
+ info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ receiverUid);
}
-
final boolean isActivityCapable =
(brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0);
- if (isActivityCapable) {
- scheduleTempAllowlistLocked(receiverUid,
- brOptions.getTemporaryAppAllowlistDuration(), r,
- brOptions.getTemporaryAppAllowlistType(),
- brOptions.getTemporaryAppAllowlistReasonCode(),
- brOptions.getTemporaryAppAllowlistReason());
- }
+ maybeScheduleTempAllowlistLocked(receiverUid, r, brOptions);
// Report that a component is used for explicit broadcasts.
if (!r.intent.isExcludingStopped() && r.curComponent != null
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index cc750ce..4629d0b 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -838,7 +838,7 @@
KEY_FREEZER_DEBOUNCE_TIMEOUT, DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
if (mFreezerDebounceTimeout < 0) {
- mFullDeltaRssThrottleKb = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
+ mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
}
}
@@ -881,7 +881,10 @@
@GuardedBy({"mAm", "mProcLock"})
void freezeAppAsyncLSP(ProcessRecord app) {
- mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
+ if (mFreezeHandler.hasMessages(SET_FROZEN_PROCESS_MSG, app)) {
+ // Skip redundant DO_FREEZE message
+ return;
+ }
mFreezeHandler.sendMessageDelayed(
mFreezeHandler.obtainMessage(
diff --git a/services/core/java/com/android/server/am/ErrorDialogController.java b/services/core/java/com/android/server/am/ErrorDialogController.java
index f23d309..a4e8f92 100644
--- a/services/core/java/com/android/server/am/ErrorDialogController.java
+++ b/services/core/java/com/android/server/am/ErrorDialogController.java
@@ -115,7 +115,7 @@
return;
}
if (needDismiss) {
- forAllDialogs(mCrashDialogs, Dialog::dismiss);
+ scheduleForAllDialogs(mCrashDialogs, Dialog::dismiss);
}
mCrashDialogs = null;
}
@@ -125,7 +125,7 @@
if (mAnrDialogs == null) {
return;
}
- forAllDialogs(mAnrDialogs, Dialog::dismiss);
+ scheduleForAllDialogs(mAnrDialogs, Dialog::dismiss);
mAnrDialogs = null;
mAnrController = null;
}
@@ -135,7 +135,7 @@
if (mViolationDialogs == null) {
return;
}
- forAllDialogs(mViolationDialogs, Dialog::dismiss);
+ scheduleForAllDialogs(mViolationDialogs, Dialog::dismiss);
mViolationDialogs = null;
}
@@ -148,6 +148,16 @@
mWaitDialog = null;
}
+ @GuardedBy("mProcLock")
+ void scheduleForAllDialogs(List<? extends BaseErrorDialog> dialogs,
+ Consumer<BaseErrorDialog> c) {
+ mService.mUiHandler.post(() -> {
+ if (dialogs != null) {
+ forAllDialogs(dialogs, c);
+ }
+ });
+ }
+
void forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c) {
for (int i = dialogs.size() - 1; i >= 0; i--) {
c.accept(dialogs.get(i));
@@ -182,15 +192,7 @@
final Context c = contexts.get(i);
mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data));
}
- mService.mUiHandler.post(() -> {
- List<AppNotRespondingDialog> dialogs;
- synchronized (mProcLock) {
- dialogs = mAnrDialogs;
- }
- if (dialogs != null) {
- forAllDialogs(dialogs, Dialog::show);
- }
- });
+ scheduleForAllDialogs(mAnrDialogs, Dialog::show);
}
@GuardedBy("mProcLock")
@@ -202,15 +204,7 @@
mViolationDialogs.add(
new StrictModeViolationDialog(c, mService, res, mApp));
}
- mService.mUiHandler.post(() -> {
- List<StrictModeViolationDialog> dialogs;
- synchronized (mProcLock) {
- dialogs = mViolationDialogs;
- }
- if (dialogs != null) {
- forAllDialogs(dialogs, Dialog::show);
- }
- });
+ scheduleForAllDialogs(mViolationDialogs, Dialog::show);
}
@GuardedBy("mProcLock")
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 40db086..4ad9909 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -394,6 +394,10 @@
mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
final int pid = msg.arg1;
final int group = msg.arg2;
+ if (pid == ActivityManagerService.MY_PID) {
+ // Skip setting the process group for system_server, keep it as default.
+ return true;
+ }
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup "
+ msg.obj + " to " + group);
@@ -507,6 +511,7 @@
final ProcessRecord topApp = mService.getTopApp();
final ProcessStateRecord state = app.mState;
final boolean wasCached = state.isCached();
+ final int oldCap = state.getSetCapability();
mAdjSeq++;
@@ -522,6 +527,7 @@
SystemClock.uptimeMillis());
if (oomAdjAll
&& (wasCached != state.isCached()
+ || oldCap != state.getSetCapability()
|| state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
// Changed to/from cached state, so apps after it in the LRU
// list may also be changed.
@@ -659,6 +665,7 @@
? oldAdj : ProcessList.UNKNOWN_ADJ;
final boolean wasBackground = ActivityManager.isProcStateBackground(
state.getSetProcState());
+ final int oldCap = state.getSetCapability();
state.setContainsCycle(false);
state.setProcStateChanged(false);
state.resetCachedInfo();
@@ -667,6 +674,7 @@
boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp, false,
SystemClock.uptimeMillis());
if (!success || (wasCached == state.isCached() && oldAdj != ProcessList.INVALID_ADJ
+ && oldCap == state.getCurCapability()
&& wasBackground == ActivityManager.isProcStateBackground(
state.getSetProcState()))) {
// Okay, it's unchanged, it won't impact any service it binds to, we're done here.
@@ -681,20 +689,62 @@
// Next to find out all its reachable processes
ArrayList<ProcessRecord> processes = mTmpProcessList;
ActiveUids uids = mTmpUidRecords;
- ArrayDeque<ProcessRecord> queue = mTmpQueue;
+ mPendingProcessSet.add(app);
+ boolean containsCycle = collectReachableProcessesLocked(mPendingProcessSet,
+ processes, uids);
+
+ // Clear the pending set as they should've been included in 'processes'.
+ mPendingProcessSet.clear();
+ // Reset the flag
+ state.setReachable(false);
+ // Remove this app from the return list because we've done the computation on it.
+ processes.remove(app);
+
+ int size = processes.size();
+ if (size > 0) {
+ mAdjSeq--;
+ // Update these reachable processes
+ updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, containsCycle, false);
+ } else if (state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ) {
+ // In case the app goes from non-cached to cached but it doesn't have other reachable
+ // processes, its adj could be still unknown as of now, assign one.
+ processes.add(app);
+ assignCachedAdjIfNecessary(processes);
+ applyOomAdjLSP(app, false, SystemClock.uptimeMillis(),
+ SystemClock.elapsedRealtime());
+ }
+ mTmpProcessList.clear();
+ mService.mOomAdjProfiler.oomAdjEnded();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ return true;
+ }
+
+ @GuardedBy("mService")
+ private boolean collectReachableProcessesLocked(ArraySet<ProcessRecord> apps,
+ ArrayList<ProcessRecord> processes, ActiveUids uids) {
+ final ArrayDeque<ProcessRecord> queue = mTmpQueue;
+ queue.clear();
+ for (int i = 0, size = apps.size(); i < size; i++) {
+ final ProcessRecord app = apps.valueAt(i);
+ app.mState.setReachable(true);
+ queue.offer(app);
+ }
+
+ return collectReachableProcessesLocked(queue, processes, uids);
+ }
+
+ @GuardedBy("mService")
+ private boolean collectReachableProcessesLocked(ArrayDeque<ProcessRecord> queue,
+ ArrayList<ProcessRecord> processes, ActiveUids uids) {
processes.clear();
uids.clear();
- queue.clear();
// Track if any of them reachables could include a cycle
boolean containsCycle = false;
// Scan downstreams of the process record
- state.setReachable(true);
- for (ProcessRecord pr = app; pr != null; pr = queue.poll()) {
- if (pr != app) {
- processes.add(pr);
- }
+ for (ProcessRecord pr = queue.poll(); pr != null; pr = queue.poll()) {
+ processes.add(pr);
final UidRecord uidRec = pr.getUidRecord();
if (uidRec != null) {
uids.put(uidRec.getUid(), uidRec);
@@ -719,10 +769,6 @@
}
queue.offer(service);
service.mState.setReachable(true);
- // During scanning the reachable dependants, remove them from the pending oomadj
- // targets list if it's possible, as they've been added into the immediate
- // oomadj targets list 'processes' above.
- mPendingProcessSet.remove(service);
}
final ProcessProviderRecord ppr = pr.mProviders;
for (int i = ppr.numberOfProviderConnections() - 1; i >= 0; i--) {
@@ -738,15 +784,9 @@
}
queue.offer(provider);
provider.mState.setReachable(true);
- // During scanning the reachable dependants, remove them from the pending oomadj
- // targets list if it's possible, as they've been added into the immediate
- // oomadj targets list 'processes' above.
- mPendingProcessSet.remove(provider);
}
}
- // Reset the flag
- state.setReachable(false);
int size = processes.size();
if (size > 0) {
// Reverse the process list, since the updateOomAdjInnerLSP scans from the end of it.
@@ -755,21 +795,8 @@
processes.set(l, processes.get(r));
processes.set(r, t);
}
- mAdjSeq--;
- // Update these reachable processes
- updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, containsCycle, false);
- } else if (state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ) {
- // In case the app goes from non-cached to cached but it doesn't have other reachable
- // processes, its adj could be still unknown as of now, assign one.
- processes.add(app);
- assignCachedAdjIfNecessary(processes);
- applyOomAdjLSP(app, false, SystemClock.uptimeMillis(),
- SystemClock.elapsedRealtime());
}
- mTmpProcessList.clear();
- mService.mOomAdjProfiler.oomAdjEnded();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- return true;
+ return containsCycle;
}
/**
@@ -853,22 +880,12 @@
final ArrayList<ProcessRecord> processes = mTmpProcessList;
final ActiveUids uids = mTmpUidRecords;
+ collectReachableProcessesLocked(mPendingProcessSet, processes, uids);
+ mPendingProcessSet.clear();
synchronized (mProcLock) {
- uids.clear();
- processes.clear();
- for (int i = mPendingProcessSet.size() - 1; i >= 0; i--) {
- final ProcessRecord app = mPendingProcessSet.valueAt(i);
- final UidRecord uidRec = app.getUidRecord();
- if (uidRec != null) {
- uids.put(uidRec.getUid(), uidRec);
- }
- processes.add(app);
- }
-
updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, true, false);
}
processes.clear();
- mPendingProcessSet.clear();
mService.mOomAdjProfiler.oomAdjEnded();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index a33e7e5..408c7cb 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -171,7 +171,7 @@
return mFreezeExempt;
}
- @GuardedBy("mPreLock")
+ @GuardedBy("mProcLock")
void setFreezeExempt(boolean exempt) {
mFreezeExempt = exempt;
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 88bd010..66d4779 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -55,7 +55,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
-
+import java.util.UUID;
/**
* The error state of the process, such as if it's crashing/ANR etc.
*/
@@ -235,6 +235,7 @@
final boolean isSilentAnr;
final int pid = mApp.getPid();
+ final UUID errorId;
synchronized (mService) {
// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
if (mService.mAtmInternal.isShuttingDown()) {
@@ -264,6 +265,13 @@
EventLog.writeEvent(EventLogTags.AM_ANR, mApp.userId, pid, mApp.processName,
mApp.info.flags, annotation);
+ if (mService.mTraceErrorLogger.isAddErrorIdEnabled()) {
+ errorId = mService.mTraceErrorLogger.generateErrorId();
+ mService.mTraceErrorLogger.addErrorIdToTrace(errorId);
+ } else {
+ errorId = null;
+ }
+
// Dump thread traces as quickly as we can, starting with "interesting" processes.
firstPids.add(pid);
@@ -315,6 +323,9 @@
&& parentShortComponentName.equals(activityShortComponentName)) {
info.append("Parent: ").append(parentShortComponentName).append("\n");
}
+ if (errorId != null) {
+ info.append("ErrorId: ").append(errorId.toString()).append("\n");
+ }
// Retrieve controller with max ANR delay from AnrControllers
// Note that we retrieve the controller before dumping stacks because dumping stacks can
@@ -457,7 +468,7 @@
? (ProcessRecord) parentProcess.mOwner : null;
mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
parentShortComponentName, parentPr, annotation, report.toString(), tracesFile,
- null, new Float(loadingProgress), incrementalMetrics);
+ null, new Float(loadingProgress), incrementalMetrics, errorId);
if (mApp.getWindowProcessController().appNotResponding(info.toString(),
() -> {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 0d19209e..0ffaccf 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -802,7 +802,7 @@
mAppDataIsolationEnabled =
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mAppDataIsolationAllowlistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
diff --git a/services/core/java/com/android/server/am/TraceErrorLogger.java b/services/core/java/com/android/server/am/TraceErrorLogger.java
new file mode 100644
index 0000000..f055be2
--- /dev/null
+++ b/services/core/java/com/android/server/am/TraceErrorLogger.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.os.Trace;
+import android.provider.DeviceConfig;
+
+import java.util.UUID;
+
+/**
+ * Adds a unique id to a trace.
+ *
+ * @hide
+ */
+class TraceErrorLogger {
+ private static final String COUNTER_PREFIX = "ErrorId:";
+ private static final String ADD_ERROR_ID = "add_error_id";
+ private static final int PLACEHOLDER_VALUE = 1;
+
+ public boolean isAddErrorIdEnabled() {
+ return DeviceConfig
+ .getBoolean(DeviceConfig.NAMESPACE_TRACE_ERROR_LOGGER, ADD_ERROR_ID,
+ false);
+ }
+
+ /**
+ * Generates a unique id with which to tag a trace.
+ */
+ public UUID generateErrorId() {
+ return UUID.randomUUID();
+ }
+
+ /**
+ * Pushes a counter containing a unique id and a label {@link #COUNTER_PREFIX} so that traces
+ * can be uniquely identified. We also add the same id to the dropbox entry of the error, so
+ * that we can join the trace and the error server-side.
+ *
+ * @param errorId The unique id with which to tag the trace.
+ */
+ public void addErrorIdToTrace(UUID errorId) {
+ Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER, COUNTER_PREFIX + errorId.toString(),
+ PLACEHOLDER_VALUE);
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 78ff67a..535fb67 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -220,9 +220,9 @@
final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
final UserLevelState pkgState = packageStates.get(packageName);
if (pkgState == null) {
- throw new IllegalArgumentException(
- String.format("Package %s is not installed for user %s",
- packageName, userId));
+ Slog.e(TAG, String.format("Package %s is not installed for user %s",
+ packageName, userId));
+ return false;
}
return pkgState.hibernated;
}
@@ -275,9 +275,9 @@
final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
final UserLevelState pkgState = packageStates.get(packageName);
if (pkgState == null) {
- throw new IllegalArgumentException(
- String.format("Package %s is not installed for user %s",
- packageName, userId));
+ Slog.e(TAG, String.format("Package %s is not installed for user %s",
+ packageName, userId));
+ return;
}
if (pkgState.hibernated == isHibernating) {
@@ -296,9 +296,7 @@
FrameworkStatsLog.USER_LEVEL_HIBERNATION_STATE_CHANGED,
stateSnapshot.packageName,
userIdSnapshot,
- stateSnapshot.hibernated,
- // TODO(b/187224817): This isn't the expected value right now.
- stateSnapshot.lastUnhibernatedMs);
+ stateSnapshot.hibernated);
});
List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
@@ -322,8 +320,8 @@
synchronized (mLock) {
GlobalLevelState state = mGlobalHibernationStates.get(packageName);
if (state == null) {
- throw new IllegalArgumentException(
- String.format("Package %s is not installed for any user", packageName));
+ Slog.e(TAG, String.format("Package %s is not installed for any user", packageName));
+ return;
}
if (state.hibernated != isHibernating) {
if (isHibernating) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 99b0d81..b6aec836 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -30,6 +30,7 @@
import static android.app.AppOpsManager.KEY_TOP_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.NoteOpEvent;
@@ -827,6 +828,14 @@
@GuardedBy("AppOpsService.this")
private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents;
+ /**
+ * Currently paused startOp events
+ *
+ * <p>Key is clientId
+ */
+ @GuardedBy("AppOpsService.this")
+ private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mPausedInProgressEvents;
+
AttributedOp(@Nullable String tag, @NonNull Op parent) {
this.tag = tag;
this.parent = parent;
@@ -944,23 +953,36 @@
@Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
@AppOpsManager.UidState int uidState, @OpFlags int flags,
boolean triggerCallbackIfNeeded) throws RemoteException {
- if (triggerCallbackIfNeeded && !parent.isRunning()) {
+ startedOrPaused(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
+ uidState, flags, triggerCallbackIfNeeded, true);
+ }
+
+ private void startedOrPaused(@NonNull IBinder clientId, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @OpFlags int flags,
+ boolean triggerCallbackIfNeeded, boolean isStarted) throws RemoteException {
+ if (triggerCallbackIfNeeded && !parent.isRunning() && isStarted) {
scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
parent.packageName, true);
}
- if (mInProgressEvents == null) {
+
+ if (isStarted && mInProgressEvents == null) {
mInProgressEvents = new ArrayMap<>(1);
+ } else if (mPausedInProgressEvents == null) {
+ mPausedInProgressEvents = new ArrayMap<>(1);
}
+ ArrayMap<IBinder, InProgressStartOpEvent> events = isStarted
+ ? mInProgressEvents : mPausedInProgressEvents;
long startTime = System.currentTimeMillis();
- InProgressStartOpEvent event = mInProgressEvents.get(clientId);
+ InProgressStartOpEvent event = events.get(clientId);
if (event == null) {
event = mInProgressStartOpEventPool.acquire(startTime,
SystemClock.elapsedRealtime(), clientId,
PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags);
- mInProgressEvents.put(clientId, event);
+ events.put(clientId, event);
} else {
if (uidState != event.mUidState) {
onUidStateChanged(uidState);
@@ -969,12 +991,15 @@
event.numUnfinishedStarts++;
- mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
- tag, uidState, flags, startTime);
+ if (isStarted) {
+ mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
+ parent.packageName, tag, uidState, flags, startTime);
+ }
+
}
/**
- * Update state when finishOp was called
+ * Update state when finishOp was called. Will finish started ops, and delete paused ops.
*
* @param clientId Id of the finishOp caller
*/
@@ -983,22 +1008,32 @@
}
private void finished(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded) {
- if (mInProgressEvents == null) {
- Slog.wtf(TAG, "No ops running");
- return;
- }
+ finishOrPause(clientId, triggerCallbackIfNeeded, false);
+ }
- int indexOfToken = mInProgressEvents.indexOfKey(clientId);
+ /**
+ * Update state when paused or finished is called. If pausing, it records the op as
+ * stopping in the HistoricalRegistry, but does not delete it.
+ */
+ private void finishOrPause(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded,
+ boolean isPausing) {
+ int indexOfToken = mInProgressEvents != null
+ ? mInProgressEvents.indexOfKey(clientId) : -1;
if (indexOfToken < 0) {
- Slog.wtf(TAG, "No op running for the client");
+ finishPossiblyPaused(clientId, isPausing);
return;
}
InProgressStartOpEvent event = mInProgressEvents.valueAt(indexOfToken);
- event.numUnfinishedStarts--;
- if (event.numUnfinishedStarts == 0) {
- event.finish();
- mInProgressEvents.removeAt(indexOfToken);
+ if (!isPausing) {
+ event.numUnfinishedStarts--;
+ }
+ // If we are pausing, create a NoteOpEvent, but don't change the InProgress event
+ if (event.numUnfinishedStarts == 0 || isPausing) {
+ if (!isPausing) {
+ event.finish();
+ mInProgressEvents.removeAt(indexOfToken);
+ }
if (mAccessEvents == null) {
mAccessEvents = new LongSparseArray<>(1);
@@ -1018,20 +1053,112 @@
parent.packageName, tag, event.getUidState(),
event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration());
- mInProgressStartOpEventPool.release(event);
+ if (!isPausing) {
+ mInProgressStartOpEventPool.release(event);
+ if (mInProgressEvents.isEmpty()) {
+ mInProgressEvents = null;
- if (mInProgressEvents.isEmpty()) {
- mInProgressEvents = null;
-
- // TODO moltmann: Also callback for single attribution tag activity changes
- if (triggerCallbackIfNeeded && !parent.isRunning()) {
- scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
- parent.packageName, false);
+ // TODO ntmyren: Also callback for single attribution tag activity changes
+ if (triggerCallbackIfNeeded && !parent.isRunning()) {
+ scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, false);
+ }
}
}
}
}
+ // Finish or pause (no-op) an already paused op
+ private void finishPossiblyPaused(@NonNull IBinder clientId, boolean isPausing) {
+ if (mPausedInProgressEvents == null) {
+ Slog.wtf(TAG, "No ops running or paused");
+ return;
+ }
+
+ int indexOfToken = mPausedInProgressEvents.indexOfKey(clientId);
+ if (indexOfToken < 0) {
+ Slog.wtf(TAG, "No op running or paused for the client");
+ return;
+ } else if (isPausing) {
+ // already paused
+ return;
+ }
+
+ // no need to record a paused event finishing.
+ InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(indexOfToken);
+ event.numUnfinishedStarts--;
+ if (event.numUnfinishedStarts == 0) {
+ mPausedInProgressEvents.removeAt(indexOfToken);
+ mInProgressStartOpEventPool.release(event);
+ if (mPausedInProgressEvents.isEmpty()) {
+ mPausedInProgressEvents = null;
+ }
+ }
+ }
+
+ /**
+ * Create an event that will be started, if the op is unpaused.
+ */
+ public void createPaused(@NonNull IBinder clientId, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @OpFlags int flags) throws RemoteException {
+ startedOrPaused(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
+ uidState, flags, true, false);
+ }
+
+ /**
+ * Pause all currently started ops. This will create a HistoricalRegistry
+ */
+ public void pause() {
+ if (mInProgressEvents == null) {
+ return;
+ }
+
+ if (mPausedInProgressEvents == null) {
+ mPausedInProgressEvents = new ArrayMap<>(1);
+ }
+
+ for (int i = 0; i < mInProgressEvents.size(); i++) {
+ InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
+ mPausedInProgressEvents.put(event.mClientId, event);
+ finishOrPause(event.mClientId, true, true);
+ }
+ scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, false);
+ mInProgressEvents = null;
+ }
+
+ /**
+ * Unpause all currently paused ops. This will reinitialize their start and duration
+ * times, but keep all other values the same
+ */
+ public void resume() {
+ if (mPausedInProgressEvents == null) {
+ return;
+ }
+
+ if (mInProgressEvents == null) {
+ mInProgressEvents = new ArrayMap<>(mPausedInProgressEvents.size());
+ }
+ boolean shouldSendActive = !mPausedInProgressEvents.isEmpty()
+ && mInProgressEvents.isEmpty();
+
+ long startTime = System.currentTimeMillis();
+ for (int i = 0; i < mPausedInProgressEvents.size(); i++) {
+ InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(i);
+ mInProgressEvents.put(event.mClientId, event);
+ event.mStartElapsedTime = SystemClock.elapsedRealtime();
+ event.mStartTime = startTime;
+ mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
+ parent.packageName, tag, event.mUidState, event.mFlags, startTime);
+ }
+ if (shouldSendActive) {
+ scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, true);
+ }
+ mPausedInProgressEvents = null;
+ }
+
/**
* Called in the case the client dies without calling finish first
*
@@ -1155,6 +1282,10 @@
return mInProgressEvents != null;
}
+ public boolean isPaused() {
+ return mPausedInProgressEvents != null;
+ }
+
boolean hasAnyTime() {
return (mAccessEvents != null && mAccessEvents.size() > 0)
|| (mRejectEvents != null && mRejectEvents.size() > 0);
@@ -2303,6 +2434,9 @@
scheduleWriteLocked();
}
uidState.evalForegroundOps(mOpModeWatchers);
+ if (mode != MODE_ERRORED && mode != previousMode) {
+ updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
+ }
}
notifyOpChangedForAllPkgsInUid(code, uid, false, permissionPolicyCallback);
@@ -2556,6 +2690,9 @@
pruneOpLocked(op, uid, packageName);
}
scheduleFastWriteLocked();
+ if (mode != MODE_ERRORED) {
+ updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
+ }
}
}
}
@@ -2926,16 +3063,20 @@
}
@Override
- public int checkOperationRaw(int code, int uid, String packageName) {
- return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, true /*raw*/);
+ public int checkOperationRaw(int code, int uid, String packageName,
+ @Nullable String attributionTag) {
+ return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag,
+ true /*raw*/);
}
@Override
public int checkOperation(int code, int uid, String packageName) {
- return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, false /*raw*/);
+ return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null,
+ false /*raw*/);
}
- private int checkOperationImpl(int code, int uid, String packageName, boolean raw) {
+ private int checkOperationImpl(int code, int uid, String packageName,
+ @Nullable String attributionTag, boolean raw) {
verifyIncomingOp(code);
verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
@@ -2943,7 +3084,7 @@
if (resolvedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
- return checkOperationUnchecked(code, uid, resolvedPackageName, raw);
+ return checkOperationUnchecked(code, uid, resolvedPackageName, attributionTag, raw);
}
/**
@@ -2957,7 +3098,7 @@
* @return The mode of the op
*/
private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,
- boolean raw) {
+ @Nullable String attributionTag, boolean raw) {
RestrictionBypass bypass;
try {
bypass = verifyAndGetBypass(uid, packageName, null);
@@ -2970,7 +3111,7 @@
return AppOpsManager.MODE_IGNORED;
}
synchronized (this) {
- if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
+ if (isOpRestrictedLocked(uid, code, packageName, attributionTag, bypass)) {
return AppOpsManager.MODE_IGNORED;
}
code = AppOpsManager.opToSwitch(code);
@@ -3161,7 +3302,7 @@
boolean shouldCollectMessage) {
RestrictionBypass bypass;
try {
- bypass = verifyAndGetBypass(uid, packageName, attributionTag);
+ bypass = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
} catch (SecurityException e) {
Slog.e(TAG, "noteOperation", e);
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
@@ -3189,7 +3330,7 @@
final int switchCode = AppOpsManager.opToSwitch(code);
final UidState uidState = ops.uidState;
- if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
+ if (isOpRestrictedLocked(uid, code, packageName, attributionTag, bypass)) {
attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_IGNORED);
@@ -3653,13 +3794,14 @@
boolean shouldCollectMessage, boolean dryRun) {
RestrictionBypass bypass;
try {
- bypass = verifyAndGetBypass(uid, packageName, attributionTag);
+ bypass = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
} catch (SecurityException e) {
Slog.e(TAG, "startOperation", e);
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
+ boolean isRestricted = false;
synchronized (this) {
final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */);
if (ops == null) {
@@ -3673,18 +3815,10 @@
packageName);
}
final Op op = getOpLocked(ops, code, uid, true);
- if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
- if (!dryRun) {
- scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
- flags, AppOpsManager.MODE_IGNORED);
- }
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
- packageName);
- }
-
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
- final int switchCode = AppOpsManager.opToSwitch(code);
final UidState uidState = ops.uidState;
+ isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, bypass);
+ final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
@@ -3720,25 +3854,30 @@
}
}
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
- + " package " + packageName);
+ + " package " + packageName + " restricted: " + isRestricted);
if (!dryRun) {
- scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags,
- AppOpsManager.MODE_ALLOWED);
try {
- attributedOp.started(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
- uidState.state, flags);
+ if (isRestricted) {
+ attributedOp.createPaused(clientId, proxyUid, proxyPackageName,
+ proxyAttributionTag, uidState.state, flags);
+ } else {
+ attributedOp.started(clientId, proxyUid, proxyPackageName,
+ proxyAttributionTag, uidState.state, flags);
+ }
} catch (RemoteException e) {
throw new RuntimeException(e);
}
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags,
+ isRestricted ? MODE_IGNORED : MODE_ALLOWED);
}
}
- if (shouldCollectAsyncNotedOp && !dryRun) {
+ if (shouldCollectAsyncNotedOp && !dryRun && !isRestricted) {
collectAsyncNotedOp(uid, packageName, code, attributionTag, AppOpsManager.OP_FLAG_SELF,
message, shouldCollectMessage);
}
- return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
+ return new SyncNotedAppOp(isRestricted ? MODE_IGNORED : MODE_ALLOWED, code, attributionTag,
packageName);
}
@@ -3822,7 +3961,7 @@
return;
}
- if (attributedOp.isRunning()) {
+ if (attributedOp.isRunning() || attributedOp.isPaused()) {
attributedOp.finished(clientId);
} else {
Slog.e(TAG, "Operation not started: uid=" + uid + " pkg=" + packageName + "("
@@ -4187,17 +4326,26 @@
}
/**
+ * @see verifyAndGetBypass(int, String, String, String)
+ */
+ private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName,
+ @Nullable String attributionTag) {
+ return verifyAndGetBypass(uid, packageName, attributionTag, null);
+ }
+
+ /**
* Verify that package belongs to uid and return the {@link RestrictionBypass bypass
* description} for the package.
*
* @param uid The uid the package belongs to
* @param packageName The package the might belong to the uid
* @param attributionTag attribution tag or {@code null} if no need to verify
+ * @param proxyPackageName The proxy package, from which the attribution tag is to be pulled
*
* @return {@code true} iff the package is privileged
*/
private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName,
- @Nullable String attributionTag) {
+ @Nullable String attributionTag, @Nullable String proxyPackageName) {
if (uid == Process.ROOT_UID) {
// For backwards compatibility, don't check package name for root UID.
return null;
@@ -4235,34 +4383,36 @@
final long ident = Binder.clearCallingIdentity();
try {
boolean isAttributionTagValid = false;
- AndroidPackage pkg = LocalServices.getService(PackageManagerInternal.class)
- .getPackage(packageName);
+ PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+ AndroidPackage pkg = pmInt.getPackage(packageName);
if (pkg != null) {
- if (attributionTag == null) {
- isAttributionTagValid = true;
- } else {
- if (pkg.getAttributions() != null) {
- int numAttributions = pkg.getAttributions().size();
- for (int i = 0; i < numAttributions; i++) {
- if (pkg.getAttributions().get(i).tag.equals(attributionTag)) {
- isAttributionTagValid = true;
- }
- }
- }
- }
+ isAttributionTagValid = isAttributionInPackage(pkg, attributionTag);
pkgUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
bypass = getBypassforPackage(pkg);
}
if (!isAttributionTagValid) {
- String msg = "attributionTag " + attributionTag + " not declared in"
- + " manifest of " + packageName;
+ AndroidPackage proxyPkg = proxyPackageName != null
+ ? pmInt.getPackage(proxyPackageName) : null;
+ boolean foundInProxy = isAttributionInPackage(proxyPkg, attributionTag);
+ String msg;
+ if (pkg != null && foundInProxy) {
+ msg = "attributionTag " + attributionTag + " declared in manifest of the proxy"
+ + " package " + proxyPackageName + ", this is not advised";
+ } else if (pkg != null) {
+ msg = "attributionTag " + attributionTag + " not declared in manifest of "
+ + packageName;
+ } else {
+ msg = "package " + packageName + " not found, can't check for "
+ + "attributionTag " + attributionTag;
+ }
+
try {
if (mPlatformCompat.isChangeEnabledByPackageName(
SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName,
userId) && mPlatformCompat.isChangeEnabledByUid(
SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE,
- callingUid)) {
+ callingUid) && !foundInProxy) {
throw new SecurityException(msg);
} else {
Slog.e(TAG, msg);
@@ -4282,6 +4432,25 @@
return bypass;
}
+ private boolean isAttributionInPackage(@Nullable AndroidPackage pkg,
+ @Nullable String attributionTag) {
+ if (pkg == null) {
+ return false;
+ } else if (attributionTag == null) {
+ return true;
+ }
+ if (pkg.getAttributions() != null) {
+ int numAttributions = pkg.getAttributions().size();
+ for (int i = 0; i < numAttributions; i++) {
+ if (pkg.getAttributions().get(i).tag.equals(attributionTag)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
/**
* Get (and potentially create) ops.
*
@@ -4390,7 +4559,7 @@
}
private boolean isOpRestrictedLocked(int uid, int code, String packageName,
- @Nullable RestrictionBypass appBypass) {
+ String attributionTag, @Nullable RestrictionBypass appBypass) {
int userHandle = UserHandle.getUserId(uid);
final int restrictionSetCount = mOpUserRestrictions.size();
@@ -4398,7 +4567,7 @@
// For each client, check that the given op is not restricted, or that the given
// package is exempt from the restriction.
ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
- if (restrictionState.hasRestriction(code, packageName, userHandle)) {
+ if (restrictionState.hasRestriction(code, packageName, attributionTag, userHandle)) {
RestrictionBypass opBypass = opAllowSystemBypassRestriction(code);
if (opBypass != null) {
// If we are the system, bypass user restrictions for certain codes
@@ -6012,25 +6181,20 @@
}
}
- final int excludedPackageCount = restrictionState.perUserExcludedPackages != null
- ? restrictionState.perUserExcludedPackages.size() : 0;
+ final int excludedPackageCount = restrictionState.perUserExcludedPackageTags != null
+ ? restrictionState.perUserExcludedPackageTags.size() : 0;
if (excludedPackageCount > 0 && dumpOp < 0) {
boolean printedPackagesHeader = false;
for (int j = 0; j < excludedPackageCount; j++) {
- int userId = restrictionState.perUserExcludedPackages.keyAt(j);
- String[] packageNames = restrictionState.perUserExcludedPackages.valueAt(j);
+ int userId = restrictionState.perUserExcludedPackageTags.keyAt(j);
+ Map<String, String[]> packageNames =
+ restrictionState.perUserExcludedPackageTags.valueAt(j);
if (packageNames == null) {
continue;
}
boolean hasPackage;
if (dumpPackage != null) {
- hasPackage = false;
- for (String pkg : packageNames) {
- if (dumpPackage.equals(pkg)) {
- hasPackage = true;
- break;
- }
- }
+ hasPackage = packageNames.containsKey(dumpPackage);
} else {
hasPackage = true;
}
@@ -6045,8 +6209,24 @@
pw.println(" Excluded packages:");
printedPackagesHeader = true;
}
- pw.print(" "); pw.print("user: "); pw.print(userId);
- pw.print(" packages: "); pw.println(Arrays.toString(packageNames));
+ pw.print(" ");
+ pw.print("user: ");
+ pw.print(userId);
+ pw.println(" packages: ");
+ for (Map.Entry<String, String[]> entry : packageNames.entrySet()) {
+ if (entry.getValue() == null) {
+ continue;
+ }
+ pw.print(" ");
+ pw.print(entry.getKey());
+ pw.print(": ");
+ if (entry.getValue().length == 0) {
+ pw.print("*");
+ } else {
+ pw.print(Arrays.toString(entry.getValue()));
+ }
+ pw.println();
+ }
}
}
}
@@ -6080,7 +6260,7 @@
@Override
public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle,
- String[] exceptionPackages) {
+ Map<String, String[]> excludedPackageTags) {
if (Binder.getCallingPid() != Process.myPid()) {
mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
@@ -6096,11 +6276,11 @@
}
verifyIncomingOp(code);
Objects.requireNonNull(token);
- setUserRestrictionNoCheck(code, restricted, token, userHandle, exceptionPackages);
+ setUserRestrictionNoCheck(code, restricted, token, userHandle, excludedPackageTags);
}
private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
- int userHandle, String[] exceptionPackages) {
+ int userHandle, Map<String, String[]> excludedPackageTags) {
synchronized (AppOpsService.this) {
ClientRestrictionState restrictionState = mOpUserRestrictions.get(token);
@@ -6113,9 +6293,13 @@
mOpUserRestrictions.put(token, restrictionState);
}
- if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) {
+ if (restrictionState.setRestriction(code, restricted, excludedPackageTags,
+ userHandle)) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyWatchersOfChange, this, code, UID_ANY));
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::updateStartedOpModeForUser, this, code, restricted,
+ userHandle));
}
if (restrictionState.isDefault()) {
@@ -6125,6 +6309,44 @@
}
}
+ private void updateStartedOpModeForUser(int code, boolean restricted, int userId) {
+ synchronized (AppOpsService.this) {
+ int numUids = mUidStates.size();
+ for (int uidNum = 0; uidNum < numUids; uidNum++) {
+ int uid = mUidStates.keyAt(uidNum);
+ if (userId != UserHandle.USER_ALL && UserHandle.getUserId(uid) != userId) {
+ continue;
+ }
+ updateStartedOpModeForUidLocked(code, restricted, uid);
+ }
+ }
+ }
+
+ private void updateStartedOpModeForUidLocked(int code, boolean restricted, int uid) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null || uidState.pkgOps == null) {
+ return;
+ }
+
+ int numPkgOps = uidState.pkgOps.size();
+ for (int pkgNum = 0; pkgNum < numPkgOps; pkgNum++) {
+ Ops ops = uidState.pkgOps.valueAt(pkgNum);
+ Op op = ops != null ? ops.get(code) : null;
+ if (op == null || (op.mode != MODE_ALLOWED && op.mode != MODE_FOREGROUND)) {
+ continue;
+ }
+ int numAttrTags = op.mAttributions.size();
+ for (int attrNum = 0; attrNum < numAttrTags; attrNum++) {
+ AttributedOp attrOp = op.mAttributions.valueAt(attrNum);
+ if (restricted) {
+ attrOp.pause();
+ } else {
+ attrOp.resume();
+ }
+ }
+ }
+ }
+
private void notifyWatchersOfChange(int code, int uid) {
final ArraySet<ModeCallback> clonedCallbacks;
synchronized (this) {
@@ -6248,6 +6470,7 @@
"offsetHistory");
// Must not hold the appops lock
mHistoricalRegistry.offsetHistory(offsetMillis);
+ mHistoricalRegistry.offsetDiscreteHistory(offsetMillis);
}
@Override
@@ -6602,7 +6825,7 @@
private final class ClientRestrictionState implements DeathRecipient {
private final IBinder token;
SparseArray<boolean[]> perUserRestrictions;
- SparseArray<String[]> perUserExcludedPackages;
+ SparseArray<Map<String, String[]>> perUserExcludedPackageTags;
public ClientRestrictionState(IBinder token)
throws RemoteException {
@@ -6611,7 +6834,7 @@
}
public boolean setRestriction(int code, boolean restricted,
- String[] excludedPackages, int userId) {
+ Map<String, String[]> excludedPackageTags, int userId) {
boolean changed = false;
if (perUserRestrictions == null && restricted) {
@@ -6653,19 +6876,27 @@
}
if (userRestrictions != null) {
- final boolean noExcludedPackages = ArrayUtils.isEmpty(excludedPackages);
- if (perUserExcludedPackages == null && !noExcludedPackages) {
- perUserExcludedPackages = new SparseArray<>();
+ final boolean noExcludedPackages = ArrayUtils.isEmpty(excludedPackageTags);
+ if (perUserExcludedPackageTags == null && !noExcludedPackages) {
+ perUserExcludedPackageTags = new SparseArray<>();
}
- if (perUserExcludedPackages != null && !Arrays.equals(excludedPackages,
- perUserExcludedPackages.get(thisUserId))) {
+ if (perUserExcludedPackageTags != null) {
if (noExcludedPackages) {
- perUserExcludedPackages.remove(thisUserId);
- if (perUserExcludedPackages.size() <= 0) {
- perUserExcludedPackages = null;
+ perUserExcludedPackageTags.remove(thisUserId);
+ if (perUserExcludedPackageTags.size() <= 0) {
+ perUserExcludedPackageTags = null;
}
} else {
- perUserExcludedPackages.put(thisUserId, excludedPackages);
+ Map<String, String[]> userExcludedPackageTags =
+ perUserExcludedPackageTags.get(thisUserId);
+ if (userExcludedPackageTags == null) {
+ userExcludedPackageTags = new ArrayMap<>(
+ excludedPackageTags.size());
+ perUserExcludedPackageTags.put(thisUserId,
+ userExcludedPackageTags);
+ }
+ userExcludedPackageTags.clear();
+ userExcludedPackageTags.putAll(excludedPackageTags);
}
changed = true;
}
@@ -6676,7 +6907,8 @@
return changed;
}
- public boolean hasRestriction(int restriction, String packageName, int userId) {
+ public boolean hasRestriction(int restriction, String packageName, String attributionTag,
+ int userId) {
if (perUserRestrictions == null) {
return false;
}
@@ -6687,21 +6919,29 @@
if (!restrictions[restriction]) {
return false;
}
- if (perUserExcludedPackages == null) {
+ if (perUserExcludedPackageTags == null) {
return true;
}
- String[] perUserExclusions = perUserExcludedPackages.get(userId);
+ Map<String, String[]> perUserExclusions = perUserExcludedPackageTags.get(userId);
if (perUserExclusions == null) {
return true;
}
- return !ArrayUtils.contains(perUserExclusions, packageName);
+ String[] excludedTags = perUserExclusions.get(packageName);
+ if (excludedTags == null) {
+ return true;
+ }
+ if (excludedTags.length == 0) {
+ // all attribution tags within the package are excluded
+ return false;
+ }
+ return !ArrayUtils.contains(excludedTags, attributionTag);
}
public void removeUser(int userId) {
- if (perUserExcludedPackages != null) {
- perUserExcludedPackages.remove(userId);
- if (perUserExcludedPackages.size() <= 0) {
- perUserExcludedPackages = null;
+ if (perUserExcludedPackageTags != null) {
+ perUserExcludedPackageTags.remove(userId);
+ if (perUserExcludedPackageTags.size() <= 0) {
+ perUserExcludedPackageTags = null;
}
}
if (perUserRestrictions != null) {
@@ -6933,23 +7173,25 @@
return mCheckOpsDelegate;
}
- public int checkOperation(int code, int uid, String packageName, boolean raw) {
+ public int checkOperation(int code, int uid, String packageName,
+ @Nullable String attributionTag, boolean raw) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
- return mPolicy.checkOperation(code, uid, packageName, raw,
+ return mPolicy.checkOperation(code, uid, packageName, attributionTag, raw,
this::checkDelegateOperationImpl);
} else {
- return mPolicy.checkOperation(code, uid, packageName, raw,
+ return mPolicy.checkOperation(code, uid, packageName, attributionTag, raw,
AppOpsService.this::checkOperationImpl);
}
} else if (mCheckOpsDelegate != null) {
- return checkDelegateOperationImpl(code, uid, packageName, raw);
+ return checkDelegateOperationImpl(code, uid, packageName, attributionTag, raw);
}
- return checkOperationImpl(code, uid, packageName, raw);
+ return checkOperationImpl(code, uid, packageName, attributionTag, raw);
}
- private int checkDelegateOperationImpl(int code, int uid, String packageName, boolean raw) {
- return mCheckOpsDelegate.checkOperation(code, uid, packageName, raw,
+ private int checkDelegateOperationImpl(int code, int uid, String packageName,
+ @Nullable String attributionTag, boolean raw) {
+ return mCheckOpsDelegate.checkOperation(code, uid, packageName, attributionTag, raw,
AppOpsService.this::checkOperationImpl);
}
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index fc48b3f..99daa24 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -27,6 +27,8 @@
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
import static android.app.AppOpsManager.OP_NONE;
+import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
+import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.flagsToString;
import static android.app.AppOpsManager.getUidStateName;
@@ -122,7 +124,8 @@
private static final String PROPERTY_DISCRETE_FLAGS = "discrete_history_op_flags";
private static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist";
private static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
- + "," + OP_CAMERA + "," + OP_RECORD_AUDIO;
+ + "," + OP_CAMERA + "," + OP_RECORD_AUDIO + "," + OP_PHONE_CALL_MICROPHONE + ","
+ + OP_PHONE_CALL_CAMERA;
private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION =
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 35e8d34..0ab6c4a 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -212,6 +212,7 @@
}
void systemReady(@NonNull ContentResolver resolver) {
+ mDiscreteRegistry.systemReady();
final Uri uri = Settings.Global.getUriFor(Settings.Global.APPOP_HISTORY_PARAMETERS);
resolver.registerContentObserver(uri, false, new ContentObserver(
FgThread.getHandler()) {
@@ -249,7 +250,6 @@
}
}
}
- mDiscreteRegistry.systemReady();
}
private boolean isPersistenceInitializedMLocked() {
@@ -594,6 +594,9 @@
mPersistence.persistHistoricalOpsDLocked(history);
}
}
+ }
+
+ void offsetDiscreteHistory(long offsetMillis) {
mDiscreteRegistry.offsetHistory(offsetMillis);
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 96bb73f..8961a5a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1034,6 +1034,11 @@
}
}
+ /*package*/ void clearAvrcpAbsoluteVolumeSupported() {
+ setAvrcpAbsoluteVolumeSupported(false);
+ mAudioService.setAvrcpAbsoluteVolumeSupported(false);
+ }
+
/*package*/ boolean getBluetoothA2dpEnabled() {
synchronized (mDeviceStateLock) {
return mBluetoothA2dpEnabled;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 18d04e9..5944a63 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1015,7 +1015,7 @@
}
// device to remove was visible by APM, update APM
- mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false);
+ mDeviceBroker.clearAvrcpAbsoluteVolumeSupported();
final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 098ce7c..136916a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -413,6 +413,26 @@
AudioSystem.STREAM_MUSIC, // STREAM_ACCESSIBILITY
AudioSystem.STREAM_MUSIC // STREAM_ASSISTANT
};
+ /**
+ * Using Volume groups configuration allows to control volume per attributes
+ * and group definition may differ from stream aliases.
+ * So, do not alias any stream on one another when using volume groups.
+ * TODO(b/181140246): volume group definition hosting alias definition.
+ */
+ private final int[] STREAM_VOLUME_ALIAS_NONE = new int[] {
+ AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
+ AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM
+ AudioSystem.STREAM_RING, // STREAM_RING
+ AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
+ AudioSystem.STREAM_ALARM, // STREAM_ALARM
+ AudioSystem.STREAM_NOTIFICATION, // STREAM_NOTIFICATION
+ AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
+ AudioSystem.STREAM_SYSTEM_ENFORCED, // STREAM_SYSTEM_ENFORCED
+ AudioSystem.STREAM_DTMF, // STREAM_DTMF
+ AudioSystem.STREAM_TTS, // STREAM_TTS
+ AudioSystem.STREAM_ACCESSIBILITY, // STREAM_ACCESSIBILITY
+ AudioSystem.STREAM_ASSISTANT // STREAM_ASSISTANT
+ };
private final int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_RING, // STREAM_SYSTEM
@@ -428,6 +448,7 @@
AudioSystem.STREAM_MUSIC // STREAM_ASSISTANT
};
protected static int[] mStreamVolumeAlias;
+ private static final int UNSET_INDEX = -1;
/**
* Map AudioSystem.STREAM_* constants to app ops. This should be used
@@ -449,6 +470,7 @@
};
private final boolean mUseFixedVolume;
+ private final boolean mUseVolumeGroupAliases;
// If absolute volume is supported in AVRCP device
private volatile boolean mAvrcpAbsVolSupported = false;
@@ -849,6 +871,9 @@
mSupportsMicPrivacyToggle = context.getSystemService(SensorPrivacyManager.class)
.supportsSensorToggle(SensorPrivacyManager.Sensors.MICROPHONE);
+ mUseVolumeGroupAliases = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_handleVolumeAliasesUsingVolumeGroups);
+
// Initialize volume
// Priority 1 - Android Property
// Priority 2 - Audio Policy Service
@@ -869,6 +894,12 @@
MIN_STREAM_VOLUME[streamType] = minVolume;
}
}
+ if (mUseVolumeGroupAliases) {
+ // Set all default to uninitialized.
+ for (int stream = 0; stream < AudioSystem.DEFAULT_STREAM_VOLUME.length; stream++) {
+ AudioSystem.DEFAULT_STREAM_VOLUME[stream] = UNSET_INDEX;
+ }
+ }
}
int maxCallVolume = SystemProperties.getInt("ro.config.vc_call_vol_steps", -1);
@@ -1705,18 +1736,42 @@
updateDefaultVolumes();
}
- // Update default indexes from aliased streams. Must be called after mStreamStates is created
+ /**
+ * Update default indexes from aliased streams. Must be called after mStreamStates is created
+ * TODO(b/181140246): when using VolumeGroup alias, we are lacking configurability for default
+ * index. Need to make default index configurable and independent of streams.
+ * Fallback on music stream for default initialization to take benefit of property based default
+ * initialization.
+ * For other volume groups not linked to any streams, default music stream index is considered.
+ */
private void updateDefaultVolumes() {
for (int stream = 0; stream < mStreamStates.length; stream++) {
- if (stream != mStreamVolumeAlias[stream]) {
- AudioSystem.DEFAULT_STREAM_VOLUME[stream] = (rescaleIndex(
- AudioSystem.DEFAULT_STREAM_VOLUME[mStreamVolumeAlias[stream]] * 10,
- mStreamVolumeAlias[stream],
- stream) + 5) / 10;
+ int streamVolumeAlias = mStreamVolumeAlias[stream];
+ if (mUseVolumeGroupAliases) {
+ if (AudioSystem.DEFAULT_STREAM_VOLUME[stream] != UNSET_INDEX) {
+ // Already initialized through default property based mecanism.
+ continue;
+ }
+ streamVolumeAlias = AudioSystem.STREAM_MUSIC;
+ int defaultAliasVolume = getUiDefaultRescaledIndex(streamVolumeAlias, stream);
+ if ((defaultAliasVolume >= MIN_STREAM_VOLUME[stream])
+ && (defaultAliasVolume <= MAX_STREAM_VOLUME[stream])) {
+ AudioSystem.DEFAULT_STREAM_VOLUME[stream] = defaultAliasVolume;
+ continue;
+ }
+ }
+ if (stream != streamVolumeAlias) {
+ AudioSystem.DEFAULT_STREAM_VOLUME[stream] =
+ getUiDefaultRescaledIndex(streamVolumeAlias, stream);
}
}
}
+ private int getUiDefaultRescaledIndex(int srcStream, int dstStream) {
+ return (rescaleIndex(AudioSystem.DEFAULT_STREAM_VOLUME[srcStream] * 10,
+ srcStream, dstStream) + 5) / 10;
+ }
+
private void dumpStreamStates(PrintWriter pw) {
pw.println("\nStream volumes (device: index)");
int numStreamTypes = AudioSystem.getNumStreamTypes();
@@ -1740,6 +1795,9 @@
if (mIsSingleVolume) {
mStreamVolumeAlias = STREAM_VOLUME_ALIAS_TELEVISION;
dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
+ } else if (mUseVolumeGroupAliases) {
+ mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NONE;
+ dtmfStreamAlias = AudioSystem.STREAM_DTMF;
} else {
switch (mPlatformType) {
case AudioSystem.PLATFORM_VOICE:
@@ -2768,7 +2826,7 @@
// If either the client forces allowing ringer modes for this adjustment,
// or the stream type is one that is affected by ringer modes
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
- (streamTypeAlias == getUiSoundsStreamType())) {
+ (isUiSoundsStreamType(streamTypeAlias))) {
int ringerMode = getRingerModeInternal();
// do not vibrate if already in vibrate mode
if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
@@ -3570,7 +3628,7 @@
case Settings.Global.ZEN_MODE_ALARMS:
case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
return !isStreamMutedByRingerOrZenMode(streamTypeAlias)
- || streamTypeAlias == getUiSoundsStreamType()
+ || isUiSoundsStreamType(streamTypeAlias)
|| (flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0;
}
@@ -3987,9 +4045,26 @@
return (mStreamStates[streamType].getIndex(device) + 5) / 10;
}
- /** @see AudioManager#getUiSoundsStreamType() */
+ /** @see AudioManager#getUiSoundsStreamType()
+ * TODO(b/181140246): when using VolumeGroup alias, we are lacking configurability for
+ * UI Sounds identification.
+ * Fallback on Voice configuration to ensure correct behavior of DnD feature.
+ */
public int getUiSoundsStreamType() {
- return mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM];
+ return mUseVolumeGroupAliases ? STREAM_VOLUME_ALIAS_VOICE[AudioSystem.STREAM_SYSTEM]
+ : mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM];
+ }
+
+ /**
+ * TODO(b/181140246): when using VolumeGroup alias, we are lacking configurability for
+ * UI Sounds identification.
+ * Fallback on Voice configuration to ensure correct behavior of DnD feature.
+ */
+ private boolean isUiSoundsStreamType(int aliasStreamType) {
+ return mUseVolumeGroupAliases
+ ? STREAM_VOLUME_ALIAS_VOICE[aliasStreamType]
+ == STREAM_VOLUME_ALIAS_VOICE[AudioSystem.STREAM_SYSTEM]
+ : aliasStreamType == mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM];
}
/** @see AudioManager#setMicrophoneMute(boolean) */
@@ -6535,6 +6610,10 @@
if (index == -1) {
continue;
}
+ if (mPublicStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED
+ && mCameraSoundForced) {
+ index = mIndexMax;
+ }
if (DEBUG_VOL) {
Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
+ " for group " + mAudioVolumeGroup.name() + ", device: " + name
@@ -7643,8 +7722,12 @@
// address is not used for now, but may be used when multiple a2dp devices are supported
sVolumeLogger.log(new AudioEventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
+ address + " support=" + support));
- mAvrcpAbsVolSupported = support;
mDeviceBroker.setAvrcpAbsoluteVolumeSupported(support);
+ setAvrcpAbsoluteVolumeSupported(support);
+ }
+
+ /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean support) {
+ mAvrcpAbsVolSupported = support;
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
mStreamStates[AudioSystem.STREAM_MUSIC], 0);
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index e08bd67..bb627e5 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.media.AudioAttributes;
+import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.VolumeShaper;
import android.util.Log;
@@ -35,15 +36,15 @@
public static final String TAG = "AudioService.FadeOutManager";
- /*package*/ static final long FADE_OUT_DURATION_MS = 2500;
+ /*package*/ static final long FADE_OUT_DURATION_MS = 2000;
private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG;
private static final VolumeShaper.Configuration FADEOUT_VSHAPE =
new VolumeShaper.Configuration.Builder()
.setId(PlaybackActivityMonitor.VOLUME_SHAPER_SYSTEM_FADEOUT_ID)
- .setCurve(new float[]{0.f, 1.0f} /* times */,
- new float[]{1.f, 0.0f} /* volumes */)
+ .setCurve(new float[]{0.f, 0.25f, 1.0f} /* times */,
+ new float[]{1.f, 0.65f, 0.0f} /* volumes */)
.setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
.setDuration(FADE_OUT_DURATION_MS)
.build();
@@ -70,6 +71,30 @@
private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
+
+ // TODO explore whether a shorter fade out would be a better UX instead of not fading out at all
+ // (legacy behavior)
+ /**
+ * Determine whether the focus request would trigger a fade out, given the parameters of the
+ * requester and those of the focus loser
+ * @param requester the parameters for the focus request
+ * @return true if there can be a fade out over the requester starting to play
+ */
+ static boolean canCauseFadeOut(@NonNull FocusRequester requester,
+ @NonNull FocusRequester loser) {
+ if (requester.getAudioAttributes().getContentType() == AudioAttributes.CONTENT_TYPE_SPEECH)
+ {
+ if (DEBUG) { Log.i(TAG, "not fading out: new focus is for speech"); }
+ return false;
+ }
+ if ((loser.getGrantFlags() & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) {
+ if (DEBUG) { Log.i(TAG, "not fading out: loser has PAUSES_ON_DUCKABLE_LOSS"); }
+ return false;
+ }
+
+ return true;
+ }
+
/**
* Evaluates whether the player associated with this configuration can and should be faded out
* @param apc the configuration of the player
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index cc60fe1..ab8b795 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -182,7 +182,7 @@
return mGrantFlags;
}
- AudioAttributes getAudioAttributes() {
+ @NonNull AudioAttributes getAudioAttributes() {
return mAttributes;
}
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 0310215..e6c4abfa 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -888,6 +888,7 @@
mEventLogger.log((new AudioEventLogger.StringEvent(
"requestAudioFocus() from uid/pid " + uid
+ "/" + Binder.getCallingPid()
+ + " AA=" + aa.usageToString() + "/" + aa.contentTypeToString()
+ " clientId=" + clientId + " callingPack=" + callingPackageName
+ " req=" + focusChangeHint
+ " flags=0x" + Integer.toHexString(flags)
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index e71219f..af9a14e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -707,6 +707,9 @@
if (DEBUG) { Log.v(TAG, "no players to fade out"); }
return false;
}
+ if (!FadeOutManager.canCauseFadeOut(winner, loser)) {
+ return false;
+ }
// check if this UID needs to be faded out (return false if not), and gather list of
// eligible players to fade out
final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 677ea5d..6482a2e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -46,6 +46,7 @@
* Interface that ClientMonitor holders should use to receive callbacks.
*/
public interface Callback {
+
/**
* Invoked when the ClientMonitor operation has been started (e.g. reached the head of
* the queue and becomes the current operation).
@@ -222,6 +223,7 @@
+ this.getClass().getSimpleName()
+ ", " + getProtoEnum()
+ ", " + getOwnerString()
- + ", " + getCookie() + "}";
+ + ", " + getCookie()
+ + ", " + getTargetUserId() + "}";
}
}
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 62a9769..f1c786b49 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -132,11 +132,13 @@
}
}
- public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
+ /** Called when a challenged has been generated. */
+ public void onChallengeGenerated(int sensorId, int userId, long challenge)
+ throws RemoteException {
if (mFaceServiceReceiver != null) {
- mFaceServiceReceiver.onChallengeGenerated(sensorId, challenge);
+ mFaceServiceReceiver.onChallengeGenerated(sensorId, userId, challenge);
} else if (mFingerprintServiceReceiver != null) {
- mFingerprintServiceReceiver.onChallengeGenerated(sensorId, challenge);
+ mFingerprintServiceReceiver.onChallengeGenerated(sensorId, userId, challenge);
}
}
@@ -153,18 +155,6 @@
}
}
- public void onChallengeInterrupted(int sensorId) throws RemoteException {
- if (mFaceServiceReceiver != null) {
- mFaceServiceReceiver.onChallengeInterrupted(sensorId);
- }
- }
-
- public void onChallengeInterruptFinished(int sensorId) throws RemoteException {
- if (mFaceServiceReceiver != null) {
- mFaceServiceReceiver.onChallengeInterruptFinished(sensorId);
- }
- }
-
// Fingerprint-specific callbacks for FingerprintManager only
public void onUdfpsPointerDown(int sensorId) throws RemoteException {
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 1fcad62..3d74f36 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -40,7 +40,7 @@
@Override
public void unableToStart() {
try {
- getListener().onChallengeGenerated(getSensorId(), 0L);
+ getListener().onChallengeGenerated(getSensorId(), getTargetUserId(), 0L);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send error", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index 8726923..57c1c74 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -110,17 +110,7 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) {
-
- }
-
- @Override
- public void onChallengeInterrupted(int sensorId) {
-
- }
-
- @Override
- public void onChallengeInterruptFinished(int sensorId) {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index 904c399..d76036b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -52,7 +52,7 @@
void onChallengeGenerated(int sensorId, int userId, long challenge) {
try {
- getListener().onChallengeGenerated(sensorId, challenge);
+ getListener().onChallengeGenerated(sensorId, userId, challenge);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send challenge", e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index dd3057e7..6a7d201 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -149,7 +149,7 @@
prop.supportsDetectInteraction, prop.halControlsPreview,
false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp);
+ internalProp, lockoutResetDispatcher);
mSensors.put(sensorId, sensor);
Slog.d(getTag(), "Added: " + internalProp);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index f551930..1e1b532 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -80,11 +80,26 @@
}
void onLockoutCleared() {
- mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_NONE);
- mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
+ resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
+ mLockoutResetDispatcher);
mCallback.onClientFinished(this, true /* success */);
}
+ /**
+ * Reset the local lockout state and notify any listeners.
+ *
+ * This should only be called when the HAL sends a reset request directly to the
+ * framework (i.e. time based reset, etc.). When the HAL is responding to a
+ * resetLockout request from an instance of this client {@link #onLockoutCleared()} should
+ * be used instead.
+ */
+ static void resetLocalLockoutStateToNone(int sensorId, int userId,
+ @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE);
+ lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId);
+ }
+
@Override
public int getProtoEnum() {
return BiometricsProto.CM_RESET_LOCKOUT;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 724531e..0e6a0f7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -57,6 +57,7 @@
import com.android.server.biometrics.sensors.Interruptable;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
@@ -124,10 +125,16 @@
private final int mSensorId;
private final int mUserId;
@NonNull
+ private final LockoutCache mLockoutCache;
+ @NonNull
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull
private final Callback mCallback;
HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
@NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull Callback callback) {
mContext = context;
mHandler = handler;
@@ -135,6 +142,8 @@
mScheduler = scheduler;
mSensorId = sensorId;
mUserId = userId;
+ mLockoutCache = lockoutTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
mCallback = callback;
}
@@ -327,13 +336,15 @@
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FaceResetLockoutClient)) {
- Slog.e(mTag, "onLockoutCleared for non-resetLockout client: "
- + Utils.getClientName(client));
- return;
+ Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
+ FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
+ mLockoutCache, mLockoutResetDispatcher);
+ } else {
+ Slog.d(mTag, "onLockoutCleared after resetLockout");
+ final FaceResetLockoutClient resetLockoutClient =
+ (FaceResetLockoutClient) client;
+ resetLockoutClient.onLockoutCleared();
}
-
- final FaceResetLockoutClient resetLockoutClient = (FaceResetLockoutClient) client;
- resetLockoutClient.onLockoutCleared();
});
}
@@ -465,7 +476,8 @@
}
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
- @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) {
+ @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
mTag = tag;
mProvider = provider;
mContext = context;
@@ -493,7 +505,8 @@
final int sensorId = mSensorProperties.sensorId;
final HalSessionCallback resultController = new HalSessionCallback(mContext,
- mHandler, mTag, mScheduler, sensorId, newUserId, callback);
+ mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
+ lockoutResetDispatcher, callback);
final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
(userIdStarted, newSession) -> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index f806767..d0580c7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -99,17 +99,7 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) {
-
- }
-
- @Override
- public void onChallengeInterrupted(int sensorId) {
-
- }
-
- @Override
- public void onChallengeInterruptFinished(int sensorId) {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index f908fba..a5bb0f4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -80,6 +80,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.time.Clock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -93,7 +94,13 @@
public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
private static final String TAG = "Face10";
+
private static final int ENROLL_TIMEOUT_SEC = 75;
+ private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60 * 1000;
+ private static final int GENERATE_CHALLENGE_COUNTER_TTL_MILLIS =
+ FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC * 1000;
+ @VisibleForTesting
+ public static Clock sSystemClock = Clock.systemUTC();
private boolean mTestHalEnabled;
@@ -102,19 +109,15 @@
@NonNull private final BiometricScheduler mScheduler;
@NonNull private final Handler mHandler;
@NonNull private final HalClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon;
- @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
@NonNull private final LockoutHalImpl mLockoutTracker;
@NonNull private final UsageStats mUsageStats;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
@Nullable private IBiometricsFace mDaemon;
@NonNull private final HalResultController mHalResultController;
- // If a challenge is generated, keep track of its owner. Since IBiometricsFace@1.0 only
- // supports a single in-flight challenge, we must notify the interrupted owner that its
- // challenge is no longer valid. The interrupted owner will be notified when the interrupter
- // has finished.
- @Nullable private FaceGenerateChallengeClient mCurrentChallengeOwner;
private int mCurrentUserId = UserHandle.USER_NULL;
private final int mSensorId;
+ private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
+ private FaceGenerateChallengeClient mGeneratedChallengeCache = null;
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
@@ -335,7 +338,6 @@
mAuthenticatorIds = new HashMap<>();
mLazyDaemon = Face10.this::getDaemon;
mLockoutTracker = new LockoutHalImpl();
- mLockoutResetDispatcher = lockoutResetDispatcher;
mHalResultController = new HalResultController(sensorProps.sensorId, context, mHandler,
mScheduler, mLockoutTracker, lockoutResetDispatcher);
mHalResultController.setCallback(() -> {
@@ -480,56 +482,56 @@
return getDaemon() != null;
}
+ private boolean isGeneratedChallengeCacheValid() {
+ return mGeneratedChallengeCache != null
+ && sSystemClock.millis() - mGeneratedChallengeCache.getCreatedAt()
+ < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS;
+ }
+
+ private void incrementChallengeCount() {
+ mGeneratedChallengeCount.add(0, sSystemClock.millis());
+ }
+
+ private int decrementChallengeCount() {
+ final long now = sSystemClock.millis();
+ // ignore values that are old in case generate/revoke calls are not matched
+ // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing
+ mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS);
+ if (!mGeneratedChallengeCount.isEmpty()) {
+ mGeneratedChallengeCount.remove(0);
+ }
+ return mGeneratedChallengeCount.size();
+ }
+
/**
- * {@link IBiometricsFace} only supports a single in-flight challenge. In cases where two
- * callers both need challenges (e.g. resetLockout right before enrollment), we need to ensure
- * that either:
- * 1) generateChallenge/operation/revokeChallenge is complete before the next generateChallenge
- * is processed by the scheduler, or
- * 2) the generateChallenge callback provides a mechanism for notifying the caller that its
- * challenge has been invalidated by a subsequent caller, as well as a mechanism for
- * notifying the previous caller that the interrupting operation is complete (e.g. the
- * interrupting client's challenge has been revoked, so that the interrupted client can
- * start retry logic if necessary). See
- * {@link
- *android.hardware.face.FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)}
- * The only case of conflicting challenges is currently resetLockout --> enroll. So, the second
- * option seems better as it prioritizes the new operation, which is user-facing.
+ * {@link IBiometricsFace} only supports a single in-flight challenge but there are cases where
+ * two callers both need challenges (e.g. resetLockout right before enrollment).
*/
@Override
public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
- if (mCurrentChallengeOwner != null) {
- final ClientMonitorCallbackConverter listener =
- mCurrentChallengeOwner.getListener();
- Slog.w(TAG, "Current challenge owner: " + mCurrentChallengeOwner
- + ", listener: " + listener
- + ", interrupted by: " + opPackageName);
- if (listener != null) {
- try {
- listener.onChallengeInterrupted(mSensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify challenge interrupted", e);
- }
- }
+ incrementChallengeCount();
+
+ if (isGeneratedChallengeCacheValid()) {
+ Slog.d(TAG, "Current challenge is cached and will be reused");
+ mGeneratedChallengeCache.reuseResult(receiver);
+ return;
}
scheduleUpdateActiveUserWithoutHandler(userId);
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId, mCurrentChallengeOwner);
+ opPackageName, mSensorId, sSystemClock.millis());
+ mGeneratedChallengeCache = client;
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
if (client != clientMonitor) {
Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client."
+ " Expecting: " + client + ", received: " + clientMonitor);
- return;
}
- Slog.d(TAG, "Current challenge owner: " + client);
- mCurrentChallengeOwner = client;
}
});
});
@@ -539,14 +541,16 @@
public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
- if (mCurrentChallengeOwner != null
- && !mCurrentChallengeOwner.getOwnerString().contentEquals(opPackageName)) {
- Slog.e(TAG, "scheduleRevokeChallenge, package: " + opPackageName
- + " attempting to revoke challenge owned by: "
- + mCurrentChallengeOwner.getOwnerString());
+ final boolean shouldRevoke = decrementChallengeCount() == 0;
+ if (!shouldRevoke) {
+ Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: "
+ + mGeneratedChallengeCount);
return;
}
+ Slog.d(TAG, "scheduleRevokeChallenge executing - no active clients");
+ mGeneratedChallengeCache = null;
+
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
mLazyDaemon, token, userId, opPackageName, mSensorId);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@@ -556,33 +560,6 @@
if (client != clientMonitor) {
Slog.e(TAG, "scheduleRevokeChallenge, mismatched client."
+ "Expecting: " + client + ", received: " + clientMonitor);
- return;
- }
-
- if (mCurrentChallengeOwner == null) {
- // Can happen if revoke is incorrectly called, for example without a
- // preceding generateChallenge
- Slog.w(TAG, "Current challenge owner is null");
- return;
- }
-
- final FaceGenerateChallengeClient previousChallengeOwner =
- mCurrentChallengeOwner.getInterruptedClient();
- mCurrentChallengeOwner = null;
-
- Slog.d(TAG, "Previous challenge owner: " + previousChallengeOwner);
- if (previousChallengeOwner != null) {
- final ClientMonitorCallbackConverter listener =
- previousChallengeOwner.getListener();
- if (listener == null) {
- Slog.w(TAG, "Listener is null");
- } else {
- try {
- listener.onChallengeInterruptFinished(mSensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify interrupt finished", e);
- }
- }
}
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index 3e0064e4..f418104 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -17,16 +17,20 @@
package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.IFaceServiceReceiver;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.internal.util.Preconditions;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Face-specific generateChallenge client supporting the
* {@link android.hardware.biometrics.face.V1_0} HIDL interface.
@@ -34,40 +38,70 @@
public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiometricsFace> {
private static final String TAG = "FaceGenerateChallengeClient";
- private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
+ static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
+ private static final Callback EMPTY_CALLBACK = new Callback() {
+ };
- // If `this` FaceGenerateChallengeClient was invoked while an existing in-flight challenge
- // was not revoked yet, store a reference to the interrupted client here. Notify the interrupted
- // client when `this` challenge is revoked.
- @Nullable private final FaceGenerateChallengeClient mInterruptedClient;
+ private final long mCreatedAt;
+ private List<IFaceServiceReceiver> mWaiting;
+ private Long mChallengeResult;
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId, @Nullable FaceGenerateChallengeClient interruptedClient) {
+ int sensorId, long now) {
super(context, lazyDaemon, token, listener, userId, owner, sensorId);
- mInterruptedClient = interruptedClient;
- }
-
- @Nullable
- public FaceGenerateChallengeClient getInterruptedClient() {
- return mInterruptedClient;
+ mCreatedAt = now;
+ mWaiting = new ArrayList<>();
}
@Override
protected void startHalOperation() {
+ mChallengeResult = null;
try {
- final long challenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
- try {
- getListener().onChallengeGenerated(getSensorId(), challenge);
- mCallback.onClientFinished(this, true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- mCallback.onClientFinished(this, false /* success */);
+ mChallengeResult = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+ // send the result to the original caller via mCallback and any waiting callers
+ // that called reuseResult
+ sendChallengeResult(getListener(), mCallback);
+ for (IFaceServiceReceiver receiver : mWaiting) {
+ sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK);
}
} catch (RemoteException e) {
Slog.e(TAG, "generateChallenge failed", e);
mCallback.onClientFinished(this, false /* success */);
+ } finally {
+ mWaiting = null;
+ }
+ }
+
+ /** @return An arbitrary time value for caching provided to the constructor. */
+ public long getCreatedAt() {
+ return mCreatedAt;
+ }
+
+ /**
+ * Reuse the result of this operation when it is available. The receiver will be notified
+ * immediately if a challenge has already been generated.
+ *
+ * @param receiver receiver to be notified of challenge result
+ */
+ public void reuseResult(@NonNull IFaceServiceReceiver receiver) {
+ if (mWaiting != null) {
+ mWaiting.add(receiver);
+ } else {
+ sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK);
+ }
+ }
+
+ private void sendChallengeResult(@NonNull ClientMonitorCallbackConverter receiver,
+ @NonNull Callback ownerCallback) {
+ Preconditions.checkState(mChallengeResult != null, "result not available");
+ try {
+ receiver.onChallengeGenerated(getSensorId(), getTargetUserId(), mChallengeResult);
+ ownerCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ ownerCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index e34afc0..29f2f20 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -100,7 +100,7 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
}
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 293b57d..6d01481 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
@@ -53,7 +53,7 @@
void onChallengeGenerated(int sensorId, int userId, long challenge) {
try {
- getListener().onChallengeGenerated(sensorId, challenge);
+ getListener().onChallengeGenerated(sensorId, userId, challenge);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send challenge", e);
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 20d6ee2..e5fafcd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -159,7 +159,7 @@
prop.sensorLocations[0].sensorLocationY,
prop.sensorLocations[0].sensorRadius);
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp, gestureAvailabilityDispatcher);
+ internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher);
mSensors.put(sensorId, sensor);
Slog.d(getTag(), "Added: " + internalProp);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index bab9506..878ef46 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -80,11 +80,26 @@
}
void onLockoutCleared() {
- mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_NONE);
- mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
+ resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
+ mLockoutResetDispatcher);
mCallback.onClientFinished(this, true /* success */);
}
+ /**
+ * Reset the local lockout state and notify any listeners.
+ *
+ * This should only be called when the HAL sends a reset request directly to the
+ * framework (i.e. time based reset, etc.). When the HAL is responding to a
+ * resetLockout request from an instance of this client {@link #onLockoutCleared()} should
+ * be used instead.
+ */
+ static void resetLocalLockoutStateToNone(int sensorId, int userId,
+ @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE);
+ lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId);
+ }
+
@Override
public int getProtoEnum() {
return BiometricsProto.CM_RESET_LOCKOUT;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index cf915ad..10137b5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -54,6 +54,7 @@
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
@@ -116,16 +117,27 @@
void onHardwareUnavailable();
}
- @NonNull private final Context mContext;
- @NonNull private final Handler mHandler;
- @NonNull private final String mTag;
- @NonNull private final UserAwareBiometricScheduler mScheduler;
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final String mTag;
+ @NonNull
+ private final UserAwareBiometricScheduler mScheduler;
private final int mSensorId;
private final int mUserId;
- @NonNull private final Callback mCallback;
+ @NonNull
+ private final LockoutCache mLockoutCache;
+ @NonNull
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull
+ private final Callback mCallback;
HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
@NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull Callback callback) {
mContext = context;
mHandler = handler;
@@ -133,6 +145,8 @@
mScheduler = scheduler;
mSensorId = sensorId;
mUserId = userId;
+ mLockoutCache = lockoutTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
mCallback = callback;
}
@@ -303,14 +317,15 @@
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FingerprintResetLockoutClient)) {
- Slog.e(mTag, "onLockoutCleared for non-resetLockout client: "
- + Utils.getClientName(client));
- return;
+ Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
+ FingerprintResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
+ mLockoutCache, mLockoutResetDispatcher);
+ } else {
+ Slog.d(mTag, "onLockoutCleared after resetLockout");
+ final FingerprintResetLockoutClient resetLockoutClient =
+ (FingerprintResetLockoutClient) client;
+ resetLockoutClient.onLockoutCleared();
}
-
- final FingerprintResetLockoutClient resetLockoutClient =
- (FingerprintResetLockoutClient) client;
- resetLockoutClient.onLockoutCleared();
});
}
@@ -415,6 +430,7 @@
Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
mTag = tag;
mProvider = provider;
@@ -422,6 +438,7 @@
mToken = new Binder();
mHandler = handler;
mSensorProperties = sensorProperties;
+ mLockoutCache = new LockoutCache();
mScheduler = new UserAwareBiometricScheduler(tag, gestureAvailabilityDispatcher,
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
new UserAwareBiometricScheduler.UserSwitchCallback() {
@@ -443,7 +460,8 @@
final int sensorId = mSensorProperties.sensorId;
final HalSessionCallback resultController = new HalSessionCallback(mContext,
- mHandler, mTag, mScheduler, sensorId, newUserId, callback);
+ mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
+ lockoutResetDispatcher, callback);
final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
(userIdStarted, newSession) -> {
@@ -466,7 +484,6 @@
resultController, userStartedCallback);
}
});
- mLockoutCache = new LockoutCache();
mAuthenticatorIds = new HashMap<>();
mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
}
@@ -529,6 +546,9 @@
proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT);
+ if (mSensorProperties.isAnyUdfpsType()) {
+ proto.write(SensorStateProto.MODALITY_FLAGS, SensorStateProto.FINGERPRINT_UDFPS);
+ }
proto.write(SensorStateProto.CURRENT_STRENGTH,
Utils.getCurrentStrength(mSensorProperties.sensorId));
proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index ad4f679..c00daff 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -101,7 +101,7 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
}
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 2746520..3353264 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -760,6 +760,9 @@
proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT);
+ if (mSensorProperties.isAnyUdfpsType()) {
+ proto.write(SensorStateProto.MODALITY_FLAGS, SensorStateProto.FINGERPRINT_UDFPS);
+ }
proto.write(SensorStateProto.CURRENT_STRENGTH,
Utils.getCurrentStrength(mSensorProperties.sensorId));
proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
index 3584397..db2f045 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
@@ -48,7 +48,7 @@
try {
final long challenge = getFreshDaemon().preEnroll();
try {
- getListener().onChallengeGenerated(getSensorId(), challenge);
+ getListener().onChallengeGenerated(getSensorId(), getTargetUserId(), challenge);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index ab67b13..093ecd5 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -379,7 +379,15 @@
return null;
}
synchronized (mLock) {
- addActiveOwnerLocked(intendingUid, pkg);
+ try {
+ addActiveOwnerLocked(intendingUid, pkg);
+ } catch (SecurityException e) {
+ // Permission could not be granted - URI may be invalid
+ Slog.i(TAG, "Could not grant permission to primary clip. Clearing clipboard.");
+ setPrimaryClipInternalLocked(null, intendingUid, pkg);
+ return null;
+ }
+
PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
notifyTextClassifierLocked(clipboard, pkg, intendingUid);
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 40ab108..7cb2921 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -93,6 +93,7 @@
private int mDisplayHeight; // real height, not rotated
private SurfaceControl mSurfaceControl;
private Surface mSurface;
+ private SurfaceControl mBLASTSurfaceControl;
private BLASTBufferQueue mBLASTBufferQueue;
private NaturalSurfaceLayout mSurfaceLayout;
private EGLDisplay mEglDisplay;
@@ -576,7 +577,7 @@
if (mMode == MODE_FADE) {
builder.setColorLayer();
} else {
- builder.setBLASTLayer();
+ builder.setContainerLayer();
}
mSurfaceControl = builder.build();
} catch (OutOfResourcesException ex) {
@@ -592,7 +593,14 @@
mTransaction.apply();
if (mMode != MODE_FADE) {
- mBLASTBufferQueue = new BLASTBufferQueue("ColorFade", mSurfaceControl,
+ final SurfaceControl.Builder b = new SurfaceControl.Builder()
+ .setName("ColorFade BLAST")
+ .setParent(mSurfaceControl)
+ .setHidden(false)
+ .setSecure(isSecure)
+ .setBLASTLayer();
+ mBLASTSurfaceControl = b.build();
+ mBLASTBufferQueue = new BLASTBufferQueue("ColorFade", mBLASTSurfaceControl,
mDisplayWidth, mDisplayHeight, PixelFormat.TRANSLUCENT);
mSurface = mBLASTBufferQueue.createSurface();
}
@@ -723,10 +731,16 @@
mTransaction.remove(mSurfaceControl).apply();
if (mSurface != null) {
mSurface.release();
- mBLASTBufferQueue.destroy();
mSurface = null;
+ }
+
+ if (mBLASTSurfaceControl != null) {
+ mBLASTSurfaceControl.release();
+ mBLASTSurfaceControl = null;
+ mBLASTBufferQueue.destroy();
mBLASTBufferQueue = null;
}
+
mSurfaceControl = null;
mSurfaceVisible = false;
mSurfaceAlpha = 0f;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 0f9e604..170564d 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -422,7 +422,7 @@
if (configBrightnessDefault != null) {
mBrightnessDefault = configBrightnessDefault.floatValue();
} else {
- mBrightnessDefault = BRIGHTNESS_DEFAULT;
+ loadBrightnessDefaultFromConfigXml();
}
}
}
@@ -689,6 +689,8 @@
if (sensorDetails != null) {
mAmbientLightSensor.type = sensorDetails.getType();
mAmbientLightSensor.name = sensorDetails.getName();
+ } else {
+ loadAmbientLightSensorFromConfigXml();
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index f75f3e1..d4920f5 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1498,8 +1498,8 @@
}
private void setDisplayPropertiesInternal(int displayId, boolean hasContent,
- float requestedRefreshRate, int requestedModeId, boolean preferMinimalPostProcessing,
- boolean inTraversal) {
+ float requestedRefreshRate, int requestedModeId, float requestedMaxRefreshRate,
+ boolean preferMinimalPostProcessing, boolean inTraversal) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display == null) {
@@ -1523,8 +1523,8 @@
requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
requestedRefreshRate).getModeId();
}
- mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode(
- displayId, requestedModeId);
+ mDisplayModeDirector.getAppRequestObserver().setAppRequest(
+ displayId, requestedModeId, requestedMaxRefreshRate);
if (display.getDisplayInfoLocked().minimalPostProcessingSupported) {
boolean mppRequest = mMinimalPostProcessingAllowed && preferMinimalPostProcessing;
@@ -3189,10 +3189,11 @@
@Override
public void setDisplayProperties(int displayId, boolean hasContent,
- float requestedRefreshRate, int requestedMode,
+ float requestedRefreshRate, int requestedMode, float requestedMaxRefreshRate,
boolean requestedMinimalPostProcessing, boolean inTraversal) {
setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate,
- requestedMode, requestedMinimalPostProcessing, inTraversal);
+ requestedMode, requestedMaxRefreshRate, requestedMinimalPostProcessing,
+ inTraversal);
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 67779a2..997f0e5 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -198,6 +198,8 @@
public float maxRefreshRate;
public int width;
public int height;
+ public boolean disableRefreshRateSwitching;
+ public float baseModeRefreshRate;
VoteSummary() {
reset();
@@ -208,6 +210,8 @@
maxRefreshRate = Float.POSITIVE_INFINITY;
width = Vote.INVALID_SIZE;
height = Vote.INVALID_SIZE;
+ disableRefreshRateSwitching = false;
+ baseModeRefreshRate = 0f;
}
}
@@ -229,13 +233,20 @@
// For refresh rates, just use the tightest bounds of all the votes
summary.minRefreshRate = Math.max(summary.minRefreshRate, vote.refreshRateRange.min);
summary.maxRefreshRate = Math.min(summary.maxRefreshRate, vote.refreshRateRange.max);
- // For display size, use only the first vote we come across (i.e. the highest
- // priority vote that includes the width / height).
+ // For display size, disable refresh rate switching and base mode refresh rate use only
+ // the first vote we come across (i.e. the highest priority vote that includes the
+ // attribute).
if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE
&& vote.height > 0 && vote.width > 0) {
summary.width = vote.width;
summary.height = vote.height;
}
+ if (!summary.disableRefreshRateSwitching && vote.disableRefreshRateSwitching) {
+ summary.disableRefreshRateSwitching = true;
+ }
+ if (summary.baseModeRefreshRate == 0f && vote.baseModeRefreshRate > 0f) {
+ summary.baseModeRefreshRate = vote.baseModeRefreshRate;
+ }
}
}
@@ -260,13 +271,14 @@
return new DesiredDisplayModeSpecs();
}
- int[] availableModes = new int[]{defaultMode.getModeId()};
+ ArrayList<Display.Mode> availableModes = new ArrayList<>();
+ availableModes.add(defaultMode);
VoteSummary primarySummary = new VoteSummary();
int lowestConsideredPriority = Vote.MIN_PRIORITY;
int highestConsideredPriority = Vote.MAX_PRIORITY;
if (mAlwaysRespectAppRequest) {
- lowestConsideredPriority = Vote.PRIORITY_APP_REQUEST_REFRESH_RATE;
+ lowestConsideredPriority = Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE;
highestConsideredPriority = Vote.PRIORITY_APP_REQUEST_SIZE;
}
@@ -286,16 +298,19 @@
}
availableModes = filterModes(modes, primarySummary);
- if (availableModes.length > 0) {
+ if (!availableModes.isEmpty()) {
if (mLoggingEnabled) {
- Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
+ Slog.w(TAG, "Found available modes=" + availableModes
+ " with lowest priority considered "
+ Vote.priorityToString(lowestConsideredPriority)
+ " and constraints: "
+ "width=" + primarySummary.width
+ ", height=" + primarySummary.height
+ ", minRefreshRate=" + primarySummary.minRefreshRate
- + ", maxRefreshRate=" + primarySummary.maxRefreshRate);
+ + ", maxRefreshRate=" + primarySummary.maxRefreshRate
+ + ", disableRefreshRateSwitching="
+ + primarySummary.disableRefreshRateSwitching
+ + ", baseModeRefreshRate=" + primarySummary.baseModeRefreshRate);
}
break;
}
@@ -307,7 +322,10 @@
+ "width=" + primarySummary.width
+ ", height=" + primarySummary.height
+ ", minRefreshRate=" + primarySummary.minRefreshRate
- + ", maxRefreshRate=" + primarySummary.maxRefreshRate);
+ + ", maxRefreshRate=" + primarySummary.maxRefreshRate
+ + ", disableRefreshRateSwitching="
+ + primarySummary.disableRefreshRateSwitching
+ + ", baseModeRefreshRate=" + primarySummary.baseModeRefreshRate);
}
// If we haven't found anything with the current set of votes, drop the
@@ -332,26 +350,38 @@
appRequestSummary.maxRefreshRate));
}
- int baseModeId = INVALID_DISPLAY_MODE_ID;
+ // Select the base mode id based on the base mode refresh rate, if available, since this
+ // will be the mode id the app voted for.
+ Display.Mode baseMode = null;
+ for (Display.Mode availableMode : availableModes) {
+ if (primarySummary.baseModeRefreshRate
+ >= availableMode.getRefreshRate() - FLOAT_TOLERANCE
+ && primarySummary.baseModeRefreshRate
+ <= availableMode.getRefreshRate() + FLOAT_TOLERANCE) {
+ baseMode = availableMode;
+ }
+ }
// Select the default mode if available. This is important because SurfaceFlinger
// can do only seamless switches by default. Some devices (e.g. TV) don't support
// seamless switching so the mode we select here won't be changed.
- for (int availableMode : availableModes) {
- if (availableMode == defaultMode.getModeId()) {
- baseModeId = defaultMode.getModeId();
- break;
+ if (baseMode == null) {
+ for (Display.Mode availableMode : availableModes) {
+ if (availableMode.getModeId() == defaultMode.getModeId()) {
+ baseMode = defaultMode;
+ break;
+ }
}
}
// If the application requests a display mode by setting
// LayoutParams.preferredDisplayModeId, it will be the only available mode and it'll
// be stored as baseModeId.
- if (baseModeId == INVALID_DISPLAY_MODE_ID && availableModes.length > 0) {
- baseModeId = availableModes[0];
+ if (baseMode == null && !availableModes.isEmpty()) {
+ baseMode = availableModes.get(0);
}
- if (baseModeId == INVALID_DISPLAY_MODE_ID) {
+ if (baseMode == null) {
Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling"
+ " back to the default mode. Display = " + displayId + ", votes = " + votes
+ ", supported modes = " + Arrays.toString(modes));
@@ -363,31 +393,19 @@
new RefreshRateRange(fps, fps));
}
- if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
- Display.Mode baseMode = null;
- for (Display.Mode mode : modes) {
- if (mode.getModeId() == baseModeId) {
- baseMode = mode;
- break;
- }
- }
- if (baseMode == null) {
- // This should never happen.
- throw new IllegalStateException(
- "The base mode with id " + baseModeId
- + " is not in the list of supported modes.");
- }
+ if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
+ || primarySummary.disableRefreshRateSwitching) {
float fps = baseMode.getRefreshRate();
- return new DesiredDisplayModeSpecs(baseModeId,
- /*allowGroupSwitching */ false,
- new RefreshRateRange(fps, fps),
- new RefreshRateRange(fps, fps));
+ primarySummary.minRefreshRate = primarySummary.maxRefreshRate = fps;
+ if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
+ appRequestSummary.minRefreshRate = appRequestSummary.maxRefreshRate = fps;
+ }
}
boolean allowGroupSwitching =
mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
- return new DesiredDisplayModeSpecs(baseModeId,
+ return new DesiredDisplayModeSpecs(baseMode.getModeId(),
allowGroupSwitching,
new RefreshRateRange(
primarySummary.minRefreshRate, primarySummary.maxRefreshRate),
@@ -396,8 +414,10 @@
}
}
- private int[] filterModes(Display.Mode[] supportedModes, VoteSummary summary) {
+ private ArrayList<Display.Mode> filterModes(Display.Mode[] supportedModes,
+ VoteSummary summary) {
ArrayList<Display.Mode> availableModes = new ArrayList<>();
+ boolean missingBaseModeRefreshRate = summary.baseModeRefreshRate > 0f;
for (Display.Mode mode : supportedModes) {
if (mode.getPhysicalWidth() != summary.width
|| mode.getPhysicalHeight() != summary.height) {
@@ -426,13 +446,16 @@
continue;
}
availableModes.add(mode);
+ if (mode.getRefreshRate() >= summary.baseModeRefreshRate - FLOAT_TOLERANCE
+ && mode.getRefreshRate() <= summary.baseModeRefreshRate + FLOAT_TOLERANCE) {
+ missingBaseModeRefreshRate = false;
+ }
}
- final int size = availableModes.size();
- int[] availableModeIds = new int[size];
- for (int i = 0; i < size; i++) {
- availableModeIds[i] = availableModes.get(i).getModeId();
+ if (missingBaseModeRefreshRate) {
+ return new ArrayList<>();
}
- return availableModeIds;
+
+ return availableModes;
}
/**
@@ -912,37 +935,52 @@
// by all other considerations. It acts to set a default frame rate for a device.
public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
- // FLICKER votes for a single refresh rate like [60,60], [90,90] or null.
- // If the higher voters result is a range, it will fix the rate to a single choice.
- // It's used to avoid refresh rate switches in certain conditions which may result in the
- // user seeing the display flickering when the switches occur.
- public static final int PRIORITY_FLICKER = 1;
+ // PRIORITY_FLICKER_REFRESH_RATE votes for a single refresh rate like [60,60], [90,90] or
+ // null. It is used to set a preferred refresh rate value in case the higher priority votes
+ // result is a range.
+ public static final int PRIORITY_FLICKER_REFRESH_RATE = 1;
// SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
// It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2;
+ // APP_REQUEST_MAX_REFRESH_RATE is used to for internal apps to limit the refresh
+ // rate in certain cases, mostly to preserve power.
+ // It votes to [0, APP_REQUEST_MAX_REFRESH_RATE].
+ public static final int PRIORITY_APP_REQUEST_MAX_REFRESH_RATE = 3;
+
// We split the app request into different priorities in case we can satisfy one desire
// without the other.
// Application can specify preferred refresh rate with below attrs.
// @see android.view.WindowManager.LayoutParams#preferredRefreshRate
// @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
- // System also forces some apps like denylisted app to run at a lower refresh rate.
+ // These translates into votes for the base mode refresh rate and resolution to be
+ // used by SurfaceFlinger as the policy of choosing the display mode. The system also
+ // forces some apps like denylisted app to run at a lower refresh rate.
// @see android.R.array#config_highRefreshRateBlacklist
- public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3;
- public static final int PRIORITY_APP_REQUEST_SIZE = 4;
+ // The preferred refresh rate is set on the main surface of the app outside of
+ // DisplayModeDirector.
+ // @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
+ public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 4;
+ public static final int PRIORITY_APP_REQUEST_SIZE = 5;
// SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
// of low priority voters. It votes [0, max(PEAK, MIN)]
- public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5;
+ public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 6;
// LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
- public static final int PRIORITY_LOW_POWER_MODE = 6;
+ public static final int PRIORITY_LOW_POWER_MODE = 7;
+
+ // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
+ // higher priority voters' result is a range, it will fix the rate to a single choice.
+ // It's used to avoid refresh rate switches in certain conditions which may result in the
+ // user seeing the display flickering when the switches occur.
+ public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
- public static final int PRIORITY_UDFPS = 7;
+ public static final int PRIORITY_UDFPS = 9;
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -953,7 +991,7 @@
// The cutoff for the app request refresh rate range. Votes with priorities lower than this
// value will not be considered when constructing the app request refresh rate range.
public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
- PRIORITY_APP_REQUEST_REFRESH_RATE;
+ PRIORITY_APP_REQUEST_MAX_REFRESH_RATE;
/**
* A value signifying an invalid width or height in a vote.
@@ -973,32 +1011,64 @@
*/
public final RefreshRateRange refreshRateRange;
+ /**
+ * Whether refresh rate switching should be disabled (i.e. the refresh rate range is
+ * a single value).
+ */
+ public final boolean disableRefreshRateSwitching;
+
+ /**
+ * The base mode refresh rate to be used for this display. This would be used when deciding
+ * the base mode id.
+ */
+ public final float baseModeRefreshRate;
+
public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
- return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
+ return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate,
+ minRefreshRate == maxRefreshRate, 0f);
}
public static Vote forSize(int width, int height) {
- return new Vote(width, height, 0f, Float.POSITIVE_INFINITY);
+ return new Vote(width, height, 0f, Float.POSITIVE_INFINITY, false,
+ 0f);
+ }
+
+ public static Vote forDisableRefreshRateSwitching() {
+ return new Vote(INVALID_SIZE, INVALID_SIZE, 0f, Float.POSITIVE_INFINITY, true,
+ 0f);
+ }
+
+ public static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
+ return new Vote(INVALID_SIZE, INVALID_SIZE, 0f, Float.POSITIVE_INFINITY, false,
+ baseModeRefreshRate);
}
private Vote(int width, int height,
- float minRefreshRate, float maxRefreshRate) {
+ float minRefreshRate, float maxRefreshRate,
+ boolean disableRefreshRateSwitching,
+ float baseModeRefreshRate) {
this.width = width;
this.height = height;
this.refreshRateRange =
new RefreshRateRange(minRefreshRate, maxRefreshRate);
+ this.disableRefreshRateSwitching = disableRefreshRateSwitching;
+ this.baseModeRefreshRate = baseModeRefreshRate;
}
public static String priorityToString(int priority) {
switch (priority) {
case PRIORITY_DEFAULT_REFRESH_RATE:
return "PRIORITY_DEFAULT_REFRESH_RATE";
- case PRIORITY_FLICKER:
- return "PRIORITY_FLICKER";
+ case PRIORITY_FLICKER_REFRESH_RATE:
+ return "PRIORITY_FLICKER_REFRESH_RATE";
+ case PRIORITY_FLICKER_REFRESH_RATE_SWITCH:
+ return "PRIORITY_FLICKER_REFRESH_RATE_SWITCH";
case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
- case PRIORITY_APP_REQUEST_REFRESH_RATE:
- return "PRIORITY_APP_REQUEST_REFRESH_RATE";
+ case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE:
+ return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE";
+ case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
+ return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
case PRIORITY_APP_REQUEST_SIZE:
return "PRIORITY_APP_REQUEST_SIZE";
case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
@@ -1018,7 +1088,9 @@
return "Vote{"
+ "width=" + width + ", height=" + height
+ ", minRefreshRate=" + refreshRateRange.min
- + ", maxRefreshRate=" + refreshRateRange.max + "}";
+ + ", maxRefreshRate=" + refreshRateRange.max
+ + ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
+ + ", baseModeRefreshRate=" + baseModeRefreshRate + "}";
}
}
@@ -1182,14 +1254,17 @@
final class AppRequestObserver {
private final SparseArray<Display.Mode> mAppRequestedModeByDisplay;
+ private final SparseArray<Float> mAppPreferredMaxRefreshRateByDisplay;
AppRequestObserver() {
mAppRequestedModeByDisplay = new SparseArray<>();
+ mAppPreferredMaxRefreshRateByDisplay = new SparseArray<>();
}
- public void setAppRequestedMode(int displayId, int modeId) {
+ public void setAppRequest(int displayId, int modeId, float requestedMaxRefreshRate) {
synchronized (mLock) {
setAppRequestedModeLocked(displayId, modeId);
+ setAppPreferredMaxRefreshRateLocked(displayId, requestedMaxRefreshRate);
}
}
@@ -1199,24 +1274,48 @@
return;
}
- final Vote refreshRateVote;
+ final Vote baseModeRefreshRateVote;
final Vote sizeVote;
if (requestedMode != null) {
mAppRequestedModeByDisplay.put(displayId, requestedMode);
- float refreshRate = requestedMode.getRefreshRate();
- refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate);
+ baseModeRefreshRateVote =
+ Vote.forBaseModeRefreshRate(requestedMode.getRefreshRate());
sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
requestedMode.getPhysicalHeight());
} else {
mAppRequestedModeByDisplay.remove(displayId);
- refreshRateVote = null;
+ baseModeRefreshRateVote = null;
sizeVote = null;
}
- updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
+ updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ baseModeRefreshRateVote);
updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
}
+ private void setAppPreferredMaxRefreshRateLocked(int displayId,
+ float requestedMaxRefreshRate) {
+ final Vote vote;
+ final Float requestedMaxRefreshRateVote =
+ requestedMaxRefreshRate > 0
+ ? new Float(requestedMaxRefreshRate) : null;
+ if (Objects.equals(requestedMaxRefreshRateVote,
+ mAppPreferredMaxRefreshRateByDisplay.get(displayId))) {
+ return;
+ }
+
+ if (requestedMaxRefreshRate > 0) {
+ mAppPreferredMaxRefreshRateByDisplay.put(displayId, requestedMaxRefreshRateVote);
+ vote = Vote.forRefreshRates(0, requestedMaxRefreshRate);
+ } else {
+ mAppPreferredMaxRefreshRateByDisplay.remove(displayId);
+ vote = null;
+ }
+ synchronized (mLock) {
+ updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, vote);
+ }
+ }
+
private Display.Mode findModeByIdLocked(int displayId, int modeId) {
Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
if (modes == null) {
@@ -1238,6 +1337,12 @@
final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
pw.println(" " + id + " -> " + mode);
}
+ pw.println(" mAppPreferredMaxRefreshRateByDisplay:");
+ for (int i = 0; i < mAppPreferredMaxRefreshRateByDisplay.size(); i++) {
+ final int id = mAppPreferredMaxRefreshRateByDisplay.keyAt(i);
+ final Float refreshRate = mAppPreferredMaxRefreshRateByDisplay.valueAt(i);
+ pw.println(" " + id + " -> " + refreshRate);
+ }
}
}
@@ -1486,7 +1591,8 @@
updateSensorStatus();
if (!changeable) {
// Revoke previous vote from BrightnessObserver
- updateVoteLocked(Vote.PRIORITY_FLICKER, null);
+ updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE, null);
+ updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, null);
}
}
}
@@ -1734,7 +1840,8 @@
return false;
}
private void onBrightnessChangedLocked() {
- Vote vote = null;
+ Vote refreshRateVote = null;
+ Vote refreshRateSwitchingVote = null;
if (mBrightness < 0) {
// Either the setting isn't available or we shouldn't be observing yet anyways.
@@ -1744,20 +1851,25 @@
boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
if (insideLowZone) {
- vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
+ refreshRateVote =
+ Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
+ refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
}
boolean insideHighZone = hasValidHighZone()
&& isInsideHighZone(mBrightness, mAmbientLux);
if (insideHighZone) {
- vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
+ refreshRateVote =
+ Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
+ refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
}
if (mLoggingEnabled) {
Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux
- + ", Vote " + vote);
+ + ", Vote " + refreshRateVote);
}
- updateVoteLocked(Vote.PRIORITY_FLICKER, vote);
+ updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE, refreshRateVote);
+ updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, refreshRateSwitchingVote);
}
private boolean hasValidLowZone() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 4ad8797a..52b05f2 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1362,6 +1362,9 @@
+ mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
+ "', previous reason: '" + mBrightnessReason + "'.");
mBrightnessReason.set(mBrightnessReasonTemp);
+ } else if (mBrightnessReasonTemp.reason == BrightnessReason.REASON_MANUAL
+ && userSetBrightnessChanged) {
+ Slog.v(TAG, "Brightness [" + brightnessState + "] manual adjustment.");
}
// Update display white-balance.
@@ -2021,7 +2024,8 @@
|| mPendingScreenBrightnessSetting < 0.0f)) {
return false;
}
- if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
+ if (BrightnessSynchronizer.floatEquals(
+ mCurrentScreenBrightnessSetting, mPendingScreenBrightnessSetting)) {
mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
return false;
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 609bd8b..ae9c64b4 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -19,21 +19,21 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.graphics.Typeface;
-import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
-import android.graphics.fonts.FontFileUtil;
import android.graphics.fonts.FontManager;
import android.graphics.fonts.FontUpdateRequest;
import android.graphics.fonts.SystemFonts;
+import android.os.ParcelFileDescriptor;
import android.os.ResultReceiver;
import android.os.SharedMemory;
import android.os.ShellCallback;
import android.system.ErrnoException;
import android.text.FontConfig;
-import android.text.TextUtils;
import android.util.AndroidException;
+import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -47,24 +47,24 @@
import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
+import java.nio.DirectByteBuffer;
import java.nio.NioUtils;
-import java.nio.channels.FileChannel;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/** A service for managing system fonts. */
-// TODO(b/173619554): Add API to update fonts.
public final class FontManagerService extends IFontManager.Stub {
private static final String TAG = "FontManagerService";
private static final String FONT_FILES_DIR = "/data/fonts/files";
+ private static final String CONFIG_XML_FILE = "/data/fonts/config/config.xml";
+ @RequiresPermission(Manifest.permission.UPDATE_FONTS)
@Override
public FontConfig getFontConfig() {
getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
@@ -72,35 +72,39 @@
return getSystemFontConfig();
}
+ @RequiresPermission(Manifest.permission.UPDATE_FONTS)
@Override
- public int updateFontFile(@NonNull FontUpdateRequest request, int baseVersion) {
- Preconditions.checkArgumentNonnegative(baseVersion);
- Objects.requireNonNull(request);
- Objects.requireNonNull(request.getFd());
- Objects.requireNonNull(request.getSignature());
- getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
- "UPDATE_FONTS permission required.");
+ public int updateFontFamily(@NonNull List<FontUpdateRequest> requests, int baseVersion) {
try {
- update(baseVersion, Collections.singletonList(request));
- return FontManager.RESULT_SUCCESS;
- } catch (SystemFontException e) {
- Slog.e(TAG, "Failed to update font file", e);
- return e.getErrorCode();
+ Preconditions.checkArgumentNonnegative(baseVersion);
+ Objects.requireNonNull(requests);
+ getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
+ "UPDATE_FONTS permission required.");
+ try {
+ update(baseVersion, requests);
+ return FontManager.RESULT_SUCCESS;
+ } catch (SystemFontException e) {
+ Slog.e(TAG, "Failed to update font family", e);
+ return e.getErrorCode();
+ }
+ } finally {
+ closeFileDescriptors(requests);
}
}
- @Override
- public int updateFontFamily(@NonNull List<FontUpdateRequest> requests, int baseVersion) {
- Preconditions.checkArgumentNonnegative(baseVersion);
- Objects.requireNonNull(requests);
- getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
- "UPDATE_FONTS permission required.");
- try {
- update(baseVersion, requests);
- return FontManager.RESULT_SUCCESS;
- } catch (SystemFontException e) {
- Slog.e(TAG, "Failed to update font family", e);
- return e.getErrorCode();
+ private static void closeFileDescriptors(@Nullable List<FontUpdateRequest> requests) {
+ // Make sure we close every passed FD, even if 'requests' is constructed incorrectly and
+ // some fields are null.
+ if (requests == null) return;
+ for (FontUpdateRequest request : requests) {
+ if (request == null) continue;
+ ParcelFileDescriptor fd = request.getFd();
+ if (fd == null) continue;
+ try {
+ fd.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close fd", e);
+ }
}
}
@@ -127,9 +131,9 @@
public static final class Lifecycle extends SystemService {
private final FontManagerService mService;
- public Lifecycle(@NonNull Context context) {
+ public Lifecycle(@NonNull Context context, boolean safeMode) {
super(context);
- mService = new FontManagerService(context);
+ mService = new FontManagerService(context, safeMode);
}
@Override
@@ -149,67 +153,6 @@
}
}
- /* package */ static class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
- @Override
- public String getPostScriptName(File file) throws IOException {
- ByteBuffer buffer = mmap(file);
- try {
- return FontFileUtil.getPostScriptName(buffer, 0);
- } finally {
- NioUtils.freeDirectBuffer(buffer);
- }
- }
-
- @Override
- public String buildFontFileName(File file) throws IOException {
- ByteBuffer buffer = mmap(file);
- try {
- String psName = FontFileUtil.getPostScriptName(buffer, 0);
- int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0);
- int isCollection = FontFileUtil.isCollectionFont(buffer);
-
- if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) {
- return null;
- }
-
- String extension;
- if (isCollection == 1) {
- extension = isType1Font == 1 ? ".otc" : ".ttc";
- } else {
- extension = isType1Font == 1 ? ".otf" : ".ttf";
- }
- return psName + extension;
- } finally {
- NioUtils.freeDirectBuffer(buffer);
- }
-
- }
-
- @Override
- public long getRevision(File file) throws IOException {
- ByteBuffer buffer = mmap(file);
- try {
- return FontFileUtil.getRevision(buffer, 0);
- } finally {
- NioUtils.freeDirectBuffer(buffer);
- }
- }
-
- @Override
- public void tryToCreateTypeface(File file) throws IOException {
- Font font = new Font.Builder(file).build();
- FontFamily family = new FontFamily.Builder(font).build();
- new Typeface.CustomFallbackBuilder(family).build();
- }
-
- private static ByteBuffer mmap(File file) throws IOException {
- try (FileInputStream in = new FileInputStream(file)) {
- FileChannel fileChannel = in.getChannel();
- return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
- }
- }
- }
-
private static class FsverityUtilImpl implements UpdatableFontDir.FsverityUtil {
@Override
public boolean hasFsverity(String filePath) {
@@ -245,30 +188,30 @@
@Nullable
private SharedMemory mSerializedFontMap = null;
- private FontManagerService(Context context) {
+ private FontManagerService(Context context, boolean safeMode) {
+ if (safeMode) {
+ Slog.i(TAG, "Entering safe mode. Deleting all font updates.");
+ UpdatableFontDir.deleteAllFiles(new File(FONT_FILES_DIR), new File(CONFIG_XML_FILE));
+ }
mContext = context;
- mUpdatableFontDir = createUpdatableFontDir();
+ mUpdatableFontDir = createUpdatableFontDir(safeMode);
initialize();
}
@Nullable
- private static UpdatableFontDir createUpdatableFontDir() {
+ private static UpdatableFontDir createUpdatableFontDir(boolean safeMode) {
+ // Never read updatable font files in safe mode.
+ if (safeMode) return null;
// If apk verity is supported, fs-verity should be available.
if (!VerityUtils.isFsVeritySupported()) return null;
- return new UpdatableFontDir(new File(FONT_FILES_DIR),
- new OtfFontFileParser(), new FsverityUtilImpl());
+ return new UpdatableFontDir(new File(FONT_FILES_DIR), new OtfFontFileParser(),
+ new FsverityUtilImpl(), new File(CONFIG_XML_FILE));
}
private void initialize() {
synchronized (mUpdatableFontDirLock) {
if (mUpdatableFontDir == null) {
- synchronized (mSerializedFontMapLock) {
- try {
- mSerializedFontMap = Typeface.serializeFontMap(Typeface.getSystemFontMap());
- } catch (IOException | ErrnoException e) {
- mSerializedFontMap = null;
- }
- }
+ setSerializedFontMap(serializeSystemServerFontMap());
return;
}
mUpdatableFontDir.loadFontFileMap();
@@ -307,18 +250,23 @@
}
}
- /* package */ void clearUpdates() throws SystemFontException {
- if (mUpdatableFontDir == null) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
- "The font updater is disabled.");
- }
- synchronized (mUpdatableFontDirLock) {
- mUpdatableFontDir.clearUpdates();
- updateSerializedFontMap();
- }
+ /**
+ * Clears all updates and restarts FontManagerService.
+ *
+ * <p>CAUTION: this method is not safe. Existing processes may crash due to missing font files.
+ * This method is only for {@link FontManagerShellCommand}.
+ */
+ /* package */ void clearUpdates() {
+ UpdatableFontDir.deleteAllFiles(new File(FONT_FILES_DIR), new File(CONFIG_XML_FILE));
+ initialize();
}
+ /**
+ * Restarts FontManagerService, removing not-the-latest font files.
+ *
+ * <p>CAUTION: this method is not safe. Existing processes may crash due to missing font files.
+ * This method is only for {@link FontManagerShellCommand}.
+ */
/* package */ void restart() {
initialize();
}
@@ -364,36 +312,57 @@
/**
* Makes new serialized font map data and updates mSerializedFontMap.
*/
- public void updateSerializedFontMap() {
+ private void updateSerializedFontMap() {
+ SharedMemory serializedFontMap = serializeFontMap(getSystemFontConfig());
+ if (serializedFontMap == null) {
+ // Fallback to the preloaded config.
+ serializedFontMap = serializeSystemServerFontMap();
+ }
+ setSerializedFontMap(serializedFontMap);
+ }
+
+ @Nullable
+ private static SharedMemory serializeFontMap(FontConfig fontConfig) {
+ final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
try {
- final FontConfig fontConfig = getSystemFontConfig();
- final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
+ final Map<String, FontFamily[]> fallback =
+ SystemFonts.buildSystemFallback(fontConfig, bufferCache);
final Map<String, Typeface> typefaceMap =
SystemFonts.buildSystemTypefaces(fontConfig, fallback);
-
- SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
- synchronized (mSerializedFontMapLock) {
- mSerializedFontMap = serializeFontMap;
- }
- return;
+ return Typeface.serializeFontMap(typefaceMap);
} catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to serialize updatable font map. "
+ "Retrying with system image fonts.", e);
- }
-
- try {
- final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig();
- final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
- final Map<String, Typeface> typefaceMap =
- SystemFonts.buildSystemTypefaces(fontConfig, fallback);
-
- SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
- synchronized (mSerializedFontMapLock) {
- mSerializedFontMap = serializeFontMap;
+ return null;
+ } finally {
+ // Unmap buffers promptly, as we map a lot of files and may hit mmap limit before
+ // GC collects ByteBuffers and unmaps them.
+ for (ByteBuffer buffer : bufferCache.values()) {
+ if (buffer instanceof DirectByteBuffer) {
+ NioUtils.freeDirectBuffer(buffer);
+ }
}
- } catch (IOException | ErrnoException e) {
- Slog.e(TAG, "Failed to serialize SystemServer system font map", e);
}
}
+ @Nullable
+ private static SharedMemory serializeSystemServerFontMap() {
+ try {
+ return Typeface.serializeFontMap(Typeface.getSystemFontMap());
+ } catch (IOException | ErrnoException e) {
+ Slog.e(TAG, "Failed to serialize SystemServer system font map", e);
+ return null;
+ }
+ }
+
+ private void setSerializedFontMap(SharedMemory serializedFontMap) {
+ SharedMemory oldFontMap = null;
+ synchronized (mSerializedFontMapLock) {
+ oldFontMap = mSerializedFontMap;
+ mSerializedFontMap = serializedFontMap;
+ }
+ if (oldFontMap != null) {
+ oldFontMap.close();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index e4928ce..3fecef7 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -448,7 +448,7 @@
}
}
- private int clear(ShellCommand shell) throws SystemFontException {
+ private int clear(ShellCommand shell) {
mService.clearUpdates();
shell.getOutPrintWriter().println("Success");
return 0;
diff --git a/services/core/java/com/android/server/graphics/fonts/OtfFontFileParser.java b/services/core/java/com/android/server/graphics/fonts/OtfFontFileParser.java
new file mode 100644
index 0000000..1ed3972
--- /dev/null
+++ b/services/core/java/com/android/server/graphics/fonts/OtfFontFileParser.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.graphics.fonts;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Typeface;
+import android.graphics.fonts.Font;
+import android.graphics.fonts.FontFamily;
+import android.graphics.fonts.FontFileUtil;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.text.TextUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.DirectByteBuffer;
+import java.nio.NioUtils;
+import java.nio.channels.FileChannel;
+
+/* package */ class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
+ @Override
+ public String getPostScriptName(File file) throws IOException {
+ ByteBuffer buffer = mmap(file);
+ try {
+ return FontFileUtil.getPostScriptName(buffer, 0);
+ } finally {
+ unmap(buffer);
+ }
+ }
+
+ @Override
+ public String buildFontFileName(File file) throws IOException {
+ ByteBuffer buffer = mmap(file);
+ try {
+ String psName = FontFileUtil.getPostScriptName(buffer, 0);
+ int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0);
+ int isCollection = FontFileUtil.isCollectionFont(buffer);
+
+ if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) {
+ return null;
+ }
+
+ String extension;
+ if (isCollection == 1) {
+ extension = isType1Font == 1 ? ".otc" : ".ttc";
+ } else {
+ extension = isType1Font == 1 ? ".otf" : ".ttf";
+ }
+ return psName + extension;
+ } finally {
+ unmap(buffer);
+ }
+
+ }
+
+ @Override
+ public long getRevision(File file) throws IOException {
+ ByteBuffer buffer = mmap(file);
+ try {
+ return FontFileUtil.getRevision(buffer, 0);
+ } finally {
+ unmap(buffer);
+ }
+ }
+
+ @Override
+ public void tryToCreateTypeface(File file) throws Throwable {
+ ByteBuffer buffer = mmap(file);
+ try {
+ Font font = new Font.Builder(buffer).build();
+ FontFamily family = new FontFamily.Builder(font).build();
+ Typeface typeface = new Typeface.CustomFallbackBuilder(family).build();
+
+ TextPaint p = new TextPaint();
+ p.setTextSize(24f);
+ p.setTypeface(typeface);
+
+ // Test string to try with the passed font.
+ // TODO: Good to extract from font file.
+ String testTextToDraw = "abcXYZ@- "
+ + "\uD83E\uDED6" // Emoji E13.0
+ + "\uD83C\uDDFA\uD83C\uDDF8" // Emoji Flags
+ + "\uD83D\uDC8F\uD83C\uDFFB" // Emoji Skin tone Sequence
+ // ZWJ Sequence
+ + "\uD83D\uDC68\uD83C\uDFFC\u200D\u2764\uFE0F\u200D\uD83D\uDC8B\u200D"
+ + "\uD83D\uDC68\uD83C\uDFFF";
+
+ int width = (int) Math.ceil(Layout.getDesiredWidth(testTextToDraw, p));
+ StaticLayout layout = StaticLayout.Builder.obtain(
+ testTextToDraw, 0, testTextToDraw.length(), p, width).build();
+ Bitmap bmp = Bitmap.createBitmap(
+ layout.getWidth(), layout.getHeight(), Bitmap.Config.ALPHA_8);
+ Canvas canvas = new Canvas(bmp);
+ layout.draw(canvas);
+ } finally {
+ unmap(buffer);
+ }
+ }
+
+ private static ByteBuffer mmap(File file) throws IOException {
+ try (FileInputStream in = new FileInputStream(file)) {
+ FileChannel fileChannel = in.getChannel();
+ return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
+ }
+ }
+
+ private static void unmap(ByteBuffer buffer) {
+ if (buffer instanceof DirectByteBuffer) {
+ NioUtils.freeDirectBuffer(buffer);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index e74dac5..743b4d9 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -59,8 +59,6 @@
private static final String TAG = "UpdatableFontDir";
private static final String RANDOM_DIR_PREFIX = "~~";
- private static final String CONFIG_XML_FILE = "/data/fonts/config/config.xml";
-
/** Interface to mock font file access in tests. */
interface FontFileParser {
String getPostScriptName(File file) throws IOException;
@@ -69,7 +67,7 @@
long getRevision(File file) throws IOException;
- void tryToCreateTypeface(File file) throws IOException;
+ void tryToCreateTypeface(File file) throws Throwable;
}
/** Interface to mock fs-verity in tests. */
@@ -139,8 +137,9 @@
*/
private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>();
- UpdatableFontDir(File filesDir, FontFileParser parser, FsverityUtil fsverityUtil) {
- this(filesDir, parser, fsverityUtil, new File(CONFIG_XML_FILE),
+ UpdatableFontDir(File filesDir, FontFileParser parser, FsverityUtil fsverityUtil,
+ File configFile) {
+ this(filesDir, parser, fsverityUtil, configFile,
System::currentTimeMillis,
(map) -> SystemFonts.getSystemFontConfig(map, 0, 0)
);
@@ -215,17 +214,6 @@
}
}
- /* package */ void clearUpdates() throws SystemFontException {
- mFontFileInfoMap.clear();
- FileUtils.deleteContents(mFilesDir);
-
- mLastModifiedMillis = mCurrentTimeSupplier.get();
- PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
- config.lastModifiedMillis = mLastModifiedMillis;
- writePersistentConfig(config);
- mConfigVersion++;
- }
-
/**
* Applies multiple {@link FontUpdateRequest}s in transaction.
* If one of the request fails, the fonts and config are rolled back to the previous state
@@ -378,10 +366,10 @@
// Try to create Typeface and treat as failure something goes wrong.
try {
mParser.tryToCreateTypeface(fontFileInfo.getFile());
- } catch (IOException e) {
+ } catch (Throwable t) {
throw new SystemFontException(
FontManager.RESULT_ERROR_INVALID_FONT_FILE,
- "Failed to create Typeface from file", e);
+ "Failed to create Typeface from file", t);
}
FontConfig fontConfig = getSystemFontConfig();
@@ -616,4 +604,19 @@
}
return familyMap;
}
+
+ /* package */ static void deleteAllFiles(File filesDir, File configFile) {
+ // As this method is called in safe mode, try to delete all files even though an exception
+ // is thrown.
+ try {
+ new AtomicFile(configFile).delete();
+ } catch (Throwable t) {
+ Slog.w(TAG, "Failed to delete " + configFile);
+ }
+ try {
+ FileUtils.deleteContents(filesDir);
+ } catch (Throwable t) {
+ Slog.w(TAG, "Failed to delete " + filesDir);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 10f6948f..37ee76b7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -136,18 +136,19 @@
if (!mService.isControlEnabled()) {
return;
}
- if (isActiveSource()) {
- mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
- mAddress, mService.getPhysicalAddress()));
- }
boolean wasActiveSource = isActiveSource();
- // Invalidate the internal active source record when goes to standby
+ // Invalidate the internal active source record when going to standby
mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
"HdmiCecLocalDevicePlayback#onStandby()");
boolean mTvSendStandbyOnSleep = mService.getHdmiCecConfig().getIntValue(
HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
== HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
- if (initiatedByCec || !mTvSendStandbyOnSleep || !wasActiveSource) {
+ if (!wasActiveSource) {
+ return;
+ }
+ if (initiatedByCec || !mTvSendStandbyOnSleep) {
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(mAddress,
+ mService.getPhysicalAddress()));
return;
}
switch (standbyAction) {
@@ -167,6 +168,9 @@
Constants.ADDR_BROADCAST));
break;
case HdmiControlManager.POWER_CONTROL_MODE_NONE:
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildInactiveSource(mAddress,
+ mService.getPhysicalAddress()));
break;
}
break;
@@ -258,18 +262,6 @@
return super.handleUserControlPressed(message);
}
- @Override
- protected void wakeUpIfActiveSource() {
- if (!isActiveSource()) {
- return;
- }
- // Wake up the device if the power is in standby mode, or its screen is off -
- // which can happen if the device is holding a partial lock.
- if (mService.isPowerStandbyOrTransient() || !mService.getPowerManager().isScreenOn()) {
- mService.wakeUp();
- }
- }
-
@ServiceThreadOnly
@Constants.HandleMessageResult
protected int handleSetMenuLanguage(HdmiCecMessage message) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 702f854..1c726e0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -380,7 +380,7 @@
if (!isActiveSource()) {
return;
}
- // Wake up the device
+ // Wake up the device. This will also exit dream mode.
mService.wakeUp();
return;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 77de187..2ed160a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -442,18 +442,85 @@
public HdmiControlService(Context context) {
super(context);
- List<Integer> deviceTypes = HdmiProperties.device_type();
- if (deviceTypes.contains(null)) {
- Slog.w(TAG, "Error parsing ro.hdmi.device.type: " + SystemProperties.get(
- "ro.hdmi.device_type"));
- deviceTypes = deviceTypes.stream().filter(Objects::nonNull).collect(
- Collectors.toList());
- }
- mLocalDevices = deviceTypes;
+ mLocalDevices = readDeviceTypes();
mSettingsObserver = new SettingsObserver(mHandler);
mHdmiCecConfig = new HdmiCecConfig(context);
}
+ @VisibleForTesting
+ protected List<HdmiProperties.cec_device_types_values> getCecDeviceTypes() {
+ return HdmiProperties.cec_device_types();
+ }
+
+ @VisibleForTesting
+ protected List<Integer> getDeviceTypes() {
+ return HdmiProperties.device_type();
+ }
+
+ /**
+ * Extracts a list of integer device types from the sysprop ro.hdmi.cec_device_types.
+ * If ro.hdmi.cec_device_types is not set, reads from ro.hdmi.device.type instead.
+ * @return the list of integer device types
+ */
+ @VisibleForTesting
+ protected List<Integer> readDeviceTypes() {
+ List<HdmiProperties.cec_device_types_values> cecDeviceTypes = getCecDeviceTypes();
+ if (!cecDeviceTypes.isEmpty()) {
+ if (cecDeviceTypes.contains(null)) {
+ Slog.w(TAG, "Error parsing ro.hdmi.cec_device_types: " + SystemProperties.get(
+ "ro.hdmi.cec_device_types"));
+ }
+ return cecDeviceTypes.stream()
+ .map(HdmiControlService::enumToIntDeviceType)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ } else {
+ // If ro.hdmi.cec_device_types isn't set, fall back to reading ro.hdmi.device_type
+ List<Integer> deviceTypes = getDeviceTypes();
+ if (deviceTypes.contains(null)) {
+ Slog.w(TAG, "Error parsing ro.hdmi.device_type: " + SystemProperties.get(
+ "ro.hdmi.device_type"));
+ }
+ return deviceTypes.stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+ }
+
+ /**
+ * Converts an enum representing a value in ro.hdmi.cec_device_types to an integer device type.
+ * Returns null if the input is null or an unrecognized device type.
+ */
+ @Nullable
+ private static Integer enumToIntDeviceType(
+ @Nullable HdmiProperties.cec_device_types_values cecDeviceType) {
+ if (cecDeviceType == null) {
+ return null;
+ }
+ switch (cecDeviceType) {
+ case TV:
+ return HdmiDeviceInfo.DEVICE_TV;
+ case RECORDING_DEVICE:
+ return HdmiDeviceInfo.DEVICE_RECORDER;
+ case RESERVED:
+ return HdmiDeviceInfo.DEVICE_RESERVED;
+ case TUNER:
+ return HdmiDeviceInfo.DEVICE_TUNER;
+ case PLAYBACK_DEVICE:
+ return HdmiDeviceInfo.DEVICE_PLAYBACK;
+ case AUDIO_SYSTEM:
+ return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
+ case PURE_CEC_SWITCH:
+ return HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH;
+ case VIDEO_PROCESSOR:
+ return HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR;
+ default:
+ Slog.w(TAG, "Unrecognized device type in ro.hdmi.cec_device_types: "
+ + cecDeviceType.getPropValue());
+ return null;
+ }
+ }
+
protected static List<Integer> getIntList(String string) {
ArrayList<Integer> list = new ArrayList<>();
TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
@@ -3030,6 +3097,7 @@
@ServiceThreadOnly
@VisibleForTesting
protected void onStandby(final int standbyAction) {
+ mWakeUpMessageReceived = false;
assertRunOnServiceThread();
mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY,
false);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
index 7226cc2..e52e32a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
@@ -90,6 +90,8 @@
pw.println(" Set the value of a CEC setting");
pw.println(" setsystemaudiomode, setsam [on|off]");
pw.println(" Sets the System Audio Mode feature on or off on TV devices");
+ pw.println(" setarc [on|off]");
+ pw.println(" Sets the ARC feature on or off on TV devices");
}
private int handleShellCommand(String cmd) throws RemoteException {
@@ -106,6 +108,8 @@
case "setsystemaudiomode":
case "setsam":
return setSystemAudioMode(pw);
+ case "setarc":
+ return setArcMode(pw);
}
getErrPrintWriter().println("Unhandled command: " + cmd);
@@ -229,6 +233,27 @@
return mCecResult.get() == HdmiControlManager.RESULT_SUCCESS ? 0 : 1;
}
+ private int setArcMode(PrintWriter pw) throws RemoteException {
+ if (1 > getRemainingArgsCount()) {
+ throw new IllegalArgumentException(
+ "Please indicate if ARC mode should be turned \"on\" or \"off\".");
+ }
+
+ String arg = getNextArg();
+ if (arg.equals("on")) {
+ pw.println("Setting ARC mode on");
+ mBinderService.setArcMode(true);
+ } else if (arg.equals("off")) {
+ pw.println("Setting ARC mode off");
+ mBinderService.setArcMode(false);
+ } else {
+ throw new IllegalArgumentException(
+ "Please indicate if ARC mode should be turned \"on\" or \"off\".");
+ }
+
+ return 0;
+ }
+
private boolean receiveCallback(String command) {
try {
if (!mLatch.await(HdmiConfig.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 1c27c65..61107b2 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -313,6 +313,7 @@
private static native void nativeSetFocusedDisplay(long ptr, int displayId);
private static native boolean nativeTransferTouchFocus(long ptr,
IBinder fromChannelToken, IBinder toChannelToken, boolean isDragDrop);
+ private static native boolean nativeTransferTouch(long ptr, IBinder destChannelToken);
private static native void nativeSetPointerSpeed(long ptr, int speed);
private static native void nativeSetShowTouches(long ptr, boolean enabled);
private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -676,6 +677,19 @@
}
/**
+ * Transfer the current touch gesture to the provided window.
+ *
+ * @param destChannelToken The token of the window or input channel that should receive the
+ * gesture
+ * @return True if the transfer succeeded, false if there was no active touch gesture happening
+ */
+ public boolean transferTouch(IBinder destChannelToken) {
+ // TODO(b/162194035): Replace this with a SPY window
+ Objects.requireNonNull(destChannelToken, "destChannelToken must not be null.");
+ return nativeTransferTouch(mPtr, destChannelToken);
+ }
+
+ /**
* Creates an input channel that will receive all input from the input dispatcher.
* @param inputChannelName The input channel name.
* @param displayId Target display id.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 518c428..94e669a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5761,6 +5761,7 @@
final String imeId = shellCommand.getNextArgRequired();
final PrintWriter out = shellCommand.getOutPrintWriter();
final PrintWriter error = shellCommand.getErrPrintWriter();
+ boolean hasFailed = false;
synchronized (mMethodMap) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
@@ -5768,11 +5769,11 @@
if (!userHasDebugPriv(userId, shellCommand)) {
continue;
}
- handleShellCommandEnableDisableInputMethodInternalLocked(userId, imeId, enabled,
- out, error);
+ hasFailed |= !handleShellCommandEnableDisableInputMethodInternalLocked(
+ userId, imeId, enabled, out, error);
}
}
- return ShellCommandResult.SUCCESS;
+ return hasFailed ? ShellCommandResult.FAILURE : ShellCommandResult.SUCCESS;
}
/**
@@ -5804,8 +5805,18 @@
return UserHandle.USER_CURRENT;
}
+ /**
+ * Handles core logic of {@code adb shell ime enable} and {@code adb shell ime disable}.
+ *
+ * @param userId user ID specified to the command. Pseudo user IDs are not supported.
+ * @param imeId IME ID specified to the command.
+ * @param enabled {@code true} for {@code adb shell ime enable}. {@code false} otherwise.
+ * @param out {@link PrintWriter} to output standard messages.
+ * @param error {@link PrintWriter} to output error messages.
+ * @return {@code false} if it fails to enable the IME. {@code false} otherwise.
+ */
@BinderThread
- private void handleShellCommandEnableDisableInputMethodInternalLocked(
+ private boolean handleShellCommandEnableDisableInputMethodInternalLocked(
@UserIdInt int userId, String imeId, boolean enabled, PrintWriter out,
PrintWriter error) {
boolean failedToEnableUnknownIme = false;
@@ -5851,15 +5862,20 @@
error.print("Unknown input method ");
error.print(imeId);
error.println(" cannot be enabled for user #" + userId);
- } else {
- out.print("Input method ");
- out.print(imeId);
- out.print(": ");
- out.print((enabled == previouslyEnabled) ? "already " : "now ");
- out.print(enabled ? "enabled" : "disabled");
- out.print(" for user #");
- out.println(userId);
+ // Also print this failure into logcat for better debuggability.
+ Slog.e(TAG, "\"ime enable " + imeId + "\" for user #" + userId
+ + " failed due to its unrecognized IME ID.");
+ return false;
}
+
+ out.print("Input method ");
+ out.print(imeId);
+ out.print(": ");
+ out.print((enabled == previouslyEnabled) ? "already " : "now ");
+ out.print(enabled ? "enabled" : "disabled");
+ out.print(" for user #");
+ out.println(userId);
+ return true;
}
/**
@@ -5874,6 +5890,7 @@
final String imeId = shellCommand.getNextArgRequired();
final PrintWriter out = shellCommand.getOutPrintWriter();
final PrintWriter error = shellCommand.getErrPrintWriter();
+ boolean hasFailed = false;
synchronized (mMethodMap) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
@@ -5887,15 +5904,19 @@
error.print(imeId);
error.print(" cannot be selected for user #");
error.println(userId);
+ // Also print this failure into logcat for better debuggability.
+ Slog.e(TAG, "\"ime set " + imeId + "\" for user #" + userId
+ + " failed due to its unrecognized IME ID.");
} else {
out.print("Input method ");
out.print(imeId);
out.print(" selected for user #");
out.println(userId);
}
+ hasFailed |= failedToSelectUnknownIme;
}
}
- return ShellCommandResult.SUCCESS;
+ return hasFailed ? ShellCommandResult.FAILURE : ShellCommandResult.SUCCESS;
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 2969e53..c340a2b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -196,7 +196,7 @@
final int numImes = imis.size();
for (int i = 0; i < numImes; ++i) {
final InputMethodInfo imi = imis.get(i);
- if (forImeMenu && !imi.showInInputMethodPicker()) {
+ if (forImeMenu && !imi.shouldShowInInputMethodPicker()) {
continue;
}
final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 172a68a..1e8d904 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -139,6 +139,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -146,7 +147,8 @@
* The service class that manages LocationProviders and issues location
* updates and alerts.
*/
-public class LocationManagerService extends ILocationManager.Stub {
+public class LocationManagerService extends ILocationManager.Stub implements
+ LocationProviderManager.StateChangedListener {
/**
* Controls lifecycle of LocationManagerService.
@@ -228,7 +230,7 @@
private static final String ATTRIBUTION_TAG = "LocationService";
- private final Object mLock = new Object();
+ final Object mLock = new Object();
private final Context mContext;
private final Injector mInjector;
@@ -257,7 +259,7 @@
new CopyOnWriteArrayList<>();
@GuardedBy("mLock")
- private @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener;
+ @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener;
LocationManagerService(Context context, Injector injector) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
@@ -269,6 +271,13 @@
mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
this::onLocationModeChanged);
+ mInjector.getSettingsHelper().addOnIgnoreSettingsPackageWhitelistChangedListener(
+ () -> refreshAppOpsRestrictions(UserHandle.USER_ALL));
+ mInjector.getUserInfoHelper().addListener((userId, change) -> {
+ if (change == UserInfoHelper.UserListener.USER_STARTED) {
+ refreshAppOpsRestrictions(userId);
+ }
+ });
// set up passive provider first since it will be required for all other location providers,
// which are loaded later once the system is ready.
@@ -324,9 +333,8 @@
synchronized (mProviderManagers) {
Preconditions.checkState(getLocationProviderManager(manager.getName()) == null);
- manager.startManager();
- manager.setOnProviderLocationTagsChangeListener(
- mOnProviderLocationTagsChangeListener);
+ manager.startManager(this);
+
if (realProvider != null) {
// custom logic wrapping all non-passive providers
if (manager != mPassiveManager) {
@@ -482,6 +490,8 @@
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+
+ refreshAppOpsRestrictions(userId);
}
@Override
@@ -1347,6 +1357,95 @@
ipw.decreaseIndent();
}
+ @Override
+ public void onStateChanged(String provider, AbstractLocationProvider.State oldState,
+ AbstractLocationProvider.State newState) {
+ if (!Objects.equals(oldState.identity, newState.identity)) {
+ refreshAppOpsRestrictions(UserHandle.USER_ALL);
+ }
+
+ OnProviderLocationTagsChangeListener listener;
+ synchronized (mLock) {
+ listener = mOnProviderLocationTagsChangeListener;
+ }
+
+ if (listener != null) {
+ if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
+ || !Objects.equals(oldState.identity, newState.identity)) {
+ if (oldState.identity != null) {
+ listener.onLocationTagsChanged(
+ new LocationManagerInternal.LocationTagInfo(
+ oldState.identity.getUid(),
+ oldState.identity.getPackageName(),
+ Collections.emptySet()));
+ }
+ if (newState.identity != null) {
+ ArraySet<String> attributionTags = new ArraySet<>(
+ newState.extraAttributionTags.size() + 1);
+ attributionTags.addAll(newState.extraAttributionTags);
+ attributionTags.add(newState.identity.getAttributionTag());
+
+ listener.onLocationTagsChanged(
+ new LocationManagerInternal.LocationTagInfo(
+ newState.identity.getUid(),
+ newState.identity.getPackageName(),
+ attributionTags));
+ }
+ }
+ }
+ }
+
+ private void refreshAppOpsRestrictions(int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ final int[] runningUserIds = mInjector.getUserInfoHelper().getRunningUserIds();
+ for (int i = 0; i < runningUserIds.length; i++) {
+ refreshAppOpsRestrictions(runningUserIds[i]);
+ }
+ return;
+ }
+
+ Preconditions.checkArgument(userId >= 0);
+
+ boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
+
+ ArrayMap<String, String[]> allowedPackages = null;
+ if (!enabled) {
+ ArrayMap<String, ArraySet<String>> packages = new ArrayMap<>();
+ for (LocationProviderManager manager : mProviderManagers) {
+ CallerIdentity identity = manager.getIdentity();
+ if (identity != null) {
+ packages.computeIfAbsent(identity.getPackageName(), k -> new ArraySet<>()).add(
+ identity.getAttributionTag());
+ }
+ }
+ for (String packageName :
+ mInjector.getSettingsHelper().getIgnoreSettingsPackageWhitelist()) {
+ packages.computeIfAbsent(packageName, k -> new ArraySet<>());
+ }
+ packages.computeIfAbsent(mContext.getPackageName(), k -> new ArraySet<>());
+
+ allowedPackages = new ArrayMap<>();
+ for (Map.Entry<String, ArraySet<String>> entry : packages.entrySet()) {
+ allowedPackages.put(entry.getKey(), entry.getValue().toArray(new String[0]));
+ }
+ }
+
+ AppOpsManager appOpsManager = Objects.requireNonNull(
+ mContext.getSystemService(AppOpsManager.class));
+ appOpsManager.setUserRestrictionForUser(
+ AppOpsManager.OP_COARSE_LOCATION,
+ !enabled,
+ LocationManagerService.this,
+ allowedPackages,
+ userId);
+ appOpsManager.setUserRestrictionForUser(
+ AppOpsManager.OP_FINE_LOCATION,
+ !enabled,
+ LocationManagerService.this,
+ allowedPackages,
+ userId);
+ }
+
private class LocalService extends LocationManagerInternal {
LocalService() {}
@@ -1421,11 +1520,6 @@
@Nullable OnProviderLocationTagsChangeListener listener) {
synchronized (mLock) {
mOnProviderLocationTagsChangeListener = listener;
- final int providerCount = mProviderManagers.size();
- for (int i = 0; i < providerCount; i++) {
- final LocationProviderManager manager = mProviderManagers.get(i);
- manager.setOnProviderLocationTagsChangeListener(listener);
- }
}
}
}
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 5dc3ed8..9378493 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -20,6 +20,7 @@
import android.location.Criteria;
import android.location.Location;
import android.location.provider.ProviderProperties;
+import android.os.SystemClock;
import android.os.UserHandle;
import com.android.modules.utils.BasicShellCommandHandler;
@@ -236,7 +237,7 @@
Location location = new Location(provider);
location.setAccuracy(DEFAULT_TEST_LOCATION_ACCURACY);
location.setTime(System.currentTimeMillis());
- location.setElapsedRealtimeNanos(System.nanoTime());
+ location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
do {
String option = getNextOption();
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index ca53431..a4a59564 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -55,8 +55,6 @@
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
-import android.location.LocationManagerInternal.LocationTagInfo;
-import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
@@ -90,7 +88,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.location.LocationPermissions;
@@ -122,7 +119,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
@@ -171,6 +167,11 @@
private static final int STATE_STOPPING = 1;
private static final int STATE_STOPPED = 2;
+ public interface StateChangedListener {
+ void onStateChanged(String provider, AbstractLocationProvider.State oldState,
+ AbstractLocationProvider.State newState);
+ }
+
protected interface LocationTransport {
void deliverOnLocationChanged(LocationResult locationResult,
@@ -1315,7 +1316,7 @@
private @Nullable OnAlarmListener mDelayedRegister;
@GuardedBy("mLock")
- private @Nullable OnProviderLocationTagsChangeListener mOnLocationTagsChangeListener;
+ private @Nullable StateChangedListener mStateChangedListener;
public LocationProviderManager(Context context, Injector injector,
String name, @Nullable PassiveLocationProviderManager passiveManager) {
@@ -1354,10 +1355,11 @@
return TAG;
}
- public void startManager() {
+ public void startManager(@Nullable StateChangedListener listener) {
synchronized (mLock) {
Preconditions.checkState(mState == STATE_STOPPED);
mState = STATE_STARTED;
+ mStateChangedListener = listener;
mUserHelper.addListener(mUserChangedListener);
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
@@ -1396,6 +1398,7 @@
mEnabled.clear();
mLastLocations.clear();
+ mStateChangedListener = null;
mState = STATE_STOPPED;
}
}
@@ -1476,19 +1479,6 @@
}
}
- /**
- * Registers a listener for the location tags of the provider.
- *
- * @param listener The listener
- */
- public void setOnProviderLocationTagsChangeListener(
- @Nullable OnProviderLocationTagsChangeListener listener) {
- Preconditions.checkArgument(mOnLocationTagsChangeListener == null || listener == null);
- synchronized (mLock) {
- mOnLocationTagsChangeListener = listener;
- }
- }
-
public void setMockProvider(@Nullable MockLocationProvider provider) {
synchronized (mLock) {
Preconditions.checkState(mState != STATE_STOPPED);
@@ -2292,31 +2282,10 @@
updateRegistrations(Registration::onProviderPropertiesChanged);
}
- if (mOnLocationTagsChangeListener != null) {
- if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
- || !Objects.equals(oldState.identity, newState.identity)) {
- if (oldState.identity != null) {
- FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
- OnProviderLocationTagsChangeListener::onLocationTagsChanged,
- mOnLocationTagsChangeListener, new LocationTagInfo(
- oldState.identity.getUid(), oldState.identity.getPackageName(),
- Collections.emptySet())
- ));
- }
- if (newState.identity != null) {
- ArraySet<String> attributionTags = new ArraySet<>(
- newState.extraAttributionTags.size() + 1);
- attributionTags.addAll(newState.extraAttributionTags);
- attributionTags.add(newState.identity.getAttributionTag());
-
- FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
- OnProviderLocationTagsChangeListener::onLocationTagsChanged,
- mOnLocationTagsChangeListener, new LocationTagInfo(
- newState.identity.getUid(), newState.identity.getPackageName(),
- attributionTags)
- ));
- }
- }
+ if (mStateChangedListener != null) {
+ StateChangedListener listener = mStateChangedListener;
+ FgThread.getExecutor().execute(
+ () -> listener.onStateChanged(mName, oldState, newState));
}
}
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index 5abc438..2bdeab4 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -98,19 +98,7 @@
}
@Override
- public void onChallengeInterrupted(int sensorId) {
- Slog.w(TAG, "Challenge interrupted, sensor: " + sensorId);
- // Consider re-attempting generateChallenge/resetLockout/revokeChallenge
- // when onChallengeInterruptFinished is invoked
- }
-
- @Override
- public void onChallengeInterruptFinished(int sensorId) {
- Slog.w(TAG, "Challenge interrupt finished, sensor: " + sensorId);
- }
-
- @Override
- public void onGenerateChallengeResult(int sensorId, long challenge) {
+ public void onGenerateChallengeResult(int sensorId, int userId, long challenge) {
if (!sensorIds.contains(sensorId)) {
Slog.e(TAG, "Unknown sensorId received: " + sensorId);
return;
@@ -128,10 +116,6 @@
}
sensorIds.remove(sensorId);
- // Challenge is only required for IBiometricsFace@1.0 (and not IFace AIDL). The
- // IBiometricsFace@1.0 HAL does not require userId to revokeChallenge, so passing
- // in 0 is OK.
- final int userId = 0;
faceManager.revokeChallenge(sensorId, userId, challenge);
if (sensorIds.isEmpty()) {
@@ -234,18 +218,12 @@
}
}
- /**
- * For devices on {@link android.hardware.biometrics.face.V1_0} which only support a single
- * in-flight challenge, we generate a single challenge to reset lockout for all profiles. This
- * hopefully reduces/eliminates issues such as overwritten challenge, incorrectly revoked
- * challenge, or other race conditions.
- */
private void processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts) {
if (mFaceManager != null) {
if (mFaceResetLockoutTask != null) {
// This code will need to be updated if this problem ever occurs.
- Slog.w(TAG, "mFaceGenerateChallengeCallback not null, previous operation may be"
- + " stuck");
+ Slog.w(TAG,
+ "mFaceGenerateChallengeCallback not null, previous operation may be stuck");
}
final List<FaceSensorPropertiesInternal> faceSensorProperties =
mFaceManager.getSensorPropertiesInternal();
@@ -258,12 +236,13 @@
mSpManager, sensorIds, pendingResetLockouts);
for (final FaceSensorPropertiesInternal prop : faceSensorProperties) {
if (prop.resetLockoutRequiresHardwareAuthToken) {
- if (prop.resetLockoutRequiresChallenge) {
- // Generate a challenge for each sensor. The challenge does not need to be
- // per-user, since the HAT returned by gatekeeper contains userId.
- mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask);
- } else {
- for (UserAuthInfo user : pendingResetLockouts) {
+ for (UserAuthInfo user : pendingResetLockouts) {
+ if (prop.resetLockoutRequiresChallenge) {
+ Slog.d(TAG, "Generating challenge for sensor: " + prop.sensorId
+ + ", user: " + user.userId);
+ mFaceManager.generateChallenge(prop.sensorId, user.userId,
+ mFaceResetLockoutTask);
+ } else {
Slog.d(TAG, "Resetting face lockout for sensor: " + prop.sensorId
+ ", user: " + user.userId);
final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 805f395..8c1fd36 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1511,7 +1511,8 @@
builder.setSmallIcon(R.drawable.stat_notify_error);
- final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
+ final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template,
+ mContext.getPackageName());
builder.setDeleteIntent(PendingIntent.getBroadcast(
mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
@@ -1597,7 +1598,8 @@
builder.setSmallIcon(R.drawable.stat_notify_error);
- final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template);
+ final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template,
+ mContext.getPackageName());
builder.setDeleteIntent(PendingIntent.getBroadcast(
mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
@@ -5478,17 +5480,19 @@
return new Intent(ACTION_ALLOW_BACKGROUND);
}
- private static Intent buildSnoozeWarningIntent(NetworkTemplate template) {
+ private static Intent buildSnoozeWarningIntent(NetworkTemplate template, String targetPackage) {
final Intent intent = new Intent(ACTION_SNOOZE_WARNING);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+ intent.setPackage(targetPackage);
return intent;
}
- private static Intent buildSnoozeRapidIntent(NetworkTemplate template) {
+ private static Intent buildSnoozeRapidIntent(NetworkTemplate template, String targetPackage) {
final Intent intent = new Intent(ACTION_SNOOZE_RAPID);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+ intent.setPackage(targetPackage);
return intent;
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 202b315..a3daae4 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -101,6 +101,7 @@
protected static final String ENABLED_SERVICES_SEPARATOR = ":";
private static final String DB_VERSION_1 = "1";
private static final String DB_VERSION_2 = "2";
+ private static final String DB_VERSION_3 = "3";
/**
@@ -113,8 +114,9 @@
static final String ATT_VERSION = "version";
static final String ATT_DEFAULTS = "defaults";
static final String ATT_USER_SET = "user_set_services";
+ static final String ATT_USER_CHANGED = "user_changed";
- static final int DB_VERSION = 3;
+ static final int DB_VERSION = 4;
static final int APPROVAL_BY_PACKAGE = 0;
static final int APPROVAL_BY_COMPONENT = 1;
@@ -160,6 +162,8 @@
@GuardedBy("mApproved")
protected ArrayMap<Integer, ArraySet<String>> mUserSetServices = new ArrayMap<>();
+ protected ArrayMap<Integer, Boolean> mIsUserChanged = new ArrayMap<>();
+
// True if approved services are stored in xml, not settings.
private boolean mUseXml;
@@ -338,6 +342,7 @@
for (int i = 0; i < N; i++) {
final int userId = mApproved.keyAt(i);
final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+ final Boolean userChanged = mIsUserChanged.get(userId);
if (approvedByType != null) {
final int M = approvedByType.size();
for (int j = 0; j < M; j++) {
@@ -345,16 +350,20 @@
final ArraySet<String> approved = approvedByType.valueAt(j);
if (approvedByType != null && approvedByType.size() > 0) {
pw.println(" " + String.join(ENABLED_SERVICES_SEPARATOR, approved)
- + " (user: " + userId + " isPrimary: " + isPrimary + ")");
+ + " (user: " + userId + " isPrimary: " + isPrimary
+ + (userChanged == null ? "" : " isUserChanged: "
+ + userChanged) + ")");
}
}
}
}
-
pw.println(" Has user set:");
Set<Integer> userIds = mUserSetServices.keySet();
for (int userId : userIds) {
- pw.println(" userId=" + userId + " value=" + mUserSetServices.get(userId));
+ if (mIsUserChanged.get(userId) == null) {
+ pw.println(" userId=" + userId + " value="
+ + (mUserSetServices.get(userId)));
+ }
}
}
@@ -489,13 +498,14 @@
continue;
}
final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+ final Boolean isUserChanged = mIsUserChanged.get(approvedUserId);
if (approvedByType != null) {
final int M = approvedByType.size();
for (int j = 0; j < M; j++) {
final boolean isPrimary = approvedByType.keyAt(j);
final Set<String> approved = approvedByType.valueAt(j);
final Set<String> userSet = mUserSetServices.get(approvedUserId);
- if (approved != null || userSet != null) {
+ if (approved != null || userSet != null || isUserChanged != null) {
String allowedItems = approved == null
? ""
: String.join(ENABLED_SERVICES_SEPARATOR, approved);
@@ -503,7 +513,9 @@
out.attribute(null, ATT_APPROVED_LIST, allowedItems);
out.attributeInt(null, ATT_USER_ID, approvedUserId);
out.attributeBoolean(null, ATT_IS_PRIMARY, isPrimary);
- if (userSet != null) {
+ if (isUserChanged != null) {
+ out.attributeBoolean(null, ATT_USER_CHANGED, isUserChanged);
+ } else if (userSet != null) {
String userSetItems =
String.join(ENABLED_SERVICES_SEPARATOR, userSet);
out.attribute(null, ATT_USER_SET, userSetItems);
@@ -618,12 +630,21 @@
? userId : parser.getAttributeInt(null, ATT_USER_ID, 0);
final boolean isPrimary =
parser.getAttributeBoolean(null, ATT_IS_PRIMARY, true);
- final String userSet = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
+
+ final String isUserChanged = XmlUtils.readStringAttribute(parser,
+ ATT_USER_CHANGED);
+ String userSetComponent = null;
+ if (isUserChanged == null) {
+ userSetComponent = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
+ } else {
+ mIsUserChanged.put(resolvedUserId, Boolean.valueOf(isUserChanged));
+ }
readExtraAttributes(tag, parser, resolvedUserId);
if (allowedManagedServicePackages == null || allowedManagedServicePackages.test(
- getPackageName(approved), resolvedUserId, getRequiredPermission())) {
+ getPackageName(approved), resolvedUserId, getRequiredPermission())
+ || approved.isEmpty()) {
if (mUm.getUserInfo(resolvedUserId) != null) {
- addApprovedList(approved, resolvedUserId, isPrimary, userSet);
+ addApprovedList(approved, resolvedUserId, isPrimary, userSetComponent);
}
mUseXml = true;
}
@@ -634,10 +655,16 @@
}
boolean isOldVersion = TextUtils.isEmpty(version)
|| DB_VERSION_1.equals(version)
- || DB_VERSION_2.equals(version);
+ || DB_VERSION_2.equals(version)
+ || DB_VERSION_3.equals(version);
+ boolean needUpgradeUserset = DB_VERSION_3.equals(version);
if (isOldVersion) {
upgradeDefaultsXmlVersion();
}
+ if (needUpgradeUserset) {
+ upgradeUserSet();
+ }
+
rebindServices(false, USER_ALL);
}
@@ -666,6 +693,8 @@
}
}
+ protected void upgradeUserSet() {};
+
/**
* Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag.
*/
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 67fd09b3..0700a9f 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -106,7 +106,6 @@
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.internal.util.CollectionUtils.emptyIfNull;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -324,7 +323,6 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
@@ -832,7 +830,7 @@
learnNASPendingIntent).build();
- return new Notification.Builder(getContext(), SystemNotificationChannels.ALERTS)
+ return new Notification.Builder(getContext(), SystemNotificationChannels.SYSTEM_CHANGES)
.setAutoCancel(false)
.setOngoing(true)
.setTicker(getContext().getResources().getString(title))
@@ -9467,6 +9465,27 @@
return mDefaultFromConfig;
}
+ @Override
+ protected void upgradeUserSet() {
+ for (int userId: mApproved.keySet()) {
+ ArraySet<String> userSetServices = mUserSetServices.get(userId);
+ mIsUserChanged.put(userId, (userSetServices != null && userSetServices.size() > 0));
+ }
+ }
+
+ @Override
+ protected void addApprovedList(String approved, int userId, boolean isPrimary,
+ String userSet) {
+ if (!TextUtils.isEmpty(approved)) {
+ String[] approvedArray = approved.split(ENABLED_SERVICES_SEPARATOR);
+ if (approvedArray.length > 1) {
+ Slog.d(TAG, "More than one approved assistants");
+ approved = approvedArray[0];
+ }
+ }
+ super.addApprovedList(approved, userId, isPrimary, userSet);
+ }
+
public NotificationAssistants(Context context, Object lock, UserProfiles up,
IPackageManager pm) {
super(context, lock, up, pm);
@@ -9641,47 +9660,12 @@
}
boolean hasUserSet(int userId) {
- synchronized (mLock) {
- ArraySet<String> userSetServices = mUserSetServices.get(userId);
- if (userSetServices == null) {
- // Legacy case - no data means user-set, unless no assistant is set
- return !mApproved.isEmpty();
- }
- Map<Boolean, ArraySet<String>> approvedByType = emptyIfNull(mApproved.get(userId));
- return userSetServices.containsAll(emptyIfNull(approvedByType.get(true)))
- && userSetServices.containsAll(emptyIfNull(approvedByType.get(false)));
- }
+ Boolean userSet = mIsUserChanged.get(userId);
+ return (userSet != null && userSet);
}
void setUserSet(int userId, boolean set) {
- synchronized (mLock) {
- ArraySet<String> userSetServices = new ArraySet<>();
- if (set) {
- ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
- if (approvedByType != null) {
- for (int i = 0; i < approvedByType.size(); i++) {
- userSetServices.addAll(approvedByType.valueAt(i));
- }
- }
- }
- mUserSetServices.put(userId, userSetServices);
- }
- }
-
- @Override
- protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId)
- throws IOException {
- // TODO: this logic looks broken, since it's trying to convert a
- // list into a boolean; for now we preserve the old parsing behavior
- // to avoid a performance regression, but someone should investigate
- final String value = parser.getAttributeValue(null, ATT_USER_SET);
- final boolean userSet;
- if (TextUtils.isEmpty(value)) {
- userSet = false;
- } else {
- userSet = Boolean.parseBoolean(value);
- }
- setUserSet(userId, userSet);
+ mIsUserChanged.put(userId, set);
}
private void notifyCapabilitiesChanged(final ManagedServiceInfo info) {
@@ -10000,6 +9984,7 @@
static final String TAG_APPROVED = "allowed";
static final String TAG_DISALLOWED= "disallowed";
static final String XML_SEPARATOR = ",";
+ static final String FLAG_SEPARATOR = "\\|";
private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
ArrayMap<Pair<ComponentName, Integer>, NotificationListenerFilter>
@@ -10271,7 +10256,7 @@
private int getTypesFromStringList(String typeList) {
int types = 0;
if (typeList != null) {
- String[] typeStrings = typeList.split(XML_SEPARATOR);
+ String[] typeStrings = typeList.split(FLAG_SEPARATOR);
for (int i = 0; i < typeStrings.length; i++) {
final String typeString = typeStrings[i];
if (TextUtils.isEmpty(typeString)) {
diff --git a/services/core/java/com/android/server/os/OWNERS b/services/core/java/com/android/server/os/OWNERS
new file mode 100644
index 0000000..1957332
--- /dev/null
+++ b/services/core/java/com/android/server/os/OWNERS
@@ -0,0 +1,2 @@
+# Bugreporting
+per-file Bugreport* = file:/platform/frameworks/native:/cmds/dumpstate/OWNERS
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index cd352b5..7b1fa14 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -84,6 +84,7 @@
// Logs all filtering instead of enforcing
private static final boolean DEBUG_ALLOW_ALL = false;
private static final boolean DEBUG_LOGGING = false;
+ private static final boolean DEBUG_TRACING = false;
/**
* This contains a list of app UIDs that are implicitly queryable because another app explicitly
@@ -363,21 +364,29 @@
@Override
public boolean isGloballyEnabled() {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "isGloballyEnabled");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "isGloballyEnabled");
+ }
try {
return mFeatureEnabled;
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
}
@Override
public boolean packageIsEnabled(AndroidPackage pkg) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "packageIsEnabled");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "packageIsEnabled");
+ }
try {
return !mDisabledPackages.contains(pkg.getPackageName());
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
}
@@ -656,7 +665,9 @@
* @param isReplace if the package is being replaced and may need extra cleanup.
*/
public void addPackage(PackageSetting newPkgSetting, boolean isReplace) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
+ }
try {
if (isReplace) {
// let's first remove any prior rules for this package
@@ -689,7 +700,9 @@
});
} finally {
onChanged();
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
}
@@ -1173,7 +1186,9 @@
*/
public boolean shouldFilterApplication(int callingUid, @Nullable SettingBase callingSetting,
PackageSetting targetPkgSetting, int userId) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication");
+ }
try {
int callingAppId = UserHandle.getAppId(callingUid);
if (callingAppId < Process.FIRST_APPLICATION_UID
@@ -1211,13 +1226,17 @@
}
return !DEBUG_ALLOW_ALL;
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
}
private boolean shouldFilterApplicationInternal(int callingUid, SettingBase callingSetting,
PackageSetting targetPkgSetting, int targetUserId) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
+ }
try {
final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
if (!featureEnabled) {
@@ -1232,7 +1251,9 @@
}
final PackageSetting callingPkgSetting;
final ArraySet<PackageSetting> callingSharedPkgSettings;
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof");
+ }
if (callingSetting instanceof PackageSetting) {
if (((PackageSetting) callingSetting).sharedUser == null) {
callingPkgSetting = (PackageSetting) callingSetting;
@@ -1246,7 +1267,9 @@
callingPkgSetting = null;
callingSharedPkgSettings = ((SharedUserSetting) callingSetting).packages;
}
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
if (callingPkgSetting != null) {
if (callingPkgSetting.pkg != null
@@ -1282,7 +1305,9 @@
return false;
}
final String targetName = targetPkg.getPackageName();
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "getAppId");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "getAppId");
+ }
final int callingAppId;
if (callingPkgSetting != null) {
callingAppId = callingPkgSetting.appId;
@@ -1290,7 +1315,9 @@
callingAppId = callingSharedPkgSettings.valueAt(0).appId; // all should be the same
}
final int targetAppId = targetPkgSetting.appId;
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
if (callingAppId == targetAppId) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "same app id");
@@ -1299,7 +1326,9 @@
}
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "requestsQueryAllPackages");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "requestsQueryAllPackages");
+ }
if (callingPkgSetting != null) {
if (callingPkgSetting.pkg != null
&& requestsQueryAllPackages(callingPkgSetting.pkg)) {
@@ -1314,10 +1343,14 @@
}
}
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable");
+ }
if (mForceQueryable.contains(targetAppId)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "force queryable");
@@ -1325,10 +1358,14 @@
return false;
}
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage");
+ }
if (mQueriesViaPackage.contains(callingAppId, targetAppId)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "queries package");
@@ -1336,10 +1373,14 @@
return false;
}
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent");
+ }
if (mQueriesViaComponentRequireRecompute) {
mStateProvider.runWithState((settings, users) -> {
recomputeComponentVisibility(settings);
@@ -1352,11 +1393,15 @@
return false;
}
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable");
+ }
final int targetUid = UserHandle.getUid(targetUserId, targetAppId);
if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
if (DEBUG_LOGGING) {
@@ -1365,11 +1410,15 @@
return false;
}
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper");
+ }
if (callingSharedPkgSettings != null) {
int size = callingSharedPkgSettings.size();
for (int index = 0; index < size; index++) {
@@ -1392,11 +1441,15 @@
}
}
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary");
+ }
if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "queryable for library users");
@@ -1404,12 +1457,16 @@
return false;
}
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
return true;
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
}
@@ -1425,7 +1482,9 @@
private static boolean pkgInstruments(
@NonNull AndroidPackage source, @NonNull AndroidPackage target) {
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "pkgInstruments");
+ if (DEBUG_TRACING) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "pkgInstruments");
+ }
final String packageName = target.getPackageName();
final List<ParsedInstrumentation> inst = source.getInstrumentations();
for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
@@ -1435,7 +1494,9 @@
}
return false;
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (DEBUG_TRACING) {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index af0aa76..77c1c1d 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,7 +16,6 @@
package com.android.server.pm;
-import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.Nullable;
@@ -24,8 +23,11 @@
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.os.BatteryManagerInternal;
import android.os.Environment;
@@ -35,6 +37,7 @@
import android.os.storage.StorageManager;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -63,9 +66,7 @@
private static final int JOB_IDLE_OPTIMIZE = 800;
private static final int JOB_POST_BOOT_UPDATE = 801;
- private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG
- ? TimeUnit.MINUTES.toMillis(1)
- : TimeUnit.DAYS.toMillis(1);
+ private static final long IDLE_OPTIMIZATION_PERIOD = TimeUnit.DAYS.toMillis(1);
private static ComponentName sDexoptServiceName = new ComponentName(
"android",
@@ -113,14 +114,24 @@
return;
}
- JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ final JobScheduler js = context.getSystemService(JobScheduler.class);
// Schedule a one-off job which scans installed packages and updates
- // out-of-date oat files.
- js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
- .setMinimumLatency(TimeUnit.MINUTES.toMillis(10))
- .setOverrideDeadline(TimeUnit.MINUTES.toMillis(60))
- .build());
+ // out-of-date oat files. Schedule it 10 minutes after the boot complete event,
+ // so that we don't overload the boot with additional dex2oat compilations.
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
+ .setMinimumLatency(TimeUnit.MINUTES.toMillis(10))
+ .setOverrideDeadline(TimeUnit.MINUTES.toMillis(60))
+ .build());
+ context.unregisterReceiver(this);
+ if (DEBUG) {
+ Slog.i(TAG, "BootBgDexopt scheduled");
+ }
+ }
+ }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
// Schedule a daily job which scans installed packages and compiles
// those with fresh profiling data.
@@ -130,8 +141,8 @@
.setPeriodic(IDLE_OPTIMIZATION_PERIOD)
.build());
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Jobs scheduled");
+ if (DEBUG) {
+ Slog.d(TAG, "BgDexopt scheduled");
}
}
@@ -151,7 +162,7 @@
@SuppressWarnings("deprecation")
final long lowThreshold = StorageManager.from(context).getStorageLowBytes(mDataDir);
if (lowThreshold == 0) {
- Log.e(TAG, "Invalid low storage threshold");
+ Slog.e(TAG, "Invalid low storage threshold");
}
return lowThreshold;
@@ -198,13 +209,12 @@
long usableSpace = mDataDir.getUsableSpace();
if (usableSpace < lowThreshold) {
// Rather bail than completely fill up the disk.
- Log.w(TAG, "Aborting background dex opt job due to low storage: " +
+ Slog.w(TAG, "Aborting background dex opt job due to low storage: " +
usableSpace);
break;
}
-
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Updating package " + pkg);
+ if (DEBUG) {
+ Slog.i(TAG, "Updating package " + pkg);
}
// Update package if needed. Note that there can be no race between concurrent
@@ -236,13 +246,13 @@
public void run() {
int result = idleOptimization(pm, pkgs, BackgroundDexOptService.this);
if (result == OPTIMIZE_PROCESSED) {
- Log.i(TAG, "Idle optimizations completed.");
+ Slog.i(TAG, "Idle optimizations completed.");
} else if (result == OPTIMIZE_ABORT_NO_SPACE_LEFT) {
- Log.w(TAG, "Idle optimizations aborted because of space constraints.");
+ Slog.w(TAG, "Idle optimizations aborted because of space constraints.");
} else if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
- Log.w(TAG, "Idle optimizations aborted by job scheduler.");
+ Slog.w(TAG, "Idle optimizations aborted by job scheduler.");
} else {
- Log.w(TAG, "Idle optimizations ended with unexpected code: " + result);
+ Slog.w(TAG, "Idle optimizations ended with unexpected code: " + result);
}
if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
// Abandon our timeslice and do not reschedule.
@@ -256,7 +266,7 @@
// Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
Context context) {
- Log.i(TAG, "Performing idle optimizations");
+ Slog.i(TAG, "Performing idle optimizations");
// If post-boot update is still running, request that it exits early.
mExitPostBootUpdate.set(true);
mAbortIdleOptimization.set(false);
@@ -331,11 +341,15 @@
final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE
* lowStorageThreshold;
boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
- Log.d(TAG, "Should Downgrade " + shouldDowngrade);
+ if (DEBUG) {
+ Slog.d(TAG, "Should Downgrade " + shouldDowngrade);
+ }
if (shouldDowngrade) {
Set<String> unusedPackages =
pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
- Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages));
+ if (DEBUG) {
+ Slog.d(TAG, "Unsused Packages " + String.join(",", unusedPackages));
+ }
if (!unusedPackages.isEmpty()) {
for (String pkg : unusedPackages) {
@@ -407,7 +421,9 @@
*/
private boolean downgradePackage(PackageManagerService pm, String pkg,
boolean isForPrimaryDex) {
- Log.d(TAG, "Downgrading " + pkg);
+ if (DEBUG) {
+ Slog.d(TAG, "Downgrading " + pkg);
+ }
boolean dex_opt_performed = false;
int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
@@ -529,7 +545,7 @@
long usableSpace = mDataDir.getUsableSpace();
if (usableSpace < lowStorageThreshold) {
// Rather bail than completely fill up the disk.
- Log.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
+ Slog.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
return OPTIMIZE_ABORT_NO_SPACE_LEFT;
}
@@ -568,8 +584,8 @@
@Override
public boolean onStartJob(JobParameters params) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "onStartJob");
+ if (DEBUG) {
+ Slog.i(TAG, "onStartJob");
}
// NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
@@ -577,17 +593,13 @@
// restart with a period of ~1 minute.
PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
if (pm.isStorageLow()) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Low storage, skipping this run");
- }
+ Slog.i(TAG, "Low storage, skipping this run");
return false;
}
final ArraySet<String> pkgs = pm.getOptimizablePackages();
if (pkgs.isEmpty()) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "No packages to optimize");
- }
+ Slog.i(TAG, "No packages to optimize");
return false;
}
@@ -603,8 +615,8 @@
@Override
public boolean onStopJob(JobParameters params) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "onStopJob");
+ if (DEBUG) {
+ Slog.d(TAG, "onStopJob");
}
if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
@@ -625,7 +637,7 @@
private void notifyPinService(ArraySet<String> updatedPackages) {
PinnerService pinnerService = LocalServices.getService(PinnerService.class);
if (pinnerService != null) {
- Log.i(TAG, "Pinning optimized code " + updatedPackages);
+ Slog.i(TAG, "Pinning optimized code " + updatedPackages);
pinnerService.update(updatedPackages, false /* force */);
}
}
@@ -660,7 +672,7 @@
final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
String sysPropValue = SystemProperties.get(sysPropKey);
if (sysPropValue == null || sysPropValue.isEmpty()) {
- Log.w(TAG, "SysProp " + sysPropKey + " not set");
+ Slog.w(TAG, "SysProp " + sysPropKey + " not set");
return Long.MAX_VALUE;
}
return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue));
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 1d73abc..c1209d4 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -288,7 +288,9 @@
if (targetUserProfiles.isEmpty()) {
return false;
}
- return hasCallerGotInteractAcrossProfilesPermission(callingPackage);
+ return hasCallerGotInteractAcrossProfilesPermission(callingPackage)
+ && haveProfilesGotInteractAcrossProfilesPermission(
+ callingPackage, targetUserProfiles);
}
private boolean hasCallerGotInteractAcrossProfilesPermission(String callingPackage) {
@@ -296,6 +298,28 @@
callingPackage, mInjector.getCallingUid(), mInjector.getCallingPid());
}
+ private boolean haveProfilesGotInteractAcrossProfilesPermission(
+ String packageName, List<UserHandle> profiles) {
+ for (UserHandle profile : profiles) {
+ final int uid = mInjector.withCleanCallingIdentity(() -> {
+ try {
+ return mInjector.getPackageManager().getPackageUidAsUser(
+ packageName, /* flags= */ 0, profile.getIdentifier());
+ } catch (PackageManager.NameNotFoundException e) {
+ return -1;
+ }
+ });
+ if (uid == -1) {
+ return false;
+ }
+ if (!hasInteractAcrossProfilesPermission(
+ packageName, uid, PermissionChecker.PID_UNKNOWN)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private boolean isCrossProfilePackageAllowlisted(String packageName) {
return mInjector.withCleanCallingIdentity(() ->
mInjector.getDevicePolicyManagerInternal()
@@ -482,16 +506,16 @@
// this particular app-op to be modified without the broader app-op permissions.
mInjector.withCleanCallingIdentity(() ->
mInjector.getAppOpsManager()
- .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode));
+ .setUidMode(OP_INTERACT_ACROSS_PROFILES, uid, newMode));
} else {
mInjector.getAppOpsManager()
- .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode);
+ .setUidMode(OP_INTERACT_ACROSS_PROFILES, uid, newMode);
}
// Kill the UID before sending the broadcast to ensure that apps can be informed when
// their app-op has been revoked.
maybeKillUid(packageName, uid, hadPermission);
- sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(profileId));
- maybeLogSetInteractAcrossProfilesAppOp(packageName, newMode, logMetrics, uid);
+ sendCanInteractAcrossProfilesChangedBroadcast(packageName, UserHandle.of(profileId));
+ maybeLogSetInteractAcrossProfilesAppOp(packageName, newMode, logMetrics);
}
/**
@@ -509,7 +533,7 @@
}
private void maybeLogSetInteractAcrossProfilesAppOp(
- String packageName, @Mode int newMode, boolean logMetrics, int uid) {
+ String packageName, @Mode int newMode, boolean logMetrics) {
if (!logMetrics) {
return;
}
@@ -517,7 +541,7 @@
.createEvent(DevicePolicyEnums.SET_INTERACT_ACROSS_PROFILES_APP_OP)
.setStrings(packageName)
.setInt(newMode)
- .setBoolean(appDeclaresCrossProfileAttribute(uid))
+ .setBoolean(appDeclaresCrossProfileAttribute(packageName))
.write();
}
@@ -533,10 +557,10 @@
}
private void sendCanInteractAcrossProfilesChangedBroadcast(
- String packageName, int uid, UserHandle userHandle) {
+ String packageName, UserHandle userHandle) {
final Intent intent =
new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED).setPackage(packageName);
- if (appDeclaresCrossProfileAttribute(uid)) {
+ if (appDeclaresCrossProfileAttribute(packageName)) {
intent.addFlags(
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND);
} else {
@@ -553,8 +577,8 @@
.queryBroadcastReceiversAsUser(intent, /* flags= */ 0, userHandle);
}
- private boolean appDeclaresCrossProfileAttribute(int uid) {
- return mInjector.getPackageManagerInternal().getPackage(uid).isCrossProfile();
+ private boolean appDeclaresCrossProfileAttribute(String packageName) {
+ return mInjector.getPackageManagerInternal().getPackage(packageName).isCrossProfile();
}
@Override
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
index 7927538..9ea16d3 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.content.IntentFilter;
-import com.android.server.WatchedIntentResolver;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index ec79483..ed00609 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -58,6 +58,7 @@
private boolean mTitlePrinted;
private boolean mFullPreferred;
private boolean mCheckIn;
+ private boolean mBrief;
private String mTargetPackageName;
@@ -128,4 +129,12 @@
public void setCheckIn(boolean checkIn) {
mCheckIn = checkIn;
}
+
+ public boolean isBrief() {
+ return mBrief;
+ }
+
+ public void setBrief(boolean brief) {
+ mBrief = brief;
+ }
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 2015c78..34caaf5 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -30,6 +30,7 @@
import android.util.TypedXmlSerializer;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.utils.WatchedArrayMap;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -65,7 +66,7 @@
protected final LongSparseArray<ArraySet<Long>> mKeySetMapping;
- private final ArrayMap<String, PackageSetting> mPackages;
+ private final WatchedArrayMap<String, PackageSetting> mPackages;
private long lastIssuedKeySetId = 0;
@@ -114,7 +115,7 @@
}
}
- public KeySetManagerService(ArrayMap<String, PackageSetting> packages) {
+ public KeySetManagerService(WatchedArrayMap<String, PackageSetting> packages) {
mKeySets = new LongSparseArray<KeySetHandle>();
mPublicKeys = new LongSparseArray<PublicKeyHandle>();
mKeySetMapping = new LongSparseArray<ArraySet<Long>>();
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 47d1629..de9add0 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -25,6 +25,7 @@
import static android.content.pm.LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
import static android.content.pm.LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;
+import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -1232,12 +1233,12 @@
cookie.user.getIdentifier());
}
- /** Returns whether or not the given UID is in allow list */
- private static boolean isCallingUidAllowed(int[] allowList, int callingUid) {
- if (allowList == null) {
+ /** Returns whether or not the given appId is in allow list */
+ private static boolean isCallingAppIdAllowed(int[] appIdAllowList, @AppIdInt int appId) {
+ if (appIdAllowList == null) {
return true;
}
- return Arrays.binarySearch(allowList, callingUid) > -1;
+ return Arrays.binarySearch(appIdAllowList, appId) > -1;
}
private String[] getFilteredPackageNames(String[] packageNames, BroadcastCookie cookie) {
@@ -1432,7 +1433,7 @@
// Handle onPackageRemoved.
if (Intent.ACTION_PACKAGE_REMOVED_INTERNAL.equals(action)) {
final String packageName = getPackageName(intent);
- final int[] allowList =
+ final int[] appIdAllowList =
intent.getIntArrayExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST);
// If {@link #EXTRA_REPLACING} is true, that will be onPackageChanged case.
if (packageName != null && !intent.getBooleanExtra(
@@ -1448,7 +1449,8 @@
if (!isEnabledProfileOf(cookie.user, user, "onPackageRemoved")) {
continue;
}
- if (!isCallingUidAllowed(allowList, cookie.callingUid)) {
+ if (!isCallingAppIdAllowed(appIdAllowList, UserHandle.getAppId(
+ cookie.callingUid))) {
continue;
}
try {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a799ce2..83eb093 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2678,12 +2678,20 @@
final String packageNameToLog =
(params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? packageName : "";
final long currentTimestamp = System.currentTimeMillis();
+ final int packageUid;
+ if (returnCode != INSTALL_SUCCEEDED) {
+ // Package didn't install; no valid uid
+ packageUid = Process.INVALID_UID;
+ } else {
+ packageUid = mPm.getPackageUid(packageName, 0, userId);
+ }
FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED,
isIncrementalInstallation(),
packageNameToLog,
currentTimestamp - createdMillis,
returnCode,
- getApksSize(packageName));
+ getApksSize(packageName),
+ packageUid);
}
private long getApksSize(String packageName) {
@@ -3754,13 +3762,6 @@
}
}
- final DataLoaderManager dataLoaderManager = mContext.getSystemService(
- DataLoaderManager.class);
- if (dataLoaderManager == null) {
- throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
- "Failed to find data loader manager service");
- }
-
final DataLoaderParams params = this.params.dataLoaderParams;
final boolean manualStartAndDestroy = !isIncrementalInstallation();
final boolean systemDataLoader = isSystemDataLoaderInstallation();
@@ -3785,20 +3786,13 @@
return;
}
try {
- IDataLoader dataLoader = dataLoaderManager.getDataLoader(dataLoaderId);
- if (dataLoader == null) {
- mDataLoaderFinished = true;
- dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
- "Failure to obtain data loader");
- return;
- }
-
switch (status) {
case IDataLoaderStatusListener.DATA_LOADER_BOUND: {
if (manualStartAndDestroy) {
FileSystemControlParcel control = new FileSystemControlParcel();
control.callback = new FileSystemConnector(addedFiles);
- dataLoader.create(dataLoaderId, params.getData(), control, this);
+ getDataLoader(dataLoaderId).create(dataLoaderId, params.getData(),
+ control, this);
}
break;
@@ -3807,12 +3801,12 @@
if (manualStartAndDestroy) {
// IncrementalFileStorages will call start after all files are
// created in IncFS.
- dataLoader.start(dataLoaderId);
+ getDataLoader(dataLoaderId).start(dataLoaderId);
}
break;
}
case IDataLoaderStatusListener.DATA_LOADER_STARTED: {
- dataLoader.prepareImage(
+ getDataLoader(dataLoaderId).prepareImage(
dataLoaderId,
addedFiles.toArray(
new InstallationFileParcel[addedFiles.size()]),
@@ -3828,7 +3822,7 @@
dispatchSessionSealed();
}
if (manualStartAndDestroy) {
- dataLoader.destroy(dataLoaderId);
+ getDataLoader(dataLoaderId).destroy(dataLoaderId);
}
break;
}
@@ -3837,7 +3831,7 @@
dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to prepare image.");
if (manualStartAndDestroy) {
- dataLoader.destroy(dataLoaderId);
+ getDataLoader(dataLoaderId).destroy(dataLoaderId);
}
break;
}
@@ -3852,11 +3846,12 @@
break;
}
case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
- mDataLoaderFinished = true;
- dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"DataLoader reported unrecoverable failure.");
- break;
}
+ } catch (PackageManagerException e) {
+ mDataLoaderFinished = true;
+ dispatchSessionValidationFailure(e.error, ExceptionUtils.getCompleteMessage(e));
} catch (RemoteException e) {
// In case of streaming failure we don't want to fail or commit the session.
// Just return from this method and allow caller to commit again.
@@ -3931,7 +3926,7 @@
}
final long bindDelayMs = 0;
- if (!dataLoaderManager.bindToDataLoader(sessionId, params.getData(), bindDelayMs,
+ if (!getDataLoaderManager().bindToDataLoader(sessionId, params.getData(), bindDelayMs,
statusListener)) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to initialize data loader");
@@ -3940,6 +3935,24 @@
return false;
}
+ private DataLoaderManager getDataLoaderManager() throws PackageManagerException {
+ DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
+ if (dataLoaderManager == null) {
+ throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failed to find data loader manager service");
+ }
+ return dataLoaderManager;
+ }
+
+ private IDataLoader getDataLoader(int dataLoaderId) throws PackageManagerException {
+ IDataLoader dataLoader = getDataLoaderManager().getDataLoader(dataLoaderId);
+ if (dataLoader == null) {
+ throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failure to obtain data loader");
+ }
+ return dataLoader;
+ }
+
private void dispatchSessionValidationFailure(int error, String detailMessage) {
mHandler.obtainMessage(MSG_SESSION_VALIDATION_FAILURE, error, -1,
detailMessage).sendToTarget();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fc36378..01c89b5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -404,6 +404,7 @@
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.utils.SnapshotCache;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.utils.Watchable;
import com.android.server.utils.Watched;
@@ -557,6 +558,7 @@
private static final int SHELL_UID = Process.SHELL_UID;
private static final int SE_UID = Process.SE_UID;
private static final int NETWORKSTACK_UID = Process.NETWORK_STACK_UID;
+ private static final int UWB_UID = Process.UWB_UID;
static final int SCAN_NO_DEX = 1 << 0;
static final int SCAN_UPDATE_SIGNATURE = 1 << 1;
@@ -870,12 +872,17 @@
@Watched
@GuardedBy("mLock")
final WatchedArrayMap<String, AndroidPackage> mPackages = new WatchedArrayMap<>();
+ private final SnapshotCache<WatchedArrayMap<String, AndroidPackage>> mPackagesSnapshot =
+ new SnapshotCache.Auto(mPackages, mPackages, "PackageManagerService.mPackages");
// Keys are isolated uids and values are the uid of the application
// that created the isolated process.
@Watched
@GuardedBy("mLock")
final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray();
+ private final SnapshotCache<WatchedSparseIntArray> mIsolatedOwnersSnapshot =
+ new SnapshotCache.Auto(mIsolatedOwners, mIsolatedOwners,
+ "PackageManagerService.mIsolatedOwners");
/**
* Tracks new system packages [received in an OTA] that we expect to
@@ -1308,14 +1315,17 @@
// Avoid invalidation-thrashing by preventing cache invalidations from causing property
// writes if the cache isn't enabled yet. We re-enable writes later when we're
// done initializing.
- sSnapshotCorked = true;
+ sSnapshotCorked.incrementAndGet();
PackageManager.corkPackageInfoCache();
}
@Override
public void enablePackageCaches() {
// Uncork cache invalidations and allow clients to cache package information.
- sSnapshotCorked = false;
+ int corking = sSnapshotCorked.decrementAndGet();
+ if (TRACE_SNAPSHOTS && corking == 0) {
+ Log.i(TAG, "snapshot: corking returns to 0");
+ }
PackageManager.uncorkPackageInfoCache();
}
}
@@ -1394,14 +1404,27 @@
@Watched
final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
mSharedLibraries = new WatchedArrayMap<>();
+ private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>>
+ mSharedLibrariesSnapshot =
+ new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries,
+ "PackageManagerService.mSharedLibraries");
@Watched
final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
mStaticLibsByDeclaringPackage = new WatchedArrayMap<>();
+ private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>>
+ mStaticLibsByDeclaringPackageSnapshot =
+ new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries,
+ "PackageManagerService.mSharedLibraries");
// Mapping from instrumentation class names to info about them.
@Watched
final WatchedArrayMap<ComponentName, ParsedInstrumentation> mInstrumentation =
new WatchedArrayMap<>();
+ private final SnapshotCache<WatchedArrayMap<ComponentName, ParsedInstrumentation>>
+ mInstrumentationSnapshot =
+ new SnapshotCache.Auto<>(mInstrumentation, mInstrumentation,
+ "PackageManagerService.mInstrumentation");
+
// Packages whose data we have transfered into another package, thus
// should no longer exist.
@@ -1587,6 +1610,7 @@
static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
static final int DOMAIN_VERIFICATION = 27;
+ static final int SNAPSHOT_UNCORK = 28;
static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
@@ -1833,11 +1857,11 @@
Snapshot(int type) {
if (type == Snapshot.SNAPPED) {
settings = mSettings.snapshot();
- isolatedOwners = mIsolatedOwners.snapshot();
- packages = mPackages.snapshot();
- sharedLibs = mSharedLibraries.snapshot();
- staticLibs = mStaticLibsByDeclaringPackage.snapshot();
- instrumentation = mInstrumentation.snapshot();
+ isolatedOwners = mIsolatedOwnersSnapshot.snapshot();
+ packages = mPackagesSnapshot.snapshot();
+ sharedLibs = mSharedLibrariesSnapshot.snapshot();
+ staticLibs = mStaticLibsByDeclaringPackageSnapshot.snapshot();
+ instrumentation = mInstrumentationSnapshot.snapshot();
resolveComponentName = mResolveComponentName.clone();
resolveActivity = new ActivityInfo(mResolveActivity);
instantAppInstallerActivity =
@@ -1902,20 +1926,13 @@
int flags, int userId);
@NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType,
int flags, int userId, int callingUid, boolean includeInstantApps);
- @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
- String resolvedType, int flags, int userId, int callingUid,
- String instantAppPkgName);
@NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent,
String resolvedType, int flags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
String instantAppPkgName);
- @Nullable ComponentName findInstallFailureActivity(String packageName, int filterCallingUid,
- int userId);
ActivityInfo getActivityInfo(ComponentName component, int flags, int userId);
ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
int filterCallingUid, int userId);
- ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
- int filterCallingUid, int userId);
AndroidPackage getPackage(String packageName);
AndroidPackage getPackage(int uid);
ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
@@ -1923,11 +1940,6 @@
ApplicationInfo getApplicationInfo(String packageName, int flags, int userId);
ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
int filterCallingUid, int userId);
- ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
- int filterCallingUid, int userId);
- ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(Intent intent,
- int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
- int userId, boolean debug);
ComponentName getDefaultHomeActivity(int userId);
ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId);
CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType,
@@ -1938,57 +1950,25 @@
List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent);
- List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
- String instantAppPkgName, @UserIdInt int userId, int filterCallingUid);
- List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
- int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
- int userId);
- List<ResolveInfo> filterIfNotSystemUser(List<ResolveInfo> resolveInfos, int userId);
- List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent,
- String resolvedType, int flags, int userId, boolean resolveForStart,
- boolean isRequesterInstantApp);
PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId);
PackageInfo getPackageInfo(String packageName, int flags, int userId);
PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags,
int filterCallingUid, int userId);
- PackageInfo getPackageInfoInternalBody(String packageName, long versionCode, int flags,
- int filterCallingUid, int userId);
PackageSetting getPackageSetting(String packageName);
PackageSetting getPackageSettingInternal(String packageName, int callingUid);
ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId);
- ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
- int callingUid);
- ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, Intent intent,
- String resolvedType, int flags, int sourceUserId);
ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
int sourceUserId, int targetUserId);
- ResolveInfo queryCrossProfileIntents(List<CrossProfileIntentFilter> matchingFilters,
- Intent intent, String resolvedType, int flags, int sourceUserId,
- boolean matchInCurrentProfile);
- ResolveInfo querySkipCurrentProfileIntents(List<CrossProfileIntentFilter> matchingFilters,
- Intent intent, String resolvedType, int flags, int sourceUserId);
ServiceInfo getServiceInfo(ComponentName component, int flags, int userId);
- ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
- int callingUid);
SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version);
String getInstantAppPackageName(int callingUid);
String resolveExternalPackageNameLPr(AndroidPackage pkg);
- String resolveInternalPackageNameInternalLocked(String packageName, long versionCode,
- int callingUid);
String resolveInternalPackageNameLPr(String packageName, long versionCode);
String[] getPackagesForUid(int uid);
- String[] getPackagesForUidInternal(int uid, int callingUid);
- String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
- boolean isCallerInstantApp);
UserInfo getProfileParent(int userId);
- boolean areWebInstantAppsDisabled(int userId);
boolean canViewInstantApps(int callingUid, int userId);
boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
int flags);
- boolean hasCrossUserPermission(int callingUid, int callingUserId, int userId,
- boolean requireFullPermission, boolean requirePermissionWhenSameUser);
- boolean hasNonNegativePriority(List<ResolveInfo> resolveInfos);
- boolean hasPermission(String permission);
boolean isCallerSameApp(String packageName, int uid);
boolean isComponentVisibleToInstantApp(@Nullable ComponentName component);
boolean isComponentVisibleToInstantApp(@Nullable ComponentName component,
@@ -1997,27 +1977,15 @@
String resolvedType, int flags);
boolean isInstantApp(String packageName, int userId);
boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid);
- boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId, int callingUid);
- boolean isInstantAppResolutionAllowed(Intent intent, List<ResolveInfo> resolvedActivities,
- int userId, boolean skipPackageCheck, int flags);
- boolean isInstantAppResolutionAllowedBody(Intent intent,
- List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck,
- int flags);
- boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
- String resolvedType, int flags);
- boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId);
boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId);
- boolean isUserEnabled(int userId);
boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
@Nullable ComponentName component, @ComponentType int componentType, int userId);
boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
int userId);
boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
int userId);
- int bestDomainVerificationStatus(int status1, int status2);
int checkUidPermission(String permName, int uid);
int getPackageUidInternal(String packageName, int flags, int userId, int callingUid);
- int updateFlags(int flags, int userId);
int updateFlagsForApplication(int flags, int userId);
int updateFlagsForComponent(int flags, int userId);
int updateFlagsForPackage(int flags, int userId);
@@ -2324,7 +2292,7 @@
userId, callingUid, instantAppPkgName);
}
- public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
+ protected @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
String instantAppPkgName) {
// reader
@@ -2462,7 +2430,7 @@
* creating an activity with an intent filter that handles the action
* {@link Intent#ACTION_INSTALL_FAILURE}.
*/
- public @Nullable ComponentName findInstallFailureActivity(
+ private @Nullable ComponentName findInstallFailureActivity(
String packageName, int filterCallingUid, int userId) {
final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE);
failureActivityIntent.setPackage(packageName);
@@ -2508,7 +2476,7 @@
return getActivityInfoInternalBody(component, flags, filterCallingUid, userId);
}
- public ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
+ protected ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
int filterCallingUid, int userId) {
ParsedActivity a = mComponentResolver.getActivity(component);
@@ -2600,7 +2568,7 @@
return getApplicationInfoInternalBody(packageName, flags, filterCallingUid, userId);
}
- public ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
+ protected ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
int filterCallingUid, int userId) {
// writer
// Normalize package name to handle renamed packages and static libs
@@ -2654,7 +2622,7 @@
return null;
}
- public ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
+ protected ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
Intent intent, int matchFlags, List<ResolveInfo> candidates,
CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
final ArrayList<ResolveInfo> result = new ArrayList<>();
@@ -2993,7 +2961,7 @@
return resolveInfos;
}
- public List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
+ private List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
String instantAppPkgName, @UserIdInt int userId, int filterCallingUid) {
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
@@ -3081,7 +3049,8 @@
*
* @return filtered list
*/
- public List<ResolveInfo> filterIfNotSystemUser(List<ResolveInfo> resolveInfos, int userId) {
+ private List<ResolveInfo> filterIfNotSystemUser(List<ResolveInfo> resolveInfos,
+ int userId) {
if (userId == UserHandle.USER_SYSTEM) {
return resolveInfos;
}
@@ -3094,7 +3063,7 @@
return resolveInfos;
}
- public List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result,
+ private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result,
Intent intent,
String resolvedType, int flags, int userId, boolean resolveForStart,
boolean isRequesterInstantApp) {
@@ -3290,7 +3259,7 @@
userId);
}
- public PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
+ protected PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
int flags, int filterCallingUid, int userId) {
// reader
// Normalize package name to handle renamed packages and static libs
@@ -3374,7 +3343,7 @@
return getInstalledPackagesBody(flags, userId, callingUid);
}
- public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
+ protected ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
int callingUid) {
// writer
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
@@ -3449,7 +3418,7 @@
* will forward the intent to the filter's target user.
* Otherwise, returns null.
*/
- public ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter,
+ private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter,
Intent intent,
String resolvedType, int flags, int sourceUserId) {
int targetUserId = filter.getTargetUserId();
@@ -3504,7 +3473,7 @@
}
// Return matching ResolveInfo in target user if any.
- public ResolveInfo queryCrossProfileIntents(
+ private ResolveInfo queryCrossProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
int flags, int sourceUserId, boolean matchInCurrentProfile) {
if (matchingFilters != null) {
@@ -3534,7 +3503,7 @@
return null;
}
- public ResolveInfo querySkipCurrentProfileIntents(
+ private ResolveInfo querySkipCurrentProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
int flags, int sourceUserId) {
if (matchingFilters != null) {
@@ -3565,7 +3534,7 @@
return getServiceInfoBody(component, flags, userId, callingUid);
}
- public ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
+ protected ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
int callingUid) {
ParsedService s = mComponentResolver.getService(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -3632,7 +3601,7 @@
return pkg.getPackageName();
}
- public String resolveInternalPackageNameInternalLocked(
+ private String resolveInternalPackageNameInternalLocked(
String packageName, long versionCode, int callingUid) {
// Handle renamed packages
String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
@@ -3724,14 +3693,14 @@
return getPackagesForUidInternal(uid, Binder.getCallingUid());
}
- public String[] getPackagesForUidInternal(int uid, int callingUid) {
+ private String[] getPackagesForUidInternal(int uid, int callingUid) {
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
}
- public String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
+ protected String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
boolean isCallerInstantApp) {
// reader
final Object obj = mSettings.getSettingLPr(appId);
@@ -3773,7 +3742,7 @@
/**
* Returns whether or not instant apps have been disabled remotely.
*/
- public boolean areWebInstantAppsDisabled(int userId) {
+ private boolean areWebInstantAppsDisabled(int userId) {
return mWebInstantAppsDisabled.get(userId);
}
@@ -3870,7 +3839,7 @@
return true;
}
- public boolean hasCrossUserPermission(
+ private boolean hasCrossUserPermission(
int callingUid, int callingUserId, int userId, boolean requireFullPermission,
boolean requirePermissionWhenSameUser) {
if (!requirePermissionWhenSameUser && userId == callingUserId) {
@@ -3890,11 +3859,11 @@
* @param resolveInfos list of resolve infos in descending priority order
* @return if the list contains a resolve info with non-negative priority
*/
- public boolean hasNonNegativePriority(List<ResolveInfo> resolveInfos) {
+ private boolean hasNonNegativePriority(List<ResolveInfo> resolveInfos) {
return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0;
}
- public boolean hasPermission(String permission) {
+ private boolean hasPermission(String permission) {
return mContext.checkCallingOrSelfPermission(permission)
== PackageManager.PERMISSION_GRANTED;
}
@@ -3988,7 +3957,7 @@
return isInstantAppInternalBody(packageName, userId, callingUid);
}
- public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
+ protected boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
int callingUid) {
if (Process.isIsolated(callingUid)) {
callingUid = getIsolatedOwner(callingUid);
@@ -4006,7 +3975,7 @@
return false;
}
- public boolean isInstantAppResolutionAllowed(
+ private boolean isInstantAppResolutionAllowed(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
boolean skipPackageCheck, int flags) {
if (mInstantAppResolverConnection == null) {
@@ -4046,7 +4015,7 @@
// Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
// Or if there's already an ephemeral app installed that handles the action
- public boolean isInstantAppResolutionAllowedBody(
+ protected boolean isInstantAppResolutionAllowedBody(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
boolean skipPackageCheck, int flags) {
final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
@@ -4079,7 +4048,7 @@
return true;
}
- public boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
+ private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
String resolvedType, int flags) {
PersistentPreferredIntentResolver ppir =
mSettings.getPersistentPreferredActivities(userId);
@@ -4097,7 +4066,7 @@
return false;
}
- public boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
+ private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
if (!mInjector.getLocalService(ActivityTaskManagerInternal.class)
.isCallerRecents(callingUid)) {
return false;
@@ -4123,7 +4092,7 @@
}
}
- public boolean isUserEnabled(int userId) {
+ private boolean isUserEnabled(int userId) {
final long callingId = Binder.clearCallingIdentity();
try {
UserInfo userInfo = mUserManager.getUserInfo(userId);
@@ -4223,7 +4192,7 @@
* Verification statuses are ordered from the worse to the best, except for
* INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
*/
- public int bestDomainVerificationStatus(int status1, int status2) {
+ private int bestDomainVerificationStatus(int status1, int status2) {
if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
return status2;
}
@@ -4263,7 +4232,7 @@
/**
* Update given flags based on encryption status of current user.
*/
- public int updateFlags(int flags, int userId) {
+ private int updateFlags(int flags, int userId) {
if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) {
// Caller expressed an explicit opinion about what encryption
@@ -4873,12 +4842,16 @@
// A lock-free cache for frequently called functions.
private volatile Computer mSnapshotComputer;
// If true, the snapshot is invalid (stale). The attribute is static since it may be
- // set from outside classes.
- private static volatile boolean sSnapshotInvalid = true;
+ // set from outside classes. The attribute may be set to true anywhere, although it
+ // should only be set true while holding mLock. However, the attribute id guaranteed
+ // to be set false only while mLock and mSnapshotLock are both held.
+ private static AtomicBoolean sSnapshotInvalid = new AtomicBoolean(true);
+ // The package manager that is using snapshots.
+ private static PackageManagerService sSnapshotConsumer = null;
// If true, the snapshot is corked. Do not create a new snapshot but use the live
// computer. This throttles snapshot creation during periods of churn in Package
// Manager.
- private static volatile boolean sSnapshotCorked = false;
+ private static AtomicInteger sSnapshotCorked = new AtomicInteger(0);
/**
* This lock is used to make reads from {@link #sSnapshotInvalid} and
@@ -4896,7 +4869,10 @@
// The snapshot disable/enable switch. An image with the flag set true uses snapshots
// and an image with the flag set false does not use snapshots.
- private static final boolean SNAPSHOT_ENABLED = false;
+ private static final boolean SNAPSHOT_ENABLED = true;
+
+ // The default auto-cork delay for snapshots. This is 1s.
+ private static final long SNAPSHOT_AUTOCORK_DELAY_MS = TimeUnit.SECONDS.toMillis(1);
// The per-instance snapshot disable/enable flag. This is generally set to false in
// test instances and set to SNAPSHOT_ENABLED in operational instances.
@@ -4921,15 +4897,16 @@
// If the current thread holds mLock then it may have modified state but not
// yet invalidated the snapshot. Always give the thread the live computer.
return mLiveComputer;
+ } else if (sSnapshotCorked.get() > 0) {
+ // Snapshots are corked, which means new ones should not be built right now.
+ mSnapshotStatistics.corked();
+ return mLiveComputer;
}
synchronized (mSnapshotLock) {
+ // This synchronization block serializes access to the snapshot computer and
+ // to the code that samples mSnapshotInvalid.
Computer c = mSnapshotComputer;
- if (sSnapshotCorked && (c != null)) {
- // Snapshots are corked, which means new ones should not be built right now.
- c.use();
- return c;
- }
- if (sSnapshotInvalid || (c == null)) {
+ if (sSnapshotInvalid.getAndSet(false) || (c == null)) {
// The snapshot is invalid if it is marked as invalid or if it is null. If it
// is null, then it is currently being rebuilt by rebuildSnapshot().
synchronized (mLock) {
@@ -4937,9 +4914,7 @@
// invalidated as it is rebuilt. However, the snapshot is still
// self-consistent (the lock is being held) and is current as of the time
// this function is entered.
- if (sSnapshotInvalid) {
- rebuildSnapshot();
- }
+ rebuildSnapshot();
// Guaranteed to be non-null. mSnapshotComputer is only be set to null
// temporarily in rebuildSnapshot(), which is guarded by mLock(). Since
@@ -4957,12 +4932,11 @@
* Rebuild the cached computer. mSnapshotComputer is temporarily set to null to block other
* threads from using the invalid computer until it is rebuilt.
*/
- @GuardedBy("mLock")
+ @GuardedBy({ "mLock", "mSnapshotLock"})
private void rebuildSnapshot() {
final long now = SystemClock.currentTimeMicro();
final int hits = mSnapshotComputer == null ? -1 : mSnapshotComputer.getUsed();
mSnapshotComputer = null;
- sSnapshotInvalid = false;
final Snapshot args = new Snapshot(Snapshot.SNAPPED);
mSnapshotComputer = new ComputerEngine(args);
final long done = SystemClock.currentTimeMicro();
@@ -4971,6 +4945,30 @@
}
/**
+ * Create a new snapshot. Used for testing only. This does collect statistics or
+ * update the snapshot used by other actors. It does not alter the invalidation
+ * flag. This method takes the mLock internally.
+ */
+ private Computer createNewSnapshot() {
+ synchronized (mLock) {
+ final Snapshot args = new Snapshot(Snapshot.SNAPPED);
+ return new ComputerEngine(args);
+ }
+ }
+
+ /**
+ * Cork snapshots. This times out after the programmed delay.
+ */
+ private void corkSnapshots(int multiplier) {
+ int corking = sSnapshotCorked.getAndIncrement();
+ if (TRACE_SNAPSHOTS && corking == 0) {
+ Log.i(TAG, "snapshot: corking goes positive");
+ }
+ Message message = mHandler.obtainMessage(SNAPSHOT_UNCORK);
+ mHandler.sendMessageDelayed(message, SNAPSHOT_AUTOCORK_DELAY_MS * multiplier);
+ }
+
+ /**
* Create a live computer
*/
private ComputerLocked createLiveComputer() {
@@ -4985,9 +4983,9 @@
*/
public static void onChange(@Nullable Watchable what) {
if (TRACE_SNAPSHOTS) {
- Log.e(TAG, "snapshot: onChange(" + what + ")");
+ Log.i(TAG, "snapshot: onChange(" + what + ")");
}
- sSnapshotInvalid = true;
+ sSnapshotInvalid.set(true);
}
/**
@@ -5366,6 +5364,13 @@
mDomainVerificationManager.runMessage(messageCode, object);
break;
}
+ case SNAPSHOT_UNCORK: {
+ int corking = sSnapshotCorked.decrementAndGet();
+ if (TRACE_SNAPSHOTS && corking == 0) {
+ Log.e(TAG, "snapshot: corking goes to zero in message handler");
+ }
+ break;
+ }
}
}
}
@@ -6313,6 +6318,8 @@
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
+ ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
t.traceEnd();
String separateProcesses = SystemProperties.get("debug.separate_processes");
@@ -6380,12 +6387,13 @@
// constructor, at which time the invalidation method updates it. The cache is
// corked initially to ensure a cached computer is not built until the end of the
// constructor.
- mSnapshotEnabled = SNAPSHOT_ENABLED;
- sSnapshotCorked = true;
- sSnapshotInvalid = true;
mSnapshotStatistics = new SnapshotStatistics();
+ sSnapshotConsumer = this;
+ sSnapshotCorked.set(1);
+ sSnapshotInvalid.set(true);
mLiveComputer = createLiveComputer();
mSnapshotComputer = null;
+ mSnapshotEnabled = SNAPSHOT_ENABLED;
registerObserver();
}
@@ -7829,12 +7837,6 @@
flags, filterCallingUid, userId);
}
- private PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
- int flags, int filterCallingUid, int userId) {
- return liveComputer().getPackageInfoInternalBody(packageName, versionCode,
- flags, filterCallingUid, userId);
- }
-
private boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
return liveComputer().isComponentVisibleToInstantApp(component);
}
@@ -8021,12 +8023,6 @@
filterCallingUid, userId);
}
- private ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
- int filterCallingUid, int userId) {
- return liveComputer().getApplicationInfoInternalBody(packageName, flags,
- filterCallingUid, userId);
- }
-
@GuardedBy("mLock")
private String normalizePackageNameLPr(String packageName) {
String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
@@ -8254,13 +8250,6 @@
}
/**
- * Update given flags based on encryption status of current user.
- */
- private int updateFlags(int flags, int userId) {
- return liveComputer().updateFlags(flags, userId);
- }
-
- /**
* Update given flags when being used to request {@link PackageInfo}.
*/
private int updateFlagsForPackage(int flags, int userId) {
@@ -8354,16 +8343,6 @@
filterCallingUid, userId);
}
- private ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
- int filterCallingUid, int userId) {
- return liveComputer().getActivityInfoInternalBody(component, flags,
- filterCallingUid, userId);
- }
-
- private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
- return liveComputer().isRecentsAccessingChildProfiles(callingUid, targetUserId);
- }
-
@Override
public boolean activitySupportsIntent(ComponentName component, Intent intent,
String resolvedType) {
@@ -8629,12 +8608,6 @@
return snapshotComputer().getServiceInfo(component, flags, userId);
}
- private ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
- int callingUid) {
- return liveComputer().getServiceInfoBody(component, flags, userId,
- callingUid);
- }
-
@Override
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
@@ -9182,16 +9155,6 @@
return snapshotComputer().getPackagesForUid(uid);
}
- private String[] getPackagesForUidInternal(int uid, int callingUid) {
- return liveComputer().getPackagesForUidInternal(uid, callingUid);
- }
-
- private String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
- boolean isCallerInstantApp) {
- return liveComputer().getPackagesForUidInternalBody(callingUid, userId, appId,
- isCallerInstantApp);
- }
-
@Override
public String getNameForUid(int uid) {
final int callingUid = Binder.getCallingUid();
@@ -9495,31 +9458,6 @@
intent, resolvedType, flags, query, 0, false, false, false, userId);
}
- /**
- * Returns whether or not instant apps have been disabled remotely.
- */
- private boolean areWebInstantAppsDisabled(int userId) {
- return liveComputer().areWebInstantAppsDisabled(userId);
- }
-
- private boolean isInstantAppResolutionAllowed(
- Intent intent, List<ResolveInfo> resolvedActivities, int userId,
- boolean skipPackageCheck, int flags) {
- return liveComputer().isInstantAppResolutionAllowed(
- intent, resolvedActivities, userId,
- skipPackageCheck, flags);
- }
-
- // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
- // Or if there's already an ephemeral app installed that handles the action
- private boolean isInstantAppResolutionAllowedBody(
- Intent intent, List<ResolveInfo> resolvedActivities, int userId,
- boolean skipPackageCheck, int flags) {
- return liveComputer().isInstantAppResolutionAllowedBody(
- intent, resolvedActivities, userId,
- skipPackageCheck, flags);
- }
-
private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
Intent origIntent, String resolvedType, String callingPackage,
@Nullable String callingFeatureId, boolean isRequesterInstantApp,
@@ -9671,12 +9609,6 @@
resolvedType, flags);
}
- private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
- String resolvedType, int flags) {
- return liveComputer().isPersistentPreferredActivitySetByDpm(intent, userId,
- resolvedType, flags);
- }
-
@GuardedBy("mLock")
private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, boolean debug, int userId) {
@@ -10070,14 +10002,6 @@
instantAppPkgName);
}
- private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent,
- String resolvedType, int flags, int userId, boolean resolveForStart,
- boolean isRequesterInstantApp) {
- return liveComputer().maybeAddInstantAppInstaller(result, intent,
- resolvedType, flags, userId, resolveForStart,
- isRequesterInstantApp);
- }
-
private static class CrossProfileDomainInfo {
/* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
ResolveInfo resolveInfo;
@@ -10091,27 +10015,6 @@
}
/**
- * Verification statuses are ordered from the worse to the best, except for
- * INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
- */
- private int bestDomainVerificationStatus(int status1, int status2) {
- return liveComputer().bestDomainVerificationStatus(status1, status2);
- }
-
- private boolean isUserEnabled(int userId) {
- return liveComputer().isUserEnabled(userId);
- }
-
- /**
- * Filter out activities with systemUserOnly flag set, when current user is not System.
- *
- * @return filtered list
- */
- private List<ResolveInfo> filterIfNotSystemUser(List<ResolveInfo> resolveInfos, int userId) {
- return liveComputer().filterIfNotSystemUser(resolveInfos, userId);
- }
-
- /**
* Filters out ephemeral activities.
* <p>When resolving for an ephemeral app, only activities that 1) are defined in the
* ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
@@ -10130,72 +10033,6 @@
resolveForStart, userId, intent);
}
- /**
- * Returns the activity component that can handle install failures.
- * <p>By default, the instant application installer handles failures. However, an
- * application may want to handle failures on its own. Applications do this by
- * creating an activity with an intent filter that handles the action
- * {@link Intent#ACTION_INSTALL_FAILURE}.
- */
- private @Nullable ComponentName findInstallFailureActivity(
- String packageName, int filterCallingUid, int userId) {
- return liveComputer().findInstallFailureActivity(
- packageName, filterCallingUid, userId);
- }
-
- /**
- * @param resolveInfos list of resolve infos in descending priority order
- * @return if the list contains a resolve info with non-negative priority
- */
- private boolean hasNonNegativePriority(List<ResolveInfo> resolveInfos) {
- return liveComputer().hasNonNegativePriority(resolveInfos);
- }
-
- private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
- int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
- int userId) {
- return liveComputer().filterCandidatesWithDomainPreferredActivitiesLPr(intent,
- matchFlags, candidates, xpDomainInfo,
- userId);
- }
-
- private ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
- Intent intent, int matchFlags, List<ResolveInfo> candidates,
- CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
- return liveComputer().filterCandidatesWithDomainPreferredActivitiesLPrBody(
- intent, matchFlags, candidates,
- xpDomainInfo, userId, debug);
- }
-
-
- private ResolveInfo querySkipCurrentProfileIntents(
- List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
- int flags, int sourceUserId) {
- return liveComputer().querySkipCurrentProfileIntents(
- matchingFilters, intent, resolvedType,
- flags, sourceUserId);
- }
-
- // Return matching ResolveInfo in target user if any.
- private ResolveInfo queryCrossProfileIntents(
- List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
- int flags, int sourceUserId, boolean matchInCurrentProfile) {
- return liveComputer().queryCrossProfileIntents(
- matchingFilters, intent, resolvedType,
- flags, sourceUserId, matchInCurrentProfile);
- }
-
- /**
- * If the filter's target user can handle the intent and is enabled: returns a ResolveInfo that
- * will forward the intent to the filter's target user.
- * Otherwise, returns null.
- */
- private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, Intent intent,
- String resolvedType, int flags, int sourceUserId) {
- return liveComputer().createForwardingResolveInfo(filter, intent,
- resolvedType, flags, sourceUserId);
- }
-
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
@@ -10517,20 +10354,6 @@
includeInstantApps);
}
- private @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
- String resolvedType, int flags, int userId, int callingUid,
- String instantAppPkgName) {
- return liveComputer().queryIntentServicesInternalBody(intent,
- resolvedType, flags, userId, callingUid,
- instantAppPkgName);
- }
-
- private List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
- String instantAppPkgName, @UserIdInt int userId, int filterCallingUid) {
- return liveComputer().applyPostServiceResolutionFilter(resolveInfos,
- instantAppPkgName, userId, filterCallingUid);
- }
-
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
String resolvedType, int flags, int userId) {
@@ -10683,12 +10506,6 @@
return snapshotComputer().getInstalledPackages(flags, userId);
}
- private ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
- int callingUid) {
- return liveComputer().getInstalledPackagesBody(flags, userId,
- callingUid);
- }
-
private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
String[] permissions, boolean[] tmp, int flags, int userId) {
int numMatch = 0;
@@ -10872,12 +10689,6 @@
callingUid);
}
- private boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
- int callingUid) {
- return liveComputer().isInstantAppInternalBody(packageName, userId,
- callingUid);
- }
-
@Override
public byte[] getInstantAppCookie(String packageName, int userId) {
if (HIDE_EPHEMERAL_APIS) {
@@ -11733,18 +11544,6 @@
requireFullPermission, checkShell, message);
}
- private boolean hasCrossUserPermission(
- int callingUid, int callingUserId, int userId, boolean requireFullPermission,
- boolean requirePermissionWhenSameUser) {
- return liveComputer().hasCrossUserPermission(
- callingUid, callingUserId, userId, requireFullPermission,
- requirePermissionWhenSameUser);
- }
-
- private boolean hasPermission(String permission) {
- return liveComputer().hasPermission(permission);
- }
-
private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
return liveComputer().isSameProfileGroup(callerUserId, userId);
}
@@ -18518,7 +18317,7 @@
}
}
- @GuardedBy({"mInstallLock", "mLock"})
+ @GuardedBy("mInstallLock")
private void installPackagesTracedLI(List<InstallRequest> requests) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
@@ -20618,12 +20417,6 @@
return liveComputer().resolveInternalPackageNameLPr(packageName, versionCode);
}
- private String resolveInternalPackageNameInternalLocked(
- String packageName, long versionCode, int callingUid) {
- return liveComputer().resolveInternalPackageNameInternalLocked(
- packageName, versionCode, callingUid);
- }
-
boolean isCallerVerifier(int callingUid) {
final int callingUserId = UserHandle.getUserId(callingUid);
return mRequiredVerifierPackage != null &&
@@ -24015,6 +23808,15 @@
dumpState.setDump(DumpState.DUMP_PER_UID_READ_TIMEOUTS);
} else if ("snapshot".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_SNAPSHOT_STATISTICS);
+ if (opti < args.length) {
+ if ("--full".equals(args[opti])) {
+ dumpState.setBrief(false);
+ opti++;
+ } else if ("--brief".equals(args[opti])) {
+ dumpState.setBrief(true);
+ opti++;
+ }
+ }
} else if ("write".equals(cmd)) {
synchronized (mLock) {
writeSettingsLPrTEMP();
@@ -24030,11 +23832,11 @@
pw.println("vers,1");
}
- // reader
- if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
- if (!checkin) {
- dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
- }
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_VERSION)
+ && packageName == null) {
+ // dump version information for all volumes with installed packages
+ dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
}
if (!checkin
@@ -24065,7 +23867,8 @@
ipw.decreaseIndent();
}
- if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
+ && packageName == null) {
final String requiredVerifierPackage = mRequiredVerifierPackage;
if (!checkin) {
if (dumpState.onTitlePrinted()) {
@@ -24086,7 +23889,8 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER)
+ && packageName == null) {
final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
final ComponentName verifierComponent = proxy.getComponentName();
if (verifierComponent != null) {
@@ -24113,11 +23917,13 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_LIBS)
+ && packageName == null) {
dump(DumpState.DUMP_LIBS, fd, pw, dumpState);
}
- if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_FEATURES)
+ && packageName == null) {
if (dumpState.onTitlePrinted()) {
pw.println();
}
@@ -24127,12 +23933,7 @@
synchronized (mAvailableFeatures) {
for (FeatureInfo feat : mAvailableFeatures.values()) {
- if (checkin) {
- pw.print("feat,");
- pw.print(feat.name);
- pw.print(",");
- pw.println(feat.version);
- } else {
+ if (!checkin) {
pw.print(" ");
pw.print(feat.name);
if (feat.version > 0) {
@@ -24140,55 +23941,73 @@
pw.print(feat.version);
}
pw.println();
+ } else {
+ pw.print("feat,");
+ pw.print(feat.name);
+ pw.print(",");
+ pw.println(feat.version);
}
}
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PREFERRED)
+ && packageName == null) {
dump(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)
+ && packageName == null) {
dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)
+ && packageName == null) {
dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
synchronized (mLock) {
mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
synchronized (mLock) {
mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
}
@@ -24203,11 +24022,15 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_QUERIES)
+ && packageName == null) {
dump(DumpState.DUMP_QUERIES, fd, pw, dumpState);
}
- if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_SHARED_USERS)
+ && packageName == null) {
// This cannot be moved to ComputerEngine since the set of packages in the
// SharedUserSetting do not have a copy.
synchronized (mLock) {
@@ -24215,7 +24038,9 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_CHANGES)
+ && packageName == null) {
if (dumpState.onTitlePrinted()) pw.println();
pw.println("Package Changes:");
synchronized (mLock) {
@@ -24242,7 +24067,9 @@
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_FROZEN)
+ && packageName == null) {
// XXX should handle packageName != null by dumping only install data that
// the given package is involved with.
if (dumpState.onTitlePrinted()) pw.println();
@@ -24263,7 +24090,9 @@
ipw.decreaseIndent();
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_VOLUMES)
+ && packageName == null) {
if (dumpState.onTitlePrinted()) pw.println();
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
@@ -24282,50 +24111,61 @@
ipw.decreaseIndent();
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
&& packageName == null) {
synchronized (mLock) {
mComponentResolver.dumpServicePermissions(pw, dumpState);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_DEXOPT)
+ && packageName == null) {
if (dumpState.onTitlePrinted()) pw.println();
dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)
+ && packageName == null) {
if (dumpState.onTitlePrinted()) pw.println();
dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
- if (dumpState.onTitlePrinted()) pw.println();
- synchronized (mLock) {
- mSettings.dumpReadMessagesLPr(pw, dumpState);
+ if (dumpState.isDumping(DumpState.DUMP_MESSAGES)
+ && packageName == null) {
+ if (!checkin) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ synchronized (mLock) {
+ mSettings.dumpReadMessagesLPr(pw, dumpState);
+ }
+ pw.println();
+ pw.println("Package warning messages:");
+ dumpCriticalInfo(pw, null);
+ } else {
+ dumpCriticalInfo(pw, "msg,");
}
- pw.println();
- pw.println("Package warning messages:");
- dumpCriticalInfo(pw, null);
- }
-
- if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
- dumpCriticalInfo(pw, "msg,");
}
// PackageInstaller should be called outside of mPackages lock
- if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_INSTALLS)
+ && packageName == null) {
// XXX should handle packageName != null by dumping only install data that
// the given package is involved with.
if (dumpState.onTitlePrinted()) pw.println();
mInstallerService.dump(new IndentingPrintWriter(pw, " ", 120));
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_APEX)
+ && packageName == null) {
mApexManager.dump(pw, packageName);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
&& packageName == null) {
pw.println();
pw.println("Per UID read timeouts:");
@@ -24344,19 +24184,22 @@
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)
+ && packageName == null) {
pw.println("Snapshot statistics");
if (!mSnapshotEnabled) {
pw.println(" Snapshots disabled");
} else {
int hits = 0;
+ int level = sSnapshotCorked.get();
synchronized (mSnapshotLock) {
if (mSnapshotComputer != null) {
hits = mSnapshotComputer.getUsed();
}
}
final long now = SystemClock.currentTimeMicro();
- mSnapshotStatistics.dump(pw, " ", now, hits, true);
+ mSnapshotStatistics.dump(pw, " ", now, hits, level, dumpState.isBrief());
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 1814a8e..a1e5153 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -33,9 +33,12 @@
import java.io.IOException;
import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Collection;
+import java.util.concurrent.atomic.AtomicLong;
/**
* Callback data loader for PackageManagerShellCommand installations.
@@ -136,6 +139,12 @@
private final byte mMode;
private final String mData;
+ private final String mSalt;
+
+ private static AtomicLong sGlobalSalt = new AtomicLong((new SecureRandom()).nextLong());
+ private static Long nextGlobalSalt() {
+ return sGlobalSalt.incrementAndGet();
+ }
static Metadata forStdIn(String fileId) {
return new Metadata(STDIN, fileId);
@@ -144,7 +153,7 @@
/** @hide */
@VisibleForTesting
public static Metadata forLocalFile(String filePath) {
- return new Metadata(LOCAL_FILE, filePath);
+ return new Metadata(LOCAL_FILE, filePath, nextGlobalSalt().toString());
}
static Metadata forDataOnlyStreaming(String fileId) {
@@ -156,26 +165,71 @@
}
private Metadata(byte mode, String data) {
+ this(mode, data, null);
+ }
+
+ private Metadata(byte mode, String data, String salt) {
this.mMode = mode;
this.mData = (data == null) ? "" : data;
+ this.mSalt = salt;
}
static Metadata fromByteArray(byte[] bytes) throws IOException {
- if (bytes == null || bytes.length == 0) {
+ if (bytes == null || bytes.length < 5) {
return null;
}
- byte mode = bytes[0];
- String data = new String(bytes, 1, bytes.length - 1, StandardCharsets.UTF_8);
- return new Metadata(mode, data);
+ int offset = 0;
+ final byte mode = bytes[offset];
+ offset += 1;
+ final String data;
+ final String salt;
+ switch (mode) {
+ case LOCAL_FILE: {
+ int dataSize = ByteBuffer.wrap(bytes, offset, 4).order(
+ ByteOrder.LITTLE_ENDIAN).getInt();
+ offset += 4;
+ data = new String(bytes, offset, dataSize, StandardCharsets.UTF_8);
+ offset += dataSize;
+ salt = new String(bytes, offset, bytes.length - offset,
+ StandardCharsets.UTF_8);
+ break;
+ }
+ default:
+ data = new String(bytes, offset, bytes.length - offset,
+ StandardCharsets.UTF_8);
+ salt = null;
+ break;
+ }
+ return new Metadata(mode, data, salt);
}
/** @hide */
@VisibleForTesting
public byte[] toByteArray() {
- byte[] dataBytes = this.mData.getBytes(StandardCharsets.UTF_8);
- byte[] result = new byte[1 + dataBytes.length];
- result[0] = this.mMode;
- System.arraycopy(dataBytes, 0, result, 1, dataBytes.length);
+ final byte[] result;
+ final byte[] dataBytes = this.mData.getBytes(StandardCharsets.UTF_8);
+ switch (this.mMode) {
+ case LOCAL_FILE: {
+ int dataSize = dataBytes.length;
+ byte[] saltBytes = this.mSalt.getBytes(StandardCharsets.UTF_8);
+ result = new byte[1 + 4 + dataSize + saltBytes.length];
+ int offset = 0;
+ result[offset] = this.mMode;
+ offset += 1;
+ ByteBuffer.wrap(result, offset, 4).order(ByteOrder.LITTLE_ENDIAN).putInt(
+ dataSize);
+ offset += 4;
+ System.arraycopy(dataBytes, 0, result, offset, dataSize);
+ offset += dataSize;
+ System.arraycopy(saltBytes, 0, result, offset, saltBytes.length);
+ break;
+ }
+ default:
+ result = new byte[1 + dataBytes.length];
+ result[0] = this.mMode;
+ System.arraycopy(dataBytes, 0, result, 1, dataBytes.length);
+ break;
+ }
return result;
}
diff --git a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
index bfddaea..bc65c3a 100644
--- a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
+++ b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.content.IntentFilter;
-import com.android.server.WatchedIntentResolver;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
index 0aca6ee..fc7680b 100644
--- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java
+++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.content.IntentFilter;
-import com.android.server.WatchedIntentResolver;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 1b8eee39..4bc87a2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -351,6 +351,7 @@
private final PackageManagerTracedLock mLock;
+ @Watched(manual = true)
private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
private final File mSettingsFilename;
@@ -364,19 +365,21 @@
/** Map from package name to settings */
@Watched
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- final WatchedArrayMap<String, PackageSetting> mPackages = new WatchedArrayMap<>();
+ final WatchedArrayMap<String, PackageSetting> mPackages;
+ private final SnapshotCache<WatchedArrayMap<String, PackageSetting>> mPackagesSnapshot;
/**
* List of packages that were involved in installing other packages, i.e. are listed
* in at least one app's InstallSource.
*/
@Watched
- private final WatchedArraySet<String> mInstallerPackages = new WatchedArraySet<>();
+ private final WatchedArraySet<String> mInstallerPackages;
+ private final SnapshotCache<WatchedArraySet<String>> mInstallerPackagesSnapshot;
/** Map from package name to appId and excluded userids */
@Watched
- private final WatchedArrayMap<String, KernelPackageState> mKernelMapping =
- new WatchedArrayMap<>();
+ private final WatchedArrayMap<String, KernelPackageState> mKernelMapping;
+ private final SnapshotCache<WatchedArrayMap<String, KernelPackageState>> mKernelMappingSnapshot;
// List of replaced system applications
@Watched
@@ -397,7 +400,7 @@
/** Map from volume UUID to {@link VersionInfo} */
@Watched
- private WatchedArrayMap<String, VersionInfo> mVersion = new WatchedArrayMap<>();
+ private final WatchedArrayMap<String, VersionInfo> mVersion = new WatchedArrayMap<>();
/**
* Version details for a storage volume that may hold apps.
@@ -435,6 +438,7 @@
}
/** Device identity for the purpose of package verification. */
+ @Watched(manual = true)
private VerifierDeviceIdentity mVerifierDeviceIdentity;
// The user's preferred activities associated with particular intent
@@ -462,10 +466,12 @@
private final WatchedSparseArray<SettingBase> mOtherAppIds;
// For reading/writing settings file.
- private final ArrayList<Signature> mPastSignatures =
- new ArrayList<Signature>();
- private final ArrayMap<Long, Integer> mKeySetRefs =
- new ArrayMap<Long, Integer>();
+ @Watched
+ private final WatchedArrayList<Signature> mPastSignatures =
+ new WatchedArrayList<Signature>();
+ @Watched
+ private final WatchedArrayMap<Long, Integer> mKeySetRefs =
+ new WatchedArrayMap<Long, Integer>();
// Packages that have been renamed since they were first installed.
// Keys are the new names of the packages, values are the original
@@ -495,18 +501,21 @@
* TODO: make this just a local variable that is passed in during package
* scanning to make it less confusing.
*/
- private final ArrayList<PackageSetting> mPendingPackages = new ArrayList<>();
+ @Watched
+ private final WatchedArrayList<PackageSetting> mPendingPackages = new WatchedArrayList<>();
private final File mSystemDir;
- public final KeySetManagerService mKeySetManagerService =
- new KeySetManagerService(mPackages.untrackedStorage());
+ private final KeySetManagerService mKeySetManagerService;
/** Settings and other information about permissions */
+ @Watched(manual = true)
final LegacyPermissionSettings mPermissions;
+ @Watched(manual = true)
private final LegacyPermissionDataProvider mPermissionDataProvider;
+ @Watched(manual = true)
private final DomainVerificationManagerInternal mDomainVerificationManager;
/**
@@ -532,23 +541,7 @@
}};
}
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public Settings(Map<String, PackageSetting> pkgSettings) {
- mLock = new PackageManagerTracedLock();
- mPackages.putAll(pkgSettings);
- mAppIds = new WatchedArrayList<>();
- mOtherAppIds = new WatchedSparseArray<>();
- mSystemDir = null;
- mPermissions = null;
- mRuntimePermissionsPersistence = null;
- mPermissionDataProvider = null;
- mSettingsFilename = null;
- mBackupSettingsFilename = null;
- mPackageListFilename = null;
- mStoppedPackagesFilename = null;
- mBackupStoppedPackagesFilename = null;
- mKernelMappingFilename = null;
- mDomainVerificationManager = null;
+ private void registerObservers() {
mPackages.registerObserver(mObserver);
mInstallerPackages.registerObserver(mObserver);
mKernelMapping.registerObserver(mObserver);
@@ -564,7 +557,43 @@
mRenamedPackages.registerObserver(mObserver);
mNextAppLinkGeneration.registerObserver(mObserver);
mDefaultBrowserApp.registerObserver(mObserver);
+ mPendingPackages.registerObserver(mObserver);
+ mPastSignatures.registerObserver(mObserver);
+ mKeySetRefs.registerObserver(mObserver);
+ }
+ // CONSTRUCTOR
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public Settings(Map<String, PackageSetting> pkgSettings) {
+ mPackages = new WatchedArrayMap<>();
+ mPackagesSnapshot =
+ new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages");
+ mKernelMapping = new WatchedArrayMap<>();
+ mKernelMappingSnapshot =
+ new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping");
+ mInstallerPackages = new WatchedArraySet<>();
+ mInstallerPackagesSnapshot =
+ new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages,
+ "Settings.mInstallerPackages");
+ mKeySetManagerService = new KeySetManagerService(mPackages);
+
+ mLock = new PackageManagerTracedLock();
+ mPackages.putAll(pkgSettings);
+ mAppIds = new WatchedArrayList<>();
+ mOtherAppIds = new WatchedSparseArray<>();
+ mSystemDir = null;
+ mPermissions = null;
+ mRuntimePermissionsPersistence = null;
+ mPermissionDataProvider = null;
+ mSettingsFilename = null;
+ mBackupSettingsFilename = null;
+ mPackageListFilename = null;
+ mStoppedPackagesFilename = null;
+ mBackupStoppedPackagesFilename = null;
+ mKernelMappingFilename = null;
+ mDomainVerificationManager = null;
+
+ registerObservers();
Watchable.verifyWatchedAttributes(this, mObserver);
mSnapshot = makeCache();
@@ -574,6 +603,18 @@
LegacyPermissionDataProvider permissionDataProvider,
@NonNull DomainVerificationManagerInternal domainVerificationManager,
@NonNull PackageManagerTracedLock lock) {
+ mPackages = new WatchedArrayMap<>();
+ mPackagesSnapshot =
+ new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages");
+ mKernelMapping = new WatchedArrayMap<>();
+ mKernelMappingSnapshot =
+ new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping");
+ mInstallerPackages = new WatchedArraySet<>();
+ mInstallerPackagesSnapshot =
+ new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages,
+ "Settings.mInstallerPackages");
+ mKeySetManagerService = new KeySetManagerService(mPackages);
+
mLock = lock;
mAppIds = new WatchedArrayList<>();
mOtherAppIds = new WatchedSparseArray<>();
@@ -602,22 +643,7 @@
mDomainVerificationManager = domainVerificationManager;
- mPackages.registerObserver(mObserver);
- mInstallerPackages.registerObserver(mObserver);
- mKernelMapping.registerObserver(mObserver);
- mDisabledSysPackages.registerObserver(mObserver);
- mBlockUninstallPackages.registerObserver(mObserver);
- mVersion.registerObserver(mObserver);
- mPreferredActivities.registerObserver(mObserver);
- mPersistentPreferredActivities.registerObserver(mObserver);
- mCrossProfileIntentResolvers.registerObserver(mObserver);
- mSharedUsers.registerObserver(mObserver);
- mAppIds.registerObserver(mObserver);
- mOtherAppIds.registerObserver(mObserver);
- mRenamedPackages.registerObserver(mObserver);
- mNextAppLinkGeneration.registerObserver(mObserver);
- mDefaultBrowserApp.registerObserver(mObserver);
-
+ registerObservers();
Watchable.verifyWatchedAttributes(this, mObserver);
mSnapshot = makeCache();
@@ -629,8 +655,13 @@
* are changed by PackageManagerService APIs are deep-copied
*/
private Settings(Settings r) {
- final int mPackagesSize = r.mPackages.size();
- mPackages.putAll(r.mPackages);
+ mPackages = r.mPackagesSnapshot.snapshot();
+ mPackagesSnapshot = new SnapshotCache.Sealed<>();
+ mKernelMapping = r.mKernelMappingSnapshot.snapshot();
+ mKernelMappingSnapshot = new SnapshotCache.Sealed<>();
+ mInstallerPackages = r.mInstallerPackagesSnapshot.snapshot();
+ mInstallerPackagesSnapshot = new SnapshotCache.Sealed<>();
+ mKeySetManagerService = new KeySetManagerService(mPackages);
// The following assignments satisfy Java requirements but are not
// needed by the read-only methods. Note especially that the lock
@@ -647,9 +678,7 @@
mDomainVerificationManager = r.mDomainVerificationManager;
- mInstallerPackages.addAll(r.mInstallerPackages);
- mKernelMapping.putAll(r.mKernelMapping);
- mDisabledSysPackages.putAll(r.mDisabledSysPackages);
+ mDisabledSysPackages.snapshot(r.mDisabledSysPackages);
mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages);
mVersion.putAll(r.mVersion);
mVerifierDeviceIdentity = r.mVerifierDeviceIdentity;
@@ -659,23 +688,26 @@
mPersistentPreferredActivities, r.mPersistentPreferredActivities);
WatchedSparseArray.snapshot(
mCrossProfileIntentResolvers, r.mCrossProfileIntentResolvers);
- mSharedUsers.putAll(r.mSharedUsers);
+ mSharedUsers.snapshot(r.mSharedUsers);
mAppIds = r.mAppIds.snapshot();
mOtherAppIds = r.mOtherAppIds.snapshot();
- mPastSignatures.addAll(r.mPastSignatures);
- mKeySetRefs.putAll(r.mKeySetRefs);
+ WatchedArrayList.snapshot(
+ mPastSignatures, r.mPastSignatures);
+ WatchedArrayMap.snapshot(
+ mKeySetRefs, r.mKeySetRefs);
mRenamedPackages.snapshot(r.mRenamedPackages);
mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration);
mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp);
// mReadMessages
- mPendingPackages.addAll(r.mPendingPackages);
+ WatchedArrayList.snapshot(
+ mPendingPackages, r.mPendingPackages);
mSystemDir = null;
// mKeySetManagerService;
mPermissions = r.mPermissions;
mPermissionDataProvider = r.mPermissionDataProvider;
// Do not register any Watchables and do not create a snapshot cache.
- mSnapshot = null;
+ mSnapshot = new SnapshotCache.Sealed();
}
/**
@@ -2326,7 +2358,7 @@
serializer.startTag(null, "shared-user");
serializer.attribute(null, ATTR_NAME, usr.name);
serializer.attributeInt(null, "userId", usr.userId);
- usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
+ usr.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
serializer.endTag(null, "shared-user");
}
@@ -2736,11 +2768,11 @@
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
- pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
+ pkg.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
if (installSource.initiatingPackageSignatures != null) {
installSource.initiatingPackageSignatures.writeXml(
- serializer, "install-initiator-sigs", mPastSignatures);
+ serializer, "install-initiator-sigs", mPastSignatures.untrackedStorage());
}
writeSigningKeySetLPr(serializer, pkg.keySetData);
@@ -2909,7 +2941,7 @@
} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
// No longer used.
} else if (tagName.equals("keyset-settings")) {
- mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs);
+ mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs.untrackedStorage());
} else if (TAG_VERSION.equals(tagName)) {
final String volumeUuid = XmlUtils.readStringAttribute(parser,
ATTR_VOLUME_UUID);
@@ -3697,7 +3729,7 @@
} else if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
readEnabledComponentsLPw(packageSetting, parser, 0);
} else if (tagName.equals("sigs")) {
- packageSetting.signatures.readXml(parser, mPastSignatures);
+ packageSetting.signatures.readXml(parser, mPastSignatures.untrackedStorage());
} else if (tagName.equals(TAG_PERMISSIONS)) {
readInstallPermissionsLPr(parser,
packageSetting.getLegacyPermissionState(), users);
@@ -3728,7 +3760,7 @@
packageSetting.keySetData.addDefinedKeySet(id, alias);
} else if (tagName.equals("install-initiator-sigs")) {
final PackageSignatures signatures = new PackageSignatures();
- signatures.readXml(parser, mPastSignatures);
+ signatures.readXml(parser, mPastSignatures.untrackedStorage());
packageSetting.installSource =
packageSetting.installSource.setInitiatingPackageSignatures(signatures);
} else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
@@ -3923,7 +3955,7 @@
String tagName = parser.getName();
if (tagName.equals("sigs")) {
- su.signatures.readXml(parser, mPastSignatures);
+ su.signatures.readXml(parser, mPastSignatures.untrackedStorage());
} else if (tagName.equals("perms")) {
readInstallPermissionsLPr(parser, su.getLegacyPermissionState(), users);
} else {
@@ -4439,7 +4471,23 @@
pw.print(prefix); pw.print(" versionCode="); pw.print(ps.versionCode);
if (pkg != null) {
pw.print(" minSdk="); pw.print(pkg.getMinSdkVersion());
- pw.print(" targetSdk="); pw.print(pkg.getTargetSdkVersion());
+ pw.print(" targetSdk="); pw.println(pkg.getTargetSdkVersion());
+
+ SparseIntArray minExtensionVersions = pkg.getMinExtensionVersions();
+
+ pw.print(prefix); pw.print(" minExtensionVersions=[");
+ if (minExtensionVersions != null) {
+ List<String> minExtVerStrings = new ArrayList<>();
+ int size = minExtensionVersions.size();
+ for (int index = 0; index < size; index++) {
+ int key = minExtensionVersions.keyAt(index);
+ int value = minExtensionVersions.valueAt(index);
+ minExtVerStrings.add(key + "=" + value);
+ }
+
+ pw.print(TextUtils.join(", ", minExtVerStrings));
+ }
+ pw.print("]");
}
pw.println();
if (pkg != null) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 7f18c4b..1e9d7e1f 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1213,7 +1213,9 @@
final String pkg = shortcutInfo.getPackage();
final int userId = shortcutInfo.getUserId();
final String id = shortcutInfo.getId();
- getPackageShortcutsLocked(pkg, userId).mutateShortcut(id, shortcutInfo, cb);
+ synchronized (mLock) {
+ getPackageShortcutsLocked(pkg, userId).mutateShortcut(id, shortcutInfo, cb);
+ }
}
/** Return the last reset time. */
@@ -2254,24 +2256,6 @@
}
@Override
- public AndroidFuture updateShortcutVisibility(String callingPkg, String packageName,
- byte[] certificate, boolean visible, int userId) {
- final AndroidFuture<Void> ret = new AndroidFuture<>();
- injectPostToHandlerIfAppSearch(() -> {
- try {
- synchronized (mLock) {
- getPackageShortcutsForPublisherLocked(callingPkg, userId)
- .updateVisibility(packageName, certificate, visible);
- }
- ret.complete(null);
- } catch (Exception e) {
- ret.completeExceptionally(e);
- }
- });
- return ret;
- }
-
- @Override
public AndroidFuture requestPinShortcut(String packageName, ShortcutInfo shortcut,
IntentSender resultIntent, int userId) {
final AndroidFuture<Boolean> ret = new AndroidFuture<>();
diff --git a/services/core/java/com/android/server/pm/SnapshotStatistics.java b/services/core/java/com/android/server/pm/SnapshotStatistics.java
index c425bad5..95f80de 100644
--- a/services/core/java/com/android/server/pm/SnapshotStatistics.java
+++ b/services/core/java/com/android/server/pm/SnapshotStatistics.java
@@ -23,6 +23,7 @@
import android.os.SystemClock;
import android.text.TextUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.EventLogTags;
import java.io.PrintWriter;
@@ -239,6 +240,11 @@
public int mTotalUsed = 0;
/**
+ * The total number of times a snapshot was bypassed because corking was in effect.
+ */
+ public int mTotalCorked = 0;
+
+ /**
* The total number of builds that count as big, which means they took longer than
* SNAPSHOT_BIG_BUILD_TIME_NS.
*/
@@ -291,6 +297,13 @@
}
}
+ /**
+ * Record a cork.
+ */
+ private void corked() {
+ mTotalCorked++;
+ }
+
private Stats(long now) {
mStartTimeUs = now;
mTimes = new int[mTimeBins.count()];
@@ -308,6 +321,7 @@
mUsed = Arrays.copyOf(orig.mUsed, orig.mUsed.length);
mTotalBuilds = orig.mTotalBuilds;
mTotalUsed = orig.mTotalUsed;
+ mTotalCorked = orig.mTotalCorked;
mBigBuilds = orig.mBigBuilds;
mShortLived = orig.mShortLived;
mTotalTimeUs = orig.mTotalTimeUs;
@@ -365,6 +379,7 @@
* Dump the summary statistics record. Choose the header or the data.
* number of builds
* number of uses
+ * number of corks
* number of big builds
* number of short lifetimes
* cumulative build time, in seconds
@@ -373,13 +388,13 @@
private void dumpStats(PrintWriter pw, String indent, long now, boolean header) {
dumpPrefix(pw, indent, now, header, "Summary stats");
if (header) {
- pw.format(Locale.US, " %10s %10s %10s %10s %10s %10s",
- "TotBlds", "TotUsed", "BigBlds", "ShortLvd",
+ pw.format(Locale.US, " %10s %10s %10s %10s %10s %10s %10s",
+ "TotBlds", "TotUsed", "TotCork", "BigBlds", "ShortLvd",
"TotTime", "MaxTime");
} else {
pw.format(Locale.US,
- " %10d %10d %10d %10d %10d %10d",
- mTotalBuilds, mTotalUsed, mBigBuilds, mShortLived,
+ " %10d %10d %10d %10d %10d %10d %10d",
+ mTotalBuilds, mTotalUsed, mTotalCorked, mBigBuilds, mShortLived,
mTotalTimeUs / 1000, mMaxBuildTimeUs / 1000);
}
pw.println();
@@ -516,7 +531,7 @@
* @param done The time at which the snapshot rebuild completed, in ns.
* @param hits The number of times the previous snapshot was used.
*/
- public void rebuild(long now, long done, int hits) {
+ public final void rebuild(long now, long done, int hits) {
// The duration has a span of about 2000s
final int duration = (int) (done - now);
boolean reportEvent = false;
@@ -544,9 +559,20 @@
}
/**
+ * Record a corked snapshot request.
+ */
+ public final void corked() {
+ synchronized (mLock) {
+ mShort[0].corked();
+ mLong[0].corked();
+ }
+ }
+
+ /**
* Roll a stats array. Shift the elements up an index and create a new element at
* index zero. The old element zero is completed with the specified time.
*/
+ @GuardedBy("mLock")
private void shift(Stats[] s, long now) {
s[0].complete(now);
for (int i = s.length - 1; i > 0; i--) {
@@ -598,7 +624,8 @@
* Dump the statistics. The format is compatible with the PackageManager dumpsys
* output.
*/
- public void dump(PrintWriter pw, String indent, long now, int unrecorded, boolean full) {
+ public void dump(PrintWriter pw, String indent, long now, int unrecorded,
+ int corkLevel, boolean brief) {
// Grab the raw statistics under lock, but print them outside of the lock.
Stats[] l;
Stats[] s;
@@ -608,10 +635,11 @@
s = Arrays.copyOf(mShort, mShort.length);
s[0] = new Stats(s[0]);
}
- pw.format(Locale.US, "%s Unrecorded hits %d", indent, unrecorded);
+ pw.format(Locale.US, "%s Unrecorded-hits: %d Cork-level: %d", indent,
+ unrecorded, corkLevel);
pw.println();
dump(pw, indent, now, l, s, "stats");
- if (!full) {
+ if (brief) {
return;
}
pw.println();
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 90a3c58..b7a069e 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -151,6 +151,9 @@
"include-filter": "com.android.server.pm.parsing.SystemPartitionParseTest"
}
]
+ },
+ {
+ "name": "CtsPackageManagerBootTestCases"
}
],
"imports": [
diff --git a/services/core/java/com/android/server/WatchedIntentResolver.java b/services/core/java/com/android/server/pm/WatchedIntentResolver.java
similarity index 82%
rename from services/core/java/com/android/server/WatchedIntentResolver.java
rename to services/core/java/com/android/server/pm/WatchedIntentResolver.java
index 0831c36..1c3d884 100644
--- a/services/core/java/com/android/server/WatchedIntentResolver.java
+++ b/services/core/java/com/android/server/pm/WatchedIntentResolver.java
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.server.pm.WatchedIntentFilter;
+import com.android.server.IntentResolver;
import com.android.server.utils.Snappable;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
import com.android.server.utils.Watcher;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
/**
@@ -34,7 +36,8 @@
* @param <R> The resolver type.
* {@hide}
*/
-public abstract class WatchedIntentResolver<F extends Watchable, R extends Object>
+public abstract class WatchedIntentResolver<F extends WatchedIntentFilter,
+ R extends WatchedIntentFilter>
extends IntentResolver<F, R>
implements Watchable, Snappable {
@@ -116,11 +119,21 @@
onChanged();
}
+ // Sorts a List of IntentFilter objects into descending priority order.
+ @SuppressWarnings("rawtypes")
+ private static final Comparator<WatchedIntentFilter> sResolvePrioritySorter =
+ new Comparator<>() {
+ public int compare(WatchedIntentFilter o1, WatchedIntentFilter o2) {
+ final int q1 = o1.getPriority();
+ final int q2 = o2.getPriority();
+ return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0);
+ }
+ };
+
@Override
@SuppressWarnings("unchecked")
protected void sortResults(List<R> results) {
- super.sortResults(results);
- onChanged();
+ Collections.sort(results, sResolvePrioritySorter);
}
/**
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 61f51e3..0f67be7 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -444,6 +444,7 @@
componentInfo.directBootAware = mainComponent.isDirectBootAware();
componentInfo.enabled = mainComponent.isEnabled();
componentInfo.splitName = mainComponent.getSplitName();
+ componentInfo.attributionTags = mainComponent.getAttributionTags();
}
private static void assignStateFieldsForPackageItemInfo(
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 50f958f..a7bac20 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -37,6 +37,7 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.media.RingtoneManager;
+import android.media.midi.MidiManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
@@ -866,6 +867,11 @@
grantPermissionsToSystemPackage(pm, systemCaptionsServicePackageName, userId,
MICROPHONE_PERMISSIONS);
}
+
+ // Bluetooth MIDI Service
+ grantSystemFixedPermissionsToSystemPackage(pm,
+ MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, userId,
+ NEARBY_DEVICES_PERMISSIONS);
}
private String getDefaultSystemHandlerActivityPackageForCategory(PackageManagerWrapper pm,
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 3a097a7..2cfbf26 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -44,6 +44,7 @@
import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
@@ -140,9 +141,10 @@
}
@Override
- public int checkOperation(int code, int uid, String packageName, boolean raw,
- QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) {
- return superImpl.apply(code, uid, packageName, raw);
+ public int checkOperation(int code, int uid, String packageName,
+ @Nullable String attributionTag, boolean raw,
+ QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) {
+ return superImpl.apply(code, uid, packageName, attributionTag, raw);
}
@Override
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d73203d..c21f72e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1072,6 +1072,8 @@
private void powerLongPress(long eventTime) {
final int behavior = getResolvedLongPressOnPowerBehavior();
+ Slog.d(TAG, "powerLongPress: eventTime=" + eventTime
+ + " mLongPressOnPowerBehavior=" + mLongPressOnPowerBehavior);
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
@@ -5130,6 +5132,7 @@
long[] pattern;
switch (effectId) {
case HapticFeedbackConstants.CONTEXT_CLICK:
+ case HapticFeedbackConstants.GESTURE_END:
return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
if (!mHapticTextHandleEnabled) {
@@ -5142,7 +5145,6 @@
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
case HapticFeedbackConstants.ENTRY_BUMP:
case HapticFeedbackConstants.DRAG_CROSSING:
- case HapticFeedbackConstants.GESTURE_END:
return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
case HapticFeedbackConstants.VIRTUAL_KEY:
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8991981..3018e5f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2957,8 +2957,7 @@
// Group has been removed.
return;
}
- // TODO (b/175764708): Support per-display doze.
- wakefulness = getWakefulnessLocked();
+ wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) &&
mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId)
&& mDisplayGroupPowerStateMapper.isReady(groupId)) {
@@ -3100,6 +3099,7 @@
* Returns true if the device is allowed to doze in its current state.
*/
private boolean canDozeLocked() {
+ // TODO (b/175764708): Support per-display doze.
return getWakefulnessLocked() == WAKEFULNESS_DOZING;
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 68e7bdb..09f8941 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -179,7 +179,7 @@
true, /* enableQuickDoze */
true, /* forceAllAppsStandby */
true, /* forceBackgroundCheck */
- PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF, /* locationMode */
+ PowerManager.LOCATION_MODE_FOREGROUND_ONLY, /* locationMode */
PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY /* soundTriggerMode */
);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 457dc43..b1b537b 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -304,6 +304,13 @@
mHandler.post(() -> mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion));
}
+ boolean isTelephonyTimeZoneDetectionSupported() {
+ enforceManageTimeZoneDetectorPermission();
+
+ return ServiceConfigAccessor.getInstance(mContext)
+ .isTelephonyTimeZoneDetectionFeatureSupported();
+ }
+
boolean isGeoTimeZoneDetectionSupported() {
enforceManageTimeZoneDetectorPermission();
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index 9899b448b..a4a46a3 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -18,6 +18,7 @@
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED;
+import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_TELEPHONY_DETECTION_SUPPORTED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SERVICE_NAME;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_GEO_DETECTION_ENABLED;
@@ -61,6 +62,8 @@
return runIsAutoDetectionEnabled();
case SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED:
return runSetAutoDetectionEnabled();
+ case SHELL_COMMAND_IS_TELEPHONY_DETECTION_SUPPORTED:
+ return runIsTelephonyDetectionSupported();
case SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED:
return runIsGeoDetectionSupported();
case SHELL_COMMAND_IS_GEO_DETECTION_ENABLED:
@@ -89,6 +92,13 @@
return 0;
}
+ private int runIsTelephonyDetectionSupported() {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean enabled = mInterface.isTelephonyTimeZoneDetectionSupported();
+ pw.println(enabled);
+ return 0;
+ }
+
private int runIsGeoDetectionSupported() {
final PrintWriter pw = getOutPrintWriter();
boolean enabled = mInterface.isGeoTimeZoneDetectionSupported();
@@ -169,6 +179,9 @@
pw.printf(" Prints true/false according to the automatic time zone detection setting\n");
pw.printf(" %s true|false\n", SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED);
pw.printf(" Sets the automatic time zone detection setting.\n");
+ pw.printf(" %s\n", SHELL_COMMAND_IS_TELEPHONY_DETECTION_SUPPORTED);
+ pw.printf(" Prints true/false according to whether telephony time zone detection is"
+ + " supported on this device.\n");
pw.printf(" %s\n", SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED);
pw.printf(" Prints true/false according to whether geolocation time zone detection is"
+ " supported on this device.\n");
diff --git a/services/core/java/com/android/server/utils/SnapshotCache.java b/services/core/java/com/android/server/utils/SnapshotCache.java
index b4b8835..42b9b23 100644
--- a/services/core/java/com/android/server/utils/SnapshotCache.java
+++ b/services/core/java/com/android/server/utils/SnapshotCache.java
@@ -19,6 +19,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* A class that caches snapshots. Instances are instantiated on a {@link Watchable}; when the
* {@link Watchable} reports a change, the cache is cleared. The snapshot() method fetches the
@@ -35,25 +38,65 @@
*/
private static final boolean ENABLED = true;
+ /**
+ * The statistics for a single cache. The object records the number of times a
+ * snapshot was reused and the number of times a snapshot was rebuilt.
+ */
+ private static class Statistics {
+ final String mName;
+ private final AtomicInteger mReused = new AtomicInteger(0);
+ private final AtomicInteger mRebuilt = new AtomicInteger(0);
+ Statistics(@NonNull String n) {
+ mName = n;
+ }
+ }
+
// The source object from which snapshots are created. This may be null if createSnapshot()
// does not require it.
protected final T mSource;
// The cached snapshot
- private T mSnapshot = null;
+ private volatile T mSnapshot = null;
// True if the snapshot is sealed and may not be modified.
- private boolean mSealed = false;
+ private volatile boolean mSealed = false;
+
+ // The statistics for this cache. This may be null.
+ private final Statistics mStatistics;
+
+ /**
+ * The global list of caches.
+ */
+ private static final WeakHashMap<SnapshotCache, Void> sCaches = new WeakHashMap<>();
/**
* Create a cache with a source object for rebuilding snapshots and a
- * {@link Watchable} that notifies when the cache is invalid.
+ * {@link Watchable} that notifies when the cache is invalid. If the name is null
+ * then statistics are not collected for this cache.
+ * @param source Source data for rebuilding snapshots.
+ * @param watchable The object that notifies when the cache is invalid.
+ * @param name The name of the cache, for statistics reporting.
+ */
+ public SnapshotCache(@Nullable T source, @NonNull Watchable watchable, @Nullable String name) {
+ mSource = source;
+ watchable.registerObserver(this);
+ if (name != null) {
+ mStatistics = new Statistics(name);
+ sCaches.put(this, null);
+ } else {
+ mStatistics = null;
+ }
+ }
+
+ /**
+ * Create a cache with a source object for rebuilding snapshots and a
+ * {@link Watchable} that notifies when the cache is invalid. The name is null in
+ * this API.
* @param source Source data for rebuilding snapshots.
* @param watchable The object that notifies when the cache is invalid.
*/
public SnapshotCache(@Nullable T source, @NonNull Watchable watchable) {
- mSource = source;
- watchable.registerObserver(this);
+ this(source, watchable, null);
}
/**
@@ -63,13 +106,14 @@
public SnapshotCache() {
mSource = null;
mSealed = true;
+ mStatistics = null;
}
/**
* Notify the object that the source object has changed. If the local object is sealed then
* IllegalStateException is thrown. Otherwise, the cache is cleared.
*/
- public void onChange(@Nullable Watchable what) {
+ public final void onChange(@Nullable Watchable what) {
if (mSealed) {
throw new IllegalStateException("attempt to change a sealed object");
}
@@ -79,7 +123,7 @@
/**
* Seal the cache. Attempts to modify the cache will generate an exception.
*/
- public void seal() {
+ public final void seal() {
mSealed = true;
}
@@ -88,11 +132,14 @@
* new snapshot and saves it in the cache.
* @return A snapshot as returned by createSnapshot() and possibly cached.
*/
- public T snapshot() {
+ public final T snapshot() {
T s = mSnapshot;
if (s == null || !ENABLED) {
s = createSnapshot();
mSnapshot = s;
+ if (mStatistics != null) mStatistics.mRebuilt.incrementAndGet();
+ } else {
+ if (mStatistics != null) mStatistics.mReused.incrementAndGet();
}
return s;
}
@@ -123,4 +170,25 @@
throw new UnsupportedOperationException("cannot snapshot a sealed snaphot");
}
}
+
+ /**
+ * A snapshot cache suitable for Snappable types. The key is that Snappable types
+ * have a known implementation of createSnapshot() so that this class is concrete.
+ * @param <T> The class whose snapshot is being cached.
+ */
+ public static class Auto<T extends Snappable<T>> extends SnapshotCache<T> {
+ public Auto(@NonNull T source, @NonNull Watchable watchable, @Nullable String name) {
+ super(source, watchable, name);
+ }
+ public Auto(@NonNull T source, @NonNull Watchable watchable) {
+ this(source, watchable, null);
+ }
+ /**
+ * Concrete createSnapshot() using the snapshot() method of <T>.
+ */
+ public T createSnapshot() {
+ return mSource.snapshot();
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 19fbdbd..5565ccb 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -145,7 +145,7 @@
*/
public void handleSubscriptionsChanged() {
final Map<ParcelUuid, Set<String>> privilegedPackages = new HashMap<>();
- final Map<Integer, ParcelUuid> newSubIdToGroupMap = new HashMap<>();
+ final Map<Integer, SubscriptionInfo> newSubIdToInfoMap = new HashMap<>();
final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList();
if (allSubs == null) {
@@ -160,7 +160,7 @@
}
// Build subId -> subGrp cache
- newSubIdToGroupMap.put(subInfo.getSubscriptionId(), subInfo.getGroupUuid());
+ newSubIdToInfoMap.put(subInfo.getSubscriptionId(), subInfo);
// Update subscription groups that are both ready, and active. For a group to be
// considered active, both of the following must be true:
@@ -186,7 +186,7 @@
}
final TelephonySubscriptionSnapshot newSnapshot =
- new TelephonySubscriptionSnapshot(newSubIdToGroupMap, privilegedPackages);
+ new TelephonySubscriptionSnapshot(newSubIdToInfoMap, privilegedPackages);
// If snapshot was meaningfully updated, fire the callback
if (!newSnapshot.equals(mCurrentSnapshot)) {
@@ -245,7 +245,7 @@
/** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */
public static class TelephonySubscriptionSnapshot {
- private final Map<Integer, ParcelUuid> mSubIdToGroupMap;
+ private final Map<Integer, SubscriptionInfo> mSubIdToInfoMap;
private final Map<ParcelUuid, Set<String>> mPrivilegedPackages;
public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT =
@@ -253,12 +253,12 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
TelephonySubscriptionSnapshot(
- @NonNull Map<Integer, ParcelUuid> subIdToGroupMap,
+ @NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap,
@NonNull Map<ParcelUuid, Set<String>> privilegedPackages) {
- Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null");
+ Objects.requireNonNull(subIdToInfoMap, "subIdToInfoMap was null");
Objects.requireNonNull(privilegedPackages, "privilegedPackages was null");
- mSubIdToGroupMap = Collections.unmodifiableMap(subIdToGroupMap);
+ mSubIdToInfoMap = Collections.unmodifiableMap(subIdToInfoMap);
final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>();
for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) {
@@ -285,7 +285,9 @@
/** Returns the Subscription Group for a given subId. */
@Nullable
public ParcelUuid getGroupForSubId(int subId) {
- return mSubIdToGroupMap.get(subId);
+ return mSubIdToInfoMap.containsKey(subId)
+ ? mSubIdToInfoMap.get(subId).getGroupUuid()
+ : null;
}
/**
@@ -295,8 +297,8 @@
public Set<Integer> getAllSubIdsInGroup(ParcelUuid subGrp) {
final Set<Integer> subIds = new ArraySet<>();
- for (Entry<Integer, ParcelUuid> entry : mSubIdToGroupMap.entrySet()) {
- if (subGrp.equals(entry.getValue())) {
+ for (Entry<Integer, SubscriptionInfo> entry : mSubIdToInfoMap.entrySet()) {
+ if (subGrp.equals(entry.getValue().getGroupUuid())) {
subIds.add(entry.getKey());
}
}
@@ -304,9 +306,17 @@
return subIds;
}
+ /** Checks if the requested subscription is opportunistic */
+ @NonNull
+ public boolean isOpportunistic(int subId) {
+ return mSubIdToInfoMap.containsKey(subId)
+ ? mSubIdToInfoMap.get(subId).isOpportunistic()
+ : false;
+ }
+
@Override
public int hashCode() {
- return Objects.hash(mSubIdToGroupMap, mPrivilegedPackages);
+ return Objects.hash(mSubIdToInfoMap, mPrivilegedPackages);
}
@Override
@@ -317,7 +327,7 @@
final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj;
- return mSubIdToGroupMap.equals(other.mSubIdToGroupMap)
+ return mSubIdToInfoMap.equals(other.mSubIdToInfoMap)
&& mPrivilegedPackages.equals(other.mPrivilegedPackages);
}
@@ -326,7 +336,7 @@
pw.println("TelephonySubscriptionSnapshot:");
pw.increaseIndent();
- pw.println("mSubIdToGroupMap: " + mSubIdToGroupMap);
+ pw.println("mSubIdToInfoMap: " + mSubIdToInfoMap);
pw.println("mPrivilegedPackages: " + mPrivilegedPackages);
pw.decreaseIndent();
@@ -335,7 +345,7 @@
@Override
public String toString() {
return "TelephonySubscriptionSnapshot{ "
- + "mSubIdToGroupMap=" + mSubIdToGroupMap
+ + "mSubIdToInfoMap=" + mSubIdToInfoMap
+ ", mPrivilegedPackages=" + mPrivilegedPackages
+ " }";
}
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index 8818023..fb4c623 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -16,6 +16,10 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.ConnectivityManager;
@@ -25,8 +29,16 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnManager;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -35,9 +47,13 @@
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.TreeSet;
/**
* Tracks a set of Networks underpinning a VcnGatewayConnection.
@@ -51,6 +67,45 @@
public class UnderlyingNetworkTracker {
@NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
+ /**
+ * Minimum signal strength for a WiFi network to be eligible for switching to
+ *
+ * <p>A network that satisfies this is eligible to become the selected underlying network with
+ * no additional conditions
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT = -70;
+
+ /**
+ * Minimum signal strength to continue using a WiFi network
+ *
+ * <p>A network that satisfies the conditions may ONLY continue to be used if it is already
+ * selected as the underlying network. A WiFi network satisfying this condition, but NOT the
+ * prospective-network RSSI threshold CANNOT be switched to.
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;
+
+ /** Priority for any cellular network for which the subscription is listed as opportunistic */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_OPPORTUNISTIC_CELLULAR = 0;
+
+ /** Priority for any WiFi network which is in use, and satisfies the in-use RSSI threshold */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_WIFI_IN_USE = 1;
+
+ /** Priority for any WiFi network which satisfies the prospective-network RSSI threshold */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_WIFI_PROSPECTIVE = 2;
+
+ /** Priority for any standard macro cellular network */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_MACRO_CELLULAR = 3;
+
+ /** Priority for any other networks (including unvalidated, etc) */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_ANY = Integer.MAX_VALUE;
+
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
@@ -58,12 +113,17 @@
@NonNull private final Dependencies mDeps;
@NonNull private final Handler mHandler;
@NonNull private final ConnectivityManager mConnectivityManager;
+ @NonNull private final TelephonyCallback mActiveDataSubIdListener =
+ new VcnActiveDataSubscriptionIdListener();
@NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>();
@Nullable private NetworkCallback mWifiBringupCallback;
- @Nullable private NetworkCallback mRouteSelectionCallback;
+ @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback;
+ @Nullable private NetworkCallback mWifiExitRssiThresholdCallback;
+ @Nullable private UnderlyingNetworkListener mRouteSelectionCallback;
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
+ @Nullable private PersistableBundle mCarrierConfig;
private boolean mIsQuitting = false;
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
@@ -104,6 +164,30 @@
mHandler = new Handler(mVcnContext.getLooper());
mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
+ mVcnContext
+ .getContext()
+ .getSystemService(TelephonyManager.class)
+ .registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener);
+
+ // TODO: Listen for changes in carrier config that affect this.
+ for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
+ PersistableBundle config =
+ mVcnContext
+ .getContext()
+ .getSystemService(CarrierConfigManager.class)
+ .getConfigForSubId(subId);
+
+ if (config != null) {
+ mCarrierConfig = config;
+
+ // Attempt to use (any) non-opportunistic subscription. If this subscription is
+ // opportunistic, continue and try to find a non-opportunistic subscription, using
+ // the opportunistic ones as a last resort.
+ if (!isOpportunistic(mLastSnapshot, Collections.singleton(subId))) {
+ break;
+ }
+ }
+ }
registerOrUpdateNetworkRequests();
}
@@ -111,16 +195,30 @@
private void registerOrUpdateNetworkRequests() {
NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback;
NetworkCallback oldWifiCallback = mWifiBringupCallback;
+ NetworkCallback oldWifiEntryRssiThresholdCallback = mWifiEntryRssiThresholdCallback;
+ NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback;
List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
mCellBringupCallbacks.clear();
// Register new callbacks. Make-before-break; always register new callbacks before removal
// of old callbacks
if (!mIsQuitting) {
- mRouteSelectionCallback = new RouteSelectionCallback();
- mConnectivityManager.requestBackgroundNetwork(
+ mRouteSelectionCallback = new UnderlyingNetworkListener();
+ mConnectivityManager.registerNetworkCallback(
getRouteSelectionRequest(), mRouteSelectionCallback, mHandler);
+ mWifiEntryRssiThresholdCallback = new NetworkBringupCallback();
+ mConnectivityManager.registerNetworkCallback(
+ getWifiEntryRssiThresholdNetworkRequest(),
+ mWifiEntryRssiThresholdCallback,
+ mHandler);
+
+ mWifiExitRssiThresholdCallback = new NetworkBringupCallback();
+ mConnectivityManager.registerNetworkCallback(
+ getWifiExitRssiThresholdNetworkRequest(),
+ mWifiExitRssiThresholdCallback,
+ mHandler);
+
mWifiBringupCallback = new NetworkBringupCallback();
mConnectivityManager.requestBackgroundNetwork(
getWifiNetworkRequest(), mWifiBringupCallback, mHandler);
@@ -135,6 +233,8 @@
} else {
mRouteSelectionCallback = null;
mWifiBringupCallback = null;
+ mWifiEntryRssiThresholdCallback = null;
+ mWifiExitRssiThresholdCallback = null;
// mCellBringupCallbacks already cleared above.
}
@@ -145,6 +245,12 @@
if (oldWifiCallback != null) {
mConnectivityManager.unregisterNetworkCallback(oldWifiCallback);
}
+ if (oldWifiEntryRssiThresholdCallback != null) {
+ mConnectivityManager.unregisterNetworkCallback(oldWifiEntryRssiThresholdCallback);
+ }
+ if (oldWifiExitRssiThresholdCallback != null) {
+ mConnectivityManager.unregisterNetworkCallback(oldWifiExitRssiThresholdCallback);
+ }
for (NetworkCallback cellBringupCallback : oldCellCallbacks) {
mConnectivityManager.unregisterNetworkCallback(cellBringupCallback);
}
@@ -158,9 +264,18 @@
* carrier owned networks may be selected, as the request specifies only subIds in the VCN's
* subscription group, while the VCN networks are excluded by virtue of not having subIds set on
* the VCN-exposed networks.
+ *
+ * <p>If the VCN that this UnderlyingNetworkTracker belongs to is in test-mode, this will return
+ * a NetworkRequest that only matches Test Networks.
*/
private NetworkRequest getRouteSelectionRequest() {
+ if (mVcnContext.isInTestMode()) {
+ return getTestNetworkRequest(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
+ }
+
return getBaseNetworkRequestBuilder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
.setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
.build();
}
@@ -182,6 +297,38 @@
}
/**
+ * Builds the WiFi entry threshold signal strength request
+ *
+ * <p>This request ensures that WiFi reports the crossing of the wifi entry RSSI threshold.
+ * Without this request, WiFi rate-limits, and reports signal strength changes at too slow a
+ * pace to effectively select a short-lived WiFi offload network.
+ */
+ private NetworkRequest getWifiEntryRssiThresholdNetworkRequest() {
+ return getBaseNetworkRequestBuilder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+ // Ensure wifi updates signal strengths when crossing this threshold.
+ .setSignalStrength(getWifiEntryRssiThreshold(mCarrierConfig))
+ .build();
+ }
+
+ /**
+ * Builds the WiFi exit threshold signal strength request
+ *
+ * <p>This request ensures that WiFi reports the crossing of the wifi exit RSSI threshold.
+ * Without this request, WiFi rate-limits, and reports signal strength changes at too slow a
+ * pace to effectively select away from a failing WiFi network.
+ */
+ private NetworkRequest getWifiExitRssiThresholdNetworkRequest() {
+ return getBaseNetworkRequestBuilder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+ // Ensure wifi updates signal strengths when crossing this threshold.
+ .setSignalStrength(getWifiExitRssiThreshold(mCarrierConfig))
+ .build();
+ }
+
+ /**
* Builds a Cellular bringup request for a given subId
*
* <p>This request is filed in order to ensure that the Telephony stack always has a
@@ -210,6 +357,15 @@
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
+ /** Builds and returns a NetworkRequest for the given subIds to match Test Networks. */
+ private NetworkRequest getTestNetworkRequest(@NonNull Set<Integer> subIds) {
+ return new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .setSubscriptionIds(subIds)
+ .build();
+ }
+
/**
* Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
*
@@ -217,10 +373,18 @@
* reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
* or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
*/
- public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
- Objects.requireNonNull(snapshot, "Missing snapshot");
+ public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot newSnapshot) {
+ Objects.requireNonNull(newSnapshot, "Missing newSnapshot");
- mLastSnapshot = snapshot;
+ final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
+ mLastSnapshot = newSnapshot;
+
+ // Only trigger re-registration if subIds in this group have changed
+ if (oldSnapshot
+ .getAllSubIdsInGroup(mSubscriptionGroup)
+ .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) {
+ return;
+ }
registerOrUpdateNetworkRequests();
}
@@ -231,88 +395,43 @@
// Will unregister all existing callbacks, but not register new ones due to quitting flag.
registerOrUpdateNetworkRequests();
+
+ mVcnContext
+ .getContext()
+ .getSystemService(TelephonyManager.class)
+ .unregisterTelephonyCallback(mActiveDataSubIdListener);
}
- /** Returns whether the currently selected Network matches the given network. */
- private static boolean isSameNetwork(
- @Nullable UnderlyingNetworkRecord.Builder recordInProgress, @NonNull Network network) {
- return recordInProgress != null && recordInProgress.getNetwork().equals(network);
- }
+ private void reevaluateNetworks() {
+ TreeSet<UnderlyingNetworkRecord> sorted =
+ new TreeSet<>(
+ UnderlyingNetworkRecord.getComparator(
+ mSubscriptionGroup, mLastSnapshot, mCurrentRecord, mCarrierConfig));
+ sorted.addAll(mRouteSelectionCallback.getUnderlyingNetworks());
- /** Notify the Callback if a full UnderlyingNetworkRecord exists. */
- private void maybeNotifyCallback() {
- // Only forward this update if a complete record has been received
- if (!mRecordInProgress.isValid()) {
+ UnderlyingNetworkRecord candidate = sorted.isEmpty() ? null : sorted.first();
+ if (Objects.equals(mCurrentRecord, candidate)) {
return;
}
- // Only forward this update if the updated record differs form the current record
- UnderlyingNetworkRecord updatedRecord = mRecordInProgress.build();
- if (!updatedRecord.equals(mCurrentRecord)) {
- mCurrentRecord = updatedRecord;
-
- mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
- }
+ mCurrentRecord = candidate;
+ mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
}
- private void handleNetworkAvailable(@NonNull Network network) {
- mVcnContext.ensureRunningOnLooperThread();
-
- mRecordInProgress = new UnderlyingNetworkRecord.Builder(network);
- }
-
- private void handleNetworkLost(@NonNull Network network) {
- mVcnContext.ensureRunningOnLooperThread();
-
- if (!isSameNetwork(mRecordInProgress, network)) {
- Slog.wtf(TAG, "Non-underlying Network lost");
- return;
+ private static boolean isOpportunistic(
+ @NonNull TelephonySubscriptionSnapshot snapshot, Set<Integer> subIds) {
+ if (snapshot == null) {
+ Slog.wtf(TAG, "Got null snapshot");
+ return false;
}
- mRecordInProgress = null;
- mCurrentRecord = null;
- mCb.onSelectedUnderlyingNetworkChanged(null /* underlyingNetworkRecord */);
- }
-
- private void handleCapabilitiesChanged(
- @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
- mVcnContext.ensureRunningOnLooperThread();
-
- if (!isSameNetwork(mRecordInProgress, network)) {
- Slog.wtf(TAG, "Invalid update to NetworkCapabilities");
- return;
+ for (int subId : subIds) {
+ if (snapshot.isOpportunistic(subId)) {
+ return true;
+ }
}
- mRecordInProgress.setNetworkCapabilities(networkCapabilities);
-
- maybeNotifyCallback();
- }
-
- private void handlePropertiesChanged(
- @NonNull Network network, @NonNull LinkProperties linkProperties) {
- mVcnContext.ensureRunningOnLooperThread();
-
- if (!isSameNetwork(mRecordInProgress, network)) {
- Slog.wtf(TAG, "Invalid update to LinkProperties");
- return;
- }
-
- mRecordInProgress.setLinkProperties(linkProperties);
-
- maybeNotifyCallback();
- }
-
- private void handleNetworkBlocked(@NonNull Network network, boolean isBlocked) {
- mVcnContext.ensureRunningOnLooperThread();
-
- if (!isSameNetwork(mRecordInProgress, network)) {
- Slog.wtf(TAG, "Invalid update to isBlocked");
- return;
- }
-
- mRecordInProgress.setIsBlocked(isBlocked);
-
- maybeNotifyCallback();
+ return false;
}
/**
@@ -331,36 +450,104 @@
* truth.
*/
@VisibleForTesting
- class RouteSelectionCallback extends NetworkCallback {
+ class UnderlyingNetworkListener extends NetworkCallback {
+ private final Map<Network, UnderlyingNetworkRecord.Builder>
+ mUnderlyingNetworkRecordBuilders = new ArrayMap<>();
+
+ private List<UnderlyingNetworkRecord> getUnderlyingNetworks() {
+ final List<UnderlyingNetworkRecord> records = new ArrayList<>();
+
+ for (UnderlyingNetworkRecord.Builder builder :
+ mUnderlyingNetworkRecordBuilders.values()) {
+ if (builder.isValid()) {
+ records.add(builder.build());
+ }
+ }
+
+ return records;
+ }
+
@Override
public void onAvailable(@NonNull Network network) {
- handleNetworkAvailable(network);
+ mUnderlyingNetworkRecordBuilders.put(
+ network, new UnderlyingNetworkRecord.Builder(network));
}
@Override
public void onLost(@NonNull Network network) {
- handleNetworkLost(network);
+ mUnderlyingNetworkRecordBuilders.remove(network);
+
+ reevaluateNetworks();
}
@Override
public void onCapabilitiesChanged(
@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
- if (networkCapabilities.equals(mRecordInProgress.getNetworkCapabilities())) return;
- handleCapabilitiesChanged(network, networkCapabilities);
+ final UnderlyingNetworkRecord.Builder builder =
+ mUnderlyingNetworkRecordBuilders.get(network);
+ if (builder == null) {
+ Slog.wtf(TAG, "Got capabilities change for unknown key: " + network);
+ return;
+ }
+
+ builder.setNetworkCapabilities(networkCapabilities);
+ if (builder.isValid()) {
+ reevaluateNetworks();
+ }
}
@Override
public void onLinkPropertiesChanged(
@NonNull Network network, @NonNull LinkProperties linkProperties) {
- handlePropertiesChanged(network, linkProperties);
+ final UnderlyingNetworkRecord.Builder builder =
+ mUnderlyingNetworkRecordBuilders.get(network);
+ if (builder == null) {
+ Slog.wtf(TAG, "Got link properties change for unknown key: " + network);
+ return;
+ }
+
+ builder.setLinkProperties(linkProperties);
+ if (builder.isValid()) {
+ reevaluateNetworks();
+ }
}
@Override
public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
- handleNetworkBlocked(network, isBlocked);
+ final UnderlyingNetworkRecord.Builder builder =
+ mUnderlyingNetworkRecordBuilders.get(network);
+ if (builder == null) {
+ Slog.wtf(TAG, "Got blocked status change for unknown key: " + network);
+ return;
+ }
+
+ builder.setIsBlocked(isBlocked);
+ if (builder.isValid()) {
+ reevaluateNetworks();
+ }
}
}
+ private static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+ if (carrierConfig != null) {
+ return carrierConfig.getInt(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+ WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT);
+ }
+
+ return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
+ }
+
+ private static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+ if (carrierConfig != null) {
+ return carrierConfig.getInt(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
+ WIFI_EXIT_RSSI_THRESHOLD_DEFAULT);
+ }
+
+ return WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
+ }
+
/** A record of a single underlying network, caching relevant fields. */
public static class UnderlyingNetworkRecord {
@NonNull public final Network network;
@@ -397,6 +584,89 @@
return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
}
+ /**
+ * Gives networks a priority class, based on the following priorities:
+ *
+ * <ol>
+ * <li>Opportunistic cellular
+ * <li>Carrier WiFi, signal strength >= WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT
+ * <li>Carrier WiFi, active network + signal strength >= WIFI_EXIT_RSSI_THRESHOLD_DEFAULT
+ * <li>Macro cellular
+ * <li>Any others
+ * </ol>
+ */
+ private int calculatePriorityClass(
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ final NetworkCapabilities caps = networkCapabilities;
+
+ // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
+
+ if (isBlocked) {
+ Slog.wtf(TAG, "Network blocked for System Server: " + network);
+ return PRIORITY_ANY;
+ }
+
+ if (caps.hasTransport(TRANSPORT_CELLULAR)
+ && isOpportunistic(snapshot, caps.getSubscriptionIds())) {
+ // If this carrier is the active data provider, ensure that opportunistic is only
+ // ever prioritized if it is also the active data subscription. This ensures that
+ // if an opportunistic subscription is still in the process of being switched to,
+ // or switched away from, the VCN does not attempt to continue using it against the
+ // decision made at the telephony layer. Failure to do so may result in the modem
+ // switching back and forth.
+ //
+ // Allow the following two cases:
+ // 1. Active subId is NOT in the group that this VCN is supporting
+ // 2. This opportunistic subscription is for the active subId
+ if (!snapshot.getAllSubIdsInGroup(subscriptionGroup)
+ .contains(SubscriptionManager.getActiveDataSubscriptionId())
+ || caps.getSubscriptionIds()
+ .contains(SubscriptionManager.getActiveDataSubscriptionId())) {
+ return PRIORITY_OPPORTUNISTIC_CELLULAR;
+ }
+ }
+
+ if (caps.hasTransport(TRANSPORT_WIFI)) {
+ if (caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)
+ && currentlySelected != null
+ && network.equals(currentlySelected.network)) {
+ return PRIORITY_WIFI_IN_USE;
+ }
+
+ if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
+ return PRIORITY_WIFI_PROSPECTIVE;
+ }
+ }
+
+ // Disallow opportunistic subscriptions from matching PRIORITY_MACRO_CELLULAR, as might
+ // be the case when Default Data SubId (CBRS) != Active Data SubId (MACRO), as might be
+ // the case if the Default Data SubId does not support certain services (eg voice
+ // calling)
+ if (caps.hasTransport(TRANSPORT_CELLULAR)
+ && !isOpportunistic(snapshot, caps.getSubscriptionIds())) {
+ return PRIORITY_MACRO_CELLULAR;
+ }
+
+ return PRIORITY_ANY;
+ }
+
+ private static Comparator<UnderlyingNetworkRecord> getComparator(
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ return (left, right) -> {
+ return Integer.compare(
+ left.calculatePriorityClass(
+ subscriptionGroup, snapshot, currentlySelected, carrierConfig),
+ right.calculatePriorityClass(
+ subscriptionGroup, snapshot, currentlySelected, carrierConfig));
+ };
+ }
+
/** Dumps the state of this record for logging and debugging purposes. */
public void dump(IndentingPrintWriter pw) {
pw.println("UnderlyingNetworkRecord:");
@@ -418,6 +688,8 @@
boolean mIsBlocked;
boolean mWasIsBlockedSet;
+ @Nullable private UnderlyingNetworkRecord mCached;
+
private Builder(@NonNull Network network) {
mNetwork = network;
}
@@ -429,6 +701,7 @@
private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
mNetworkCapabilities = networkCapabilities;
+ mCached = null;
}
@Nullable
@@ -438,11 +711,13 @@
private void setLinkProperties(@NonNull LinkProperties linkProperties) {
mLinkProperties = linkProperties;
+ mCached = null;
}
private void setIsBlocked(boolean isBlocked) {
mIsBlocked = isBlocked;
mWasIsBlockedSet = true;
+ mCached = null;
}
private boolean isValid() {
@@ -450,12 +725,30 @@
}
private UnderlyingNetworkRecord build() {
- return new UnderlyingNetworkRecord(
- mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
+ if (!isValid()) {
+ throw new IllegalArgumentException(
+ "Called build before UnderlyingNetworkRecord was valid");
+ }
+
+ if (mCached == null) {
+ mCached =
+ new UnderlyingNetworkRecord(
+ mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
+ }
+
+ return mCached;
}
}
}
+ private class VcnActiveDataSubscriptionIdListener extends TelephonyCallback
+ implements ActiveDataSubscriptionIdListener {
+ @Override
+ public void onActiveDataSubscriptionIdChanged(int subId) {
+ reevaluateNetworks();
+ }
+ }
+
/** Callbacks for being notified of the changes in, or to the selected underlying network. */
public interface UnderlyingNetworkTrackerCallback {
/**
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index 7399e56..d958222 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -31,14 +31,17 @@
@NonNull private final Context mContext;
@NonNull private final Looper mLooper;
@NonNull private final VcnNetworkProvider mVcnNetworkProvider;
+ private final boolean mIsInTestMode;
public VcnContext(
@NonNull Context context,
@NonNull Looper looper,
- @NonNull VcnNetworkProvider vcnNetworkProvider) {
+ @NonNull VcnNetworkProvider vcnNetworkProvider,
+ boolean isInTestMode) {
mContext = Objects.requireNonNull(context, "Missing context");
mLooper = Objects.requireNonNull(looper, "Missing looper");
mVcnNetworkProvider = Objects.requireNonNull(vcnNetworkProvider, "Missing networkProvider");
+ mIsInTestMode = isInTestMode;
}
@NonNull
@@ -56,6 +59,10 @@
return mVcnNetworkProvider;
}
+ public boolean isInTestMode() {
+ return mIsInTestMode;
+ }
+
/**
* Verifies that the caller is running on the VcnContext Thread.
*
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index ae873e2..2ac50b6 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -66,7 +66,6 @@
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
import com.android.server.utils.ManagedApplicationService.LogEvent;
@@ -86,6 +85,7 @@
import java.util.Collection;
import java.util.Date;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -856,12 +856,15 @@
// If user changed drop restrictions for the old user.
if (oldUserId != newUserId) {
appOpsManager.setUserRestrictionForUser(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
- false, mOverlayToken, null, oldUserId);
+ false, mOverlayToken, (Map<String, String[]>) null, oldUserId);
}
// Apply the restrictions for the current user based on vr state
- String[] exemptions = (exemptedPackage == null) ? new String[0] :
- new String[] { exemptedPackage };
+ ArrayMap<String, String[]> exemptions = null;
+ if (exemptedPackage != null) {
+ exemptions = new ArrayMap<>(1);
+ exemptions.put(exemptedPackage, new String[0]);
+ }
appOpsManager.setUserRestrictionForUser(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
mVrModeEnabled, mOverlayToken, exemptions, newUserId);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 1ab402d..e1b7dd3 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -76,6 +76,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IncrementalStatesInfo;
import android.content.pm.dex.ArtManagerInternal;
import android.content.pm.dex.PackageOptimizationInfo;
import android.metrics.LogMaker;
@@ -84,6 +85,7 @@
import android.os.Looper;
import android.os.SystemClock;
import android.os.Trace;
+import android.os.incremental.IncrementalManager;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;
@@ -948,6 +950,14 @@
builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER,
packageOptimizationInfo.getCompilationFilter());
mMetricsLogger.write(builder);
+
+ // Incremental info
+ boolean isIncremental = false, isLoading = false;
+ final String codePath = info.applicationInfo.getCodePath();
+ if (codePath != null && IncrementalManager.isIncrementalPath(codePath)) {
+ isIncremental = true;
+ isLoading = isIncrementalLoading(info.packageName, info.userId);
+ }
FrameworkStatsLog.write(
FrameworkStatsLog.APP_START_OCCURRED,
info.applicationInfo.uid,
@@ -967,7 +977,10 @@
packageOptimizationInfo.getCompilationFilter(),
info.sourceType,
info.sourceEventDelayMs,
- isHibernating);
+ isHibernating,
+ isIncremental,
+ isLoading,
+ info.launchedActivityName.hashCode());
if (DEBUG_METRICS) {
Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
@@ -982,6 +995,12 @@
logAppStartMemoryStateCapture(info);
}
+ private boolean isIncrementalLoading(String packageName, int userId) {
+ final IncrementalStatesInfo info = mSupervisor.mService.getPackageManagerInternalLocked()
+ .getIncrementalStatesInfo(packageName, 0 /* filterCallingUid */, userId);
+ return info != null && info.isLoading();
+ }
+
private void logAppDisplayed(TransitionInfoSnapshot info) {
if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
return;
@@ -1063,6 +1082,14 @@
mMetricsLogger.write(builder);
final PackageOptimizationInfo packageOptimizationInfo =
infoSnapshot.getPackageOptimizationInfo(getArtManagerInternal());
+ // Incremental info
+ boolean isIncremental = false, isLoading = false;
+ final String codePath = info.mLastLaunchedActivity.info.applicationInfo.getCodePath();
+ if (codePath != null && IncrementalManager.isIncrementalPath(codePath)) {
+ isIncremental = true;
+ isLoading = isIncrementalLoading(info.mLastLaunchedActivity.packageName,
+ info.mLastLaunchedActivity.mUserId);
+ }
FrameworkStatsLog.write(
FrameworkStatsLog.APP_START_FULLY_DRAWN,
info.mLastLaunchedActivity.info.applicationInfo.uid,
@@ -1076,7 +1103,10 @@
packageOptimizationInfo.getCompilationReason(),
packageOptimizationInfo.getCompilationFilter(),
info.mSourceType,
- info.mSourceEventDelayMs);
+ info.mSourceEventDelayMs,
+ isIncremental,
+ isLoading,
+ info.mLastLaunchedActivity.info.name.hashCode());
// Ends the trace started at the beginning of this function. This is located here to allow
// the trace slice to have a noticable duration.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9f51d97..b55e653 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2483,39 +2483,52 @@
}
/**
- * @return whether this activity supports split-screen multi-window and can be put in the docked
- * root task.
+ * @return whether this activity supports split-screen multi-window and can be put in
+ * split-screen.
*/
@Override
public boolean supportsSplitScreenWindowingMode() {
- // An activity can not be docked even if it is considered resizeable because it only
- // supports picture-in-picture mode but has a non-resizeable resizeMode
+ return supportsSplitScreenWindowingModeInDisplayArea(getDisplayArea());
+ }
+
+ /**
+ * @return whether this activity supports split-screen multi-window and can be put in
+ * split-screen if it is in the given {@link TaskDisplayArea}.
+ */
+ boolean supportsSplitScreenWindowingModeInDisplayArea(@Nullable TaskDisplayArea tda) {
return super.supportsSplitScreenWindowingMode()
- && mAtmService.mSupportsSplitScreenMultiWindow && supportsMultiWindow();
+ && mAtmService.mSupportsSplitScreenMultiWindow
+ && supportsMultiWindowInDisplayArea(tda);
+ }
+
+ boolean supportsFreeform() {
+ return supportsFreeformInDisplayArea(getDisplayArea());
}
/**
* @return whether this activity supports freeform multi-window and can be put in the freeform
- * root task.
+ * windowing mode if it is in the given {@link TaskDisplayArea}.
*/
- boolean supportsFreeform() {
- return mAtmService.mSupportsFreeformWindowManagement && supportsMultiWindow();
+ boolean supportsFreeformInDisplayArea(@Nullable TaskDisplayArea tda) {
+ return mAtmService.mSupportsFreeformWindowManagement
+ && supportsMultiWindowInDisplayArea(tda);
+ }
+
+ boolean supportsMultiWindow() {
+ return supportsMultiWindowInDisplayArea(getDisplayArea());
}
/**
- * @return whether this activity supports multi-window.
+ * @return whether this activity supports multi-window if it is in the given
+ * {@link TaskDisplayArea}.
*/
- boolean supportsMultiWindow() {
- return mAtmService.mSupportsMultiWindow && !isActivityTypeHome()
- && (isResizeable() || mAtmService.mDevEnableNonResizableMultiWindow);
- }
-
- // TODO(b/176061101) replace supportsMultiWindow() after fixing tests.
- boolean supportsMultiWindow2() {
+ boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
+ if (isActivityTypeHome()) {
+ return false;
+ }
if (!mAtmService.mSupportsMultiWindow) {
return false;
}
- final TaskDisplayArea tda = getDisplayArea();
if (tda == null) {
return false;
}
@@ -2811,6 +2824,7 @@
final Transition newTransition = (!mAtmService.getTransitionController().isCollecting()
&& mAtmService.getTransitionController().getTransitionPlayer() != null)
? mAtmService.getTransitionController().createTransition(TRANSIT_CLOSE) : null;
+ mTaskSupervisor.mNoHistoryActivities.remove(this);
makeFinishingLocked();
// Make a local reference to its task since this.task could be set to null once this
// activity is destroyed and detached from task.
@@ -6851,7 +6865,7 @@
}
// An activity in size compatibility mode may have override bounds which equals to its
// parent bounds, so the exact bounds should also be checked to allow IME window to attach
- // to the activity. See {@link DisplayContent#isImeAttachedToApp}.
+ // to the activity. See {@link DisplayContent#shouldImeAttachedToApp}.
final WindowContainer parent = getParent();
return parent == null || parent.getBounds().equals(overrideBounds);
}
@@ -7469,7 +7483,10 @@
if (mVisibleRequested) {
// It may toggle the UI for user to restart the size compatibility mode activity.
display.handleActivitySizeCompatModeIfNeeded(this);
- } else if (mCompatDisplayInsets != null) {
+ } else if (mCompatDisplayInsets != null && !visibleIgnoringKeyguard) {
+ // visibleIgnoringKeyguard is checked to avoid clearing mCompatDisplayInsets during
+ // displays change. Displays are turned off during the change so mVisibleRequested
+ // can be false.
// The override changes can only be obtained from display, because we don't have the
// difference of full configuration in each hierarchy.
final int displayChanges = display.getCurrentOverrideConfigurationChanges();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 0d3c74e..7bc29f2 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -192,6 +192,7 @@
private boolean mKeepCurTransition;
private boolean mAvoidMoveToFront;
private boolean mFrozeTaskList;
+ private boolean mTransientLaunch;
// We must track when we deliver the new intent since multiple code paths invoke
// {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
@@ -1792,7 +1793,7 @@
mTargetRootTask.moveToFront("startActivityInner");
}
mRootWindowContainer.resumeFocusedTasksTopActivities(
- mTargetRootTask, mStartActivity, mOptions);
+ mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
}
}
mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask);
@@ -2209,6 +2210,7 @@
mKeepCurTransition = false;
mAvoidMoveToFront = false;
mFrozeTaskList = false;
+ mTransientLaunch = false;
mVoiceSession = null;
mVoiceInteractor = null;
@@ -2311,6 +2313,7 @@
mDoResume = false;
mAvoidMoveToFront = true;
}
+ mTransientLaunch = mOptions.getTransientLaunch();
mTargetRootTask = Task.fromWindowContainerToken(mOptions.getLaunchRootTask());
}
@@ -2642,7 +2645,7 @@
}
if (mTargetRootTask.isFocusable()) {
mRootWindowContainer.resumeFocusedTasksTopActivities(mTargetRootTask, null,
- mOptions);
+ mOptions, mTransientLaunch);
} else {
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index c6a66c5..37a9b80 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -835,7 +835,7 @@
final boolean forceResizable = Settings.Global.getInt(
resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
final boolean devEnableNonResizableMultiWindow = Settings.Global.getInt(
- resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0;
+ resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0;
final int supportsNonResizableMultiWindow = mContext.getResources().getInteger(
com.android.internal.R.integer.config_supportsNonResizableMultiWindow);
final int respectsActivityMinWidthHeightMultiWindow = mContext.getResources().getInteger(
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 8583061..7fe0f5b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -277,6 +277,13 @@
* settle down before doing so. It contains ActivityRecord objects. */
final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<>();
+ /**
+ * Activities that specify No History must be removed once the user navigates away from them.
+ * If the device goes to sleep with such an activity in the paused state then we save it
+ * here and finish it later if another activity replaces it on wakeup.
+ */
+ final ArrayList<ActivityRecord> mNoHistoryActivities = new ArrayList<>();
+
/** List of activities whose multi-window mode changed that we need to report to the
* application */
private final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>();
@@ -479,6 +486,20 @@
}
}
+ void finishNoHistoryActivitiesIfNeeded(ActivityRecord next) {
+ for (int i = mNoHistoryActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord noHistoryActivity = mNoHistoryActivities.get(i);
+ if (!noHistoryActivity.finishing && noHistoryActivity != next
+ && next.occludesParent()
+ && noHistoryActivity.getDisplayId() == next.getDisplayId()) {
+ ProtoLog.d(WM_DEBUG_STATES, "no-history finish of %s on new resume",
+ noHistoryActivity);
+ noHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
+ mNoHistoryActivities.remove(noHistoryActivity);
+ }
+ }
+ }
+
private static int nextTaskIdForUser(int taskId, int userId) {
int nextTaskId = taskId + 1;
if (nextTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) {
@@ -1684,7 +1705,8 @@
// Leave the task in its current root task or a fullscreen root task if it isn't
// resizeable and the preferred root task is in multi-window mode.
- if (inMultiWindowMode && !task.supportsMultiWindow()) {
+ if (inMultiWindowMode
+ && !task.supportsMultiWindowInDisplayArea(rootTask.getDisplayArea())) {
Slog.w(TAG, "Can not move unresizeable task=" + task + " to multi-window root task="
+ rootTask + " Moving to a fullscreen root task instead.");
if (prevRootTask != null) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d5a7619..c1b287f 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -449,6 +449,19 @@
if (mRemoteAnimationController != null) {
mRemoteAnimationController.goodToGo(transit);
+ } else if ((isTaskOpenTransitOld(transit) || transit == TRANSIT_OLD_WALLPAPER_CLOSE)
+ && topOpeningAnim != null) {
+ if (mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
+ && mService.getRecentsAnimationController() == null) {
+ final NavBarFadeAnimationController controller =
+ new NavBarFadeAnimationController(mDisplayContent);
+ // For remote animation case, the nav bar fades out and in is controlled by the
+ // remote side. For non-remote animation case, we play the fade out/in animation
+ // here. We play the nav bar fade-out animation when the app transition animation
+ // starts and play the fade-in animation sequentially once the fade-out is finished.
+ controller.fadeOutAndInSequentially(topOpeningAnim.getDurationHint(),
+ null /* fadeOutParent */, topOpeningApp.getSurfaceControl());
+ }
}
return redoLayout;
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 9855ea5..b24ab93 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -25,6 +25,9 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.util.Preconditions.checkState;
+import static com.android.server.wm.DisplayAreaProto.FEATURE_ID;
+import static com.android.server.wm.DisplayAreaProto.IS_ORGANIZED;
+import static com.android.server.wm.DisplayAreaProto.IS_ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayAreaProto.IS_TASK_DISPLAY_AREA;
import static com.android.server.wm.DisplayAreaProto.NAME;
import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
@@ -273,6 +276,9 @@
super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
proto.write(NAME, mName);
proto.write(IS_TASK_DISPLAY_AREA, isTaskDisplayArea());
+ proto.write(IS_ROOT_DISPLAY_AREA, asRootDisplayArea() != null);
+ proto.write(FEATURE_ID, mFeatureId);
+ proto.write(IS_ORGANIZED, isOrganized());
proto.end(token);
}
@@ -515,7 +521,7 @@
return true;
}
- protected boolean isTaskDisplayArea() {
+ boolean isTaskDisplayArea() {
return false;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 206f67e..1ec07d0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -901,6 +901,14 @@
&& preferredModeId != 0) {
mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId;
}
+
+ final float preferredMaxRefreshRate = getDisplayPolicy().getRefreshRatePolicy()
+ .getPreferredMaxRefreshRate(w);
+ if (mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate == 0
+ && preferredMaxRefreshRate != 0) {
+ mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate =
+ preferredMaxRefreshRate;
+ }
}
}
@@ -1531,7 +1539,9 @@
// to cover the activity configuration change.
return false;
}
- if (r.mStartingData != null && r.mStartingData.hasImeSurface()) {
+ if ((r.mStartingData != null && r.mStartingData.hasImeSurface())
+ || (mInsetsStateController.getImeSourceProvider()
+ .getSource().getVisibleFrame() != null)) {
// Currently it is unknown that when will IME window be ready. Reject the case to
// avoid flickering by showing IME in inconsistent orientation.
return false;
@@ -1562,6 +1572,10 @@
// If the transition has not started yet, the activity must be the top.
return false;
}
+ if (mLastWallpaperVisible && r.windowsCanBeWallpaperTarget()) {
+ // Use normal rotation animation for orientation change of visible wallpaper.
+ return false;
+ }
final int rotation = rotationForActivityInDifferentOrientation(r);
if (rotation == ROTATION_UNDEFINED) {
// The display rotation won't be changed by current top activity. The client side
@@ -2397,6 +2411,8 @@
mDisplayPolicy.onConfigurationChanged();
mPinnedTaskController.onPostDisplayConfigurationChanged();
}
+ // Update IME parent if needed.
+ updateImeParent();
if (lastOrientation != getConfiguration().orientation) {
getMetricsLogger().write(
@@ -2984,6 +3000,8 @@
// Hide the windows which are not significant in rotation animation. So that the windows
// don't need to block the unfreeze time.
if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()
+ // Do not fade for freezing without rotation change.
+ && mDisplayRotation.getRotation() != getWindowConfiguration().getRotation()
&& mFadeRotationAnimationController == null) {
startFadeRotationAnimation(false /* shouldDebounce */);
}
@@ -3621,7 +3639,7 @@
return mImeInputTarget != null && !mImeInputTarget.inMultiWindowMode();
}
- boolean isImeAttachedToApp() {
+ boolean shouldImeAttachedToApp() {
return isImeControlledByApp()
&& mImeLayeringTarget != null
&& mImeLayeringTarget.mActivityRecord != null
@@ -3635,6 +3653,20 @@
}
/**
+ * Unlike {@link #shouldImeAttachedToApp()}, this method returns {@code @true} only when both
+ * the IME layering target is valid to attach the IME surface to the app, and the
+ * {@link #mInputMethodSurfaceParent} of the {@link ImeContainer} has actually attached to
+ * the app. (i.e. Even if {@link #shouldImeAttachedToApp()} returns {@code true}, calling this
+ * method will return {@code false} if the IME surface doesn't actually attach to the app.)
+ */
+ boolean isImeAttachedToApp() {
+ return shouldImeAttachedToApp()
+ && mInputMethodSurfaceParent != null
+ && mInputMethodSurfaceParent.isSameSurface(
+ mImeLayeringTarget.mActivityRecord.getSurfaceControl());
+ }
+
+ /**
* Finds the window which can host IME if IME target cannot host it.
* e.g. IME target cannot host IME when it's display has a parent display OR when display
* doesn't support IME/system decorations.
@@ -3762,7 +3794,7 @@
@VisibleForTesting
void attachAndShowImeScreenshotOnTarget() {
// No need to attach screenshot if the IME target not exists or screen is off.
- if (!isImeAttachedToApp() || !mWmService.mPolicy.isScreenOn()) {
+ if (!shouldImeAttachedToApp() || !mWmService.mPolicy.isScreenOn()) {
return;
}
@@ -3930,12 +3962,13 @@
// Attach it to app if the target is part of an app and such app is covering the entire
// screen. If it's not covering the entire screen the IME might extend beyond the apps
// bounds.
- if (allowAttachToApp && isImeAttachedToApp()) {
+ if (allowAttachToApp && shouldImeAttachedToApp()) {
return mImeLayeringTarget.mActivityRecord.getSurfaceControl();
}
// Otherwise, we just attach it to where the display area policy put it.
- return mImeWindowsContainer.getParent().getSurfaceControl();
+ return mImeWindowsContainer.getParent() != null
+ ? mImeWindowsContainer.getParent().getSurfaceControl() : null;
}
void setLayoutNeeded() {
@@ -4226,6 +4259,7 @@
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
+ mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
true /* inTraversal, must call performTraversalInTrans... below */);
}
@@ -4509,12 +4543,13 @@
}
private static final class ApplySurfaceChangesTransactionState {
- boolean displayHasContent;
- boolean obscured;
- boolean syswin;
- boolean preferMinimalPostProcessing;
- float preferredRefreshRate;
- int preferredModeId;
+ public boolean displayHasContent;
+ public boolean obscured;
+ public boolean syswin;
+ public boolean preferMinimalPostProcessing;
+ public float preferredRefreshRate;
+ public int preferredModeId;
+ public float preferredMaxRefreshRate;
void reset() {
displayHasContent = false;
@@ -4523,6 +4558,7 @@
preferMinimalPostProcessing = false;
preferredRefreshRate = 0;
preferredModeId = 0;
+ preferredMaxRefreshRate = 0;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 94d81fb..0cf4379 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
+import static android.service.displayhash.DisplayHashingService.EXTRA_INTERVAL_BETWEEN_REQUESTS;
import static android.service.displayhash.DisplayHashingService.EXTRA_VERIFIED_DISPLAY_HASH;
-import static android.service.displayhash.DisplayHashingService.SERVICE_META_DATA;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
@@ -36,9 +36,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -54,22 +51,15 @@
import android.service.displayhash.DisplayHashParams;
import android.service.displayhash.DisplayHashingService;
import android.service.displayhash.IDisplayHashingService;
-import android.util.AttributeSet;
import android.util.Size;
import android.util.Slog;
-import android.util.Xml;
import android.view.MagnificationSpec;
import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -114,25 +104,18 @@
private final Matrix mTmpMatrix = new Matrix();
private final RectF mTmpRectF = new RectF();
- /**
- * Lock used when retrieving xml metadata. Lock when retrieving the xml data the first time
- * since it will be cached after that. Check if {@link #mParsedXml} is set to determine if the
- * metadata needs to retrieved.
- */
- private final Object mParseXmlLock = new Object();
/**
- * Flag whether the xml metadata has been retrieved and parsed. Once this is set to true,
- * there's no need to request metadata again.
+ * Lock used for the cached {@link #mIntervalBetweenRequestMillis}
*/
- @GuardedBy("mParseXmlLock")
- private boolean mParsedXml;
+ private final Object mIntervalBetweenRequestsLock = new Object();
/**
* Specified duration between requests to generate a display hash in milliseconds. Requests
* faster than this delay will be throttled.
*/
- private int mDurationBetweenRequestMillis = 0;
+ @GuardedBy("mDurationBetweenRequestsLock")
+ private int mIntervalBetweenRequestMillis = -1;
/**
* The last time an app requested to generate a display hash in System time.
@@ -203,8 +186,8 @@
return true;
}
- int mDurationBetweenRequestsMs = getDurationBetweenRequestMillis();
- if (currentTime - mLastRequestTimeMs < mDurationBetweenRequestsMs) {
+ int mIntervalBetweenRequestsMs = getIntervalBetweenRequestMillis();
+ if (currentTime - mLastRequestTimeMs < mIntervalBetweenRequestsMs) {
return false;
}
@@ -356,61 +339,25 @@
}
}
- private int getDurationBetweenRequestMillis() {
- if (!parseXmlProperties()) {
- return 0;
- }
- return mDurationBetweenRequestMillis;
- }
-
- private boolean parseXmlProperties() {
- // We have a separate lock for the xml parsing since it doesn't need to make the
- // request through the service connection. Instead, we have a lock to ensure we can
- // properly cache the xml metadata so we don't need to call into the ExtServices
- // process for each request.
- synchronized (mParseXmlLock) {
- if (mParsedXml) {
- return true;
+ private int getIntervalBetweenRequestMillis() {
+ // We have a separate lock for the hashing params to ensure we can properly cache the
+ // hashing params so we don't need to call into the ExtServices process for each request.
+ synchronized (mIntervalBetweenRequestsLock) {
+ if (mIntervalBetweenRequestMillis != -1) {
+ return mIntervalBetweenRequestMillis;
}
- final ServiceInfo serviceInfo = getServiceInfo();
- if (serviceInfo == null) return false;
-
- final PackageManager pm = mContext.getPackageManager();
-
- XmlResourceParser parser;
- parser = serviceInfo.loadXmlMetaData(pm, SERVICE_META_DATA);
- if (parser == null) {
- return false;
- }
-
- Resources res;
- try {
- res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
- } catch (PackageManager.NameNotFoundException e) {
- return false;
- }
-
- AttributeSet attrs = Xml.asAttributeSet(parser);
-
- int type;
- while (true) {
+ final SyncCommand syncCommand = new SyncCommand();
+ Bundle results = syncCommand.run((service, remoteCallback) -> {
try {
- if (!((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && type != XmlPullParser.START_TAG)) {
- break;
- }
- } catch (XmlPullParserException | IOException e) {
- return false;
+ service.getIntervalBetweenRequestsMillis(remoteCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to invoke getDisplayHashAlgorithms command", e);
}
- }
+ });
- TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHashingService);
- mDurationBetweenRequestMillis = sa.getInt(
- R.styleable.DisplayHashingService_durationBetweenRequestsMillis, 0);
- sa.recycle();
- mParsedXml = true;
- return true;
+ mIntervalBetweenRequestMillis = results.getInt(EXTRA_INTERVAL_BETWEEN_REQUESTS, 0);
+ return mIntervalBetweenRequestMillis;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 30f69dd79..30151c3 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -92,6 +92,7 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
@@ -372,7 +373,6 @@
* when the navigation bar mode is changed.
*/
private boolean mShouldAttachNavBarToAppDuringTransition;
- private NavBarFadeAnimationController mNavBarFadeAnimationController;
// -------- PolicyHandler --------
private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
@@ -1087,7 +1087,6 @@
break;
case TYPE_NAVIGATION_BAR:
mNavigationBar = win;
- updateNavBarFadeController();
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
(displayFrames, windowState, inOutFrame) -> {
@@ -1233,7 +1232,6 @@
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null);
} else if (mNavigationBar == win || mNavigationBarAlt == win) {
mNavigationBar = null;
- updateNavBarFadeController();
mNavigationBarAlt = null;
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null);
} else if (mNotificationShade == win) {
@@ -2059,7 +2057,6 @@
res.getBoolean(R.bool.config_attachNavBarToAppDuringTransition);
if (mShouldAttachNavBarToAppDuringTransition != shouldAttach) {
mShouldAttachNavBarToAppDuringTransition = shouldAttach;
- updateNavBarFadeController();
}
}
@@ -2667,7 +2664,8 @@
if (oldImmersiveMode != newImmersiveMode) {
mLastImmersiveMode = newImmersiveMode;
// The immersive confirmation window should be attached to the immersive window root.
- final int rootDisplayAreaId = win.getRootDisplayArea().mFeatureId;
+ final RootDisplayArea root = win.getRootDisplayArea();
+ final int rootDisplayAreaId = root == null ? FEATURE_UNDEFINED : root.mFeatureId;
mImmersiveModeConfirmation.immersiveModeChangedLw(rootDisplayAreaId, newImmersiveMode,
mService.mPolicy.isUserSetupComplete(),
isNavBarEmpty(disableFlags));
@@ -3060,19 +3058,4 @@
boolean shouldAttachNavBarToAppDuringTransition() {
return mShouldAttachNavBarToAppDuringTransition && mNavigationBar != null;
}
-
- @Nullable NavBarFadeAnimationController getNavBarFadeAnimationController() {
- return mNavBarFadeAnimationController;
- }
-
- private void updateNavBarFadeController() {
- if (shouldAttachNavBarToAppDuringTransition()) {
- if (mNavBarFadeAnimationController == null) {
- mNavBarFadeAnimationController =
- new NavBarFadeAnimationController(mDisplayContent);
- }
- } else {
- mNavBarFadeAnimationController = null;
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 0e73d79..2c969ab 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -186,6 +186,10 @@
@Surface.Rotation
private int mUserRotation = Surface.ROTATION_0;
+ private static final int CAMERA_ROTATION_DISABLED = 0;
+ private static final int CAMERA_ROTATION_ENABLED = 1;
+ private int mCameraRotationMode = CAMERA_ROTATION_DISABLED;
+
/**
* Flag that indicates this is a display that may run better when fixed to user rotation.
*/
@@ -1462,6 +1466,14 @@
if (shouldUpdateOrientationListener) {
updateOrientationListenerLw(); // Enable or disable the orientation listener.
}
+
+ final int cameraRotationMode = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.CAMERA_AUTOROTATE, 0,
+ UserHandle.USER_CURRENT);
+ if (mCameraRotationMode != cameraRotationMode) {
+ mCameraRotationMode = cameraRotationMode;
+ shouldUpdateRotation = true;
+ }
}
return shouldUpdateRotation;
@@ -1491,6 +1503,7 @@
pw.print(prefix + " mUserRotationMode="
+ WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation));
+ pw.print(" mCameraRotationMode=" + mCameraRotationMode);
pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations));
pw.print(prefix + " mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation));
@@ -1539,6 +1552,13 @@
}
}
+ @Override
+ public boolean isRotationResolverEnabled() {
+ return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+ && mCameraRotationMode == CAMERA_ROTATION_ENABLED
+ && !mService.mPowerManager.isPowerSaveMode();
+ }
+
@Override
public void onProposedRotationChanged(int rotation) {
@@ -1582,6 +1602,10 @@
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.USER_ROTATION), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.CAMERA_AUTOROTATE), false, this,
+ UserHandle.USER_ALL);
+
updateSettings();
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index fd4bbd7..6340036 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
@@ -441,8 +444,9 @@
Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
+ final boolean containsAppExtras = containsApplicationExtras(mDataDescription);
mService.mRoot.forAllWindows(w -> {
- sendDragStartedLocked(w, touchX, touchY, mDataDescription, mData);
+ sendDragStartedLocked(w, touchX, touchY, containsAppExtras);
}, false /* traverseTopToBottom */);
}
@@ -455,9 +459,9 @@
* process, so it's safe for the caller to call recycle() on the event afterwards.
*/
private void sendDragStartedLocked(WindowState newWin, float touchX, float touchY,
- ClipDescription desc, ClipData data) {
+ boolean containsAppExtras) {
final boolean interceptsGlobalDrag = targetInterceptsGlobalDrag(newWin);
- if (mDragInProgress && isValidDropTarget(newWin, interceptsGlobalDrag)) {
+ if (mDragInProgress && isValidDropTarget(newWin, containsAppExtras, interceptsGlobalDrag)) {
DragEvent event = obtainDragEvent(DragEvent.ACTION_DRAG_STARTED, touchX, touchY,
interceptsGlobalDrag, false /* includeDragSurface */,
null /* dragAndDropPermission */);
@@ -476,10 +480,28 @@
}
}
- private boolean isValidDropTarget(WindowState targetWin, boolean interceptsGlobalDrag) {
+ /**
+ * Returns true if this is a drag of an application mime type.
+ */
+ private boolean containsApplicationExtras(ClipDescription desc) {
+ if (desc == null) {
+ return false;
+ }
+ return desc.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY)
+ || desc.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT)
+ || desc.hasMimeType(MIMETYPE_APPLICATION_TASK);
+ }
+
+ private boolean isValidDropTarget(WindowState targetWin, boolean containsAppExtras,
+ boolean interceptsGlobalDrag) {
if (targetWin == null) {
return false;
}
+ if (!interceptsGlobalDrag && containsAppExtras) {
+ // App-drags can only go to windows that can intercept global drag, and not to normal
+ // app windows
+ return false;
+ }
if (!targetWin.isPotentialDragTarget(interceptsGlobalDrag)) {
return false;
}
@@ -522,7 +544,8 @@
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "need to send DRAG_STARTED to new window " + newWin);
}
- sendDragStartedLocked(newWin, mCurrentX, mCurrentY, mDataDescription, mData);
+ sendDragStartedLocked(newWin, mCurrentX, mCurrentY,
+ containsApplicationExtras(mDataDescription));
}
}
diff --git a/services/core/java/com/android/server/wm/FadeAnimationController.java b/services/core/java/com/android/server/wm/FadeAnimationController.java
index 17d20ae..2f3ad40 100644
--- a/services/core/java/com/android/server/wm/FadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeAnimationController.java
@@ -37,7 +37,7 @@
*/
public class FadeAnimationController {
protected final Context mContext;
- private final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();
+ protected final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();
public FadeAnimationController(DisplayContent displayContent) {
mContext = displayContent.mWmService.mContext;
@@ -69,17 +69,11 @@
return;
}
- final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
- if (animation == null) {
+ final FadeAnimationAdapter animationAdapter = createAdapter(show, windowToken);
+ if (animationAdapter == null) {
return;
}
- final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
- createAnimationSpec(animation);
-
- final FadeAnimationAdapter animationAdapter = new FadeAnimationAdapter(
- windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken);
-
// We deferred the end of the animation when hiding the token, so we need to end it now that
// it's shown again.
final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
@@ -92,7 +86,21 @@
show /* hidden */, animationType, finishedCallback);
}
- private LocalAnimationAdapter.AnimationSpec createAnimationSpec(@NonNull Animation animation) {
+ protected FadeAnimationAdapter createAdapter(boolean show, WindowToken windowToken) {
+ final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
+ if (animation == null) {
+ return null;
+ }
+
+ final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
+ createAnimationSpec(animation);
+
+ return new FadeAnimationAdapter(
+ windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken);
+ }
+
+ protected LocalAnimationAdapter.AnimationSpec createAnimationSpec(
+ @NonNull Animation animation) {
return new LocalAnimationAdapter.AnimationSpec() {
final Transformation mTransformation = new Transformation();
@@ -130,8 +138,8 @@
};
}
- private class FadeAnimationAdapter extends LocalAnimationAdapter {
- private final boolean mShow;
+ protected class FadeAnimationAdapter extends LocalAnimationAdapter {
+ protected final boolean mShow;
private final WindowToken mToken;
FadeAnimationAdapter(AnimationSpec windowAnimationSpec,
diff --git a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
index 30861eb..e50dc51 100644
--- a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
@@ -18,6 +18,7 @@
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import android.view.SurfaceControl;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
@@ -35,12 +36,17 @@
private static final Interpolator FADE_OUT_INTERPOLATOR =
new PathInterpolator(0.2f, 0f, 1f, 1f);
+ private DisplayContent mDisplayContent;
private final WindowState mNavigationBar;
private Animation mFadeInAnimation;
private Animation mFadeOutAnimation;
+ private SurfaceControl mFadeInParent;
+ private SurfaceControl mFadeOutParent;
+ private boolean mPlaySequentially = false;
public NavBarFadeAnimationController(DisplayContent displayContent) {
super(displayContent);
+ mDisplayContent = displayContent;
mNavigationBar = displayContent.getDisplayPolicy().getNavigationBar();
mFadeInAnimation = new AlphaAnimation(0f, 1f);
mFadeInAnimation.setDuration(FADE_IN_DURATION);
@@ -61,12 +67,103 @@
return mFadeOutAnimation;
}
+ @Override
+ protected FadeAnimationAdapter createAdapter(boolean show, WindowToken windowToken) {
+ final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
+ if (animation == null) {
+ return null;
+ }
+
+ final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
+ createAnimationSpec(animation);
+ return new NavFadeAnimationAdapter(
+ windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken,
+ show ? mFadeInParent : mFadeOutParent);
+ }
+
/**
* Run the fade-in/out animation for the navigation bar.
*
* @param show true for fade-in, otherwise for fade-out.
*/
public void fadeWindowToken(boolean show) {
- fadeWindowToken(show, mNavigationBar.mToken, ANIMATION_TYPE_APP_TRANSITION);
+ final FadeRotationAnimationController controller =
+ mDisplayContent.getFadeRotationAnimationController();
+ final Runnable fadeAnim = () -> fadeWindowToken(show, mNavigationBar.mToken,
+ ANIMATION_TYPE_APP_TRANSITION);
+ if (controller == null) {
+ fadeAnim.run();
+ } else if (!controller.isTargetToken(mNavigationBar.mToken)) {
+ // If fade rotation animation is running and the nav bar is not controlled by it:
+ // - For fade-in animation, defer the animation until fade rotation animation finishes.
+ // - For fade-out animation, just play the animation.
+ if (show) {
+ controller.setOnShowRunnable(fadeAnim);
+ } else {
+ fadeAnim.run();
+ }
+ } else {
+ // If fade rotation animation is running and controlling the nav bar, make sure we empty
+ // the mDeferredFinishCallbacks and defer the runnable until fade rotation animation
+ // finishes.
+ final Runnable runnable = mDeferredFinishCallbacks.remove(mNavigationBar.mToken);
+ if (runnable != null) {
+ controller.setOnShowRunnable(runnable);
+ }
+ }
+ }
+
+ void fadeOutAndInSequentially(long totalDuration, SurfaceControl fadeOutParent,
+ SurfaceControl fadeInParent) {
+ mPlaySequentially = true;
+ if (totalDuration > 0) {
+ // The animation duration of each animation varies so we set the fade-out duration to
+ // 1/3 of the total app transition duration and set the fade-in duration to 2/3 of it.
+ final long fadeInDuration = totalDuration * 2L / 3L;
+ mFadeOutAnimation.setDuration(totalDuration - fadeInDuration);
+ mFadeInAnimation.setDuration(fadeInDuration);
+ }
+ mFadeOutParent = fadeOutParent;
+ mFadeInParent = fadeInParent;
+ fadeWindowToken(false);
+ }
+
+ /**
+ * The animation adapter that is capable of playing fade-out and fade-in sequentially and
+ * reparenting the navigation bar to a specified SurfaceControl when fade animation starts.
+ */
+ protected class NavFadeAnimationAdapter extends FadeAnimationAdapter {
+ private SurfaceControl mParent;
+
+ NavFadeAnimationAdapter(AnimationSpec windowAnimationSpec,
+ SurfaceAnimationRunner surfaceAnimationRunner, boolean show,
+ WindowToken token, SurfaceControl parent) {
+ super(windowAnimationSpec, surfaceAnimationRunner, show, token);
+ mParent = parent;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ super.startAnimation(animationLeash, t, type, finishCallback);
+ if (mParent != null && mParent.isValid()) {
+ t.reparent(animationLeash, mParent);
+ // Place the nav bar on top of anything else (e.g. ime and starting window) in the
+ // parent.
+ t.setLayer(animationLeash, Integer.MAX_VALUE);
+ }
+ }
+
+ @Override
+ public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+ if (mPlaySequentially) {
+ if (!mShow) {
+ fadeWindowToken(true);
+ }
+ return false;
+ } else {
+ return super.shouldDeferAnimationFinish(endDeferFinishCallback);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index dec6460..b44a980 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -231,7 +231,8 @@
@Override
public void setFinishTaskTransaction(int taskId,
- PictureInPictureSurfaceTransaction finishTransaction) {
+ PictureInPictureSurfaceTransaction finishTransaction,
+ SurfaceControl overlay) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"setFinishTaskTransaction(%d): transaction=%s", taskId, finishTransaction);
final long token = Binder.clearCallingIdentity();
@@ -241,6 +242,7 @@
final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
if (taskAdapter.mTask.mTaskId == taskId) {
taskAdapter.mFinishTransaction = finishTransaction;
+ taskAdapter.mFinishOverlay = overlay;
break;
}
}
@@ -289,12 +291,6 @@
}
}
if (!behindSystemBars) {
- // Make sure to update the correct IME parent in case that the IME parent
- // may be computed as display layer when re-layout window happens during
- // rotation but there is intermediate state that the bounds of task and
- // the IME target's activity is not the same during rotating.
- mDisplayContent.updateImeParent();
-
// Hiding IME if IME window is not attached to app.
// Since some windowing mode is not proper to snapshot Task with IME window
// while the app transitioning to the next task (e.g. split-screen mode)
@@ -518,7 +514,6 @@
void removeAnimation(TaskAnimationAdapter taskAdapter) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"removeAnimation(%d)", taskAdapter.mTask.mTaskId);
- taskAdapter.mTask.setCanAffectSystemUiFlags(true);
taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter.mLastAnimationType,
taskAdapter);
mPendingAnimations.remove(taskAdapter);
@@ -633,6 +628,7 @@
return;
}
mNavigationBarAttachedToApp = true;
+ navWindow.mToken.cancelAnimation();
final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
if (shouldTranslateNavBar) {
@@ -653,7 +649,8 @@
}
}
- private void restoreNavigationBarFromApp(boolean animate) {
+ @VisibleForTesting
+ void restoreNavigationBarFromApp(boolean animate) {
if (!mNavigationBarAttachedToApp) {
return;
}
@@ -676,23 +673,9 @@
t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer());
if (animate) {
- final NavBarFadeAnimationController navBarFadeAnimationController =
- mDisplayContent.getDisplayPolicy().getNavBarFadeAnimationController();
- final Runnable fadeInAnim = () -> {
- // Reparent the SurfaceControl of nav bar token back.
- t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
- // Run fade-in animation to show navigation bar back to bottom of the display.
- if (navBarFadeAnimationController != null) {
- navBarFadeAnimationController.fadeWindowToken(true);
- }
- };
- final FadeRotationAnimationController fadeRotationAnimationController =
- mDisplayContent.getFadeRotationAnimationController();
- if (fadeRotationAnimationController != null) {
- fadeRotationAnimationController.setOnShowRunnable(fadeInAnim);
- } else {
- fadeInAnim.run();
- }
+ final NavBarFadeAnimationController controller =
+ new NavBarFadeAnimationController(mDisplayContent);
+ controller.fadeWindowToken(true);
} else {
// Reparent the SurfaceControl of nav bar token back.
t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
@@ -1106,6 +1089,8 @@
private final Rect mLocalBounds = new Rect();
// The final surface transaction when animation is finished.
private PictureInPictureSurfaceTransaction mFinishTransaction;
+ // An overlay used to mask the content as an app goes into PIP
+ private SurfaceControl mFinishOverlay;
TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
mTask = task;
@@ -1143,20 +1128,38 @@
void onCleanup() {
if (mFinishTransaction != null) {
final Transaction pendingTransaction = mTask.getPendingTransaction();
+
+ // Reparent the overlay
+ if (mFinishOverlay != null) {
+ pendingTransaction.reparent(mFinishOverlay, mTask.mSurfaceControl);
+ }
+
+ // Transfer the transform from the leash to the task
PictureInPictureSurfaceTransaction.apply(mFinishTransaction,
mTask.mSurfaceControl, pendingTransaction);
- mTask.setLastRecentsAnimationTransaction(mFinishTransaction);
+ mTask.setLastRecentsAnimationTransaction(mFinishTransaction, mFinishOverlay);
if (mDisplayContent.isFixedRotationLaunchingApp(mTargetActivityRecord)) {
// The transaction is needed for position when rotating the display.
mDisplayContent.mPinnedTaskController.setEnterPipTransaction(
mFinishTransaction);
}
mFinishTransaction = null;
+ mFinishOverlay = null;
pendingTransaction.apply();
+
+ // In the case where we are transferring the transform to the task in preparation
+ // for entering PIP, we disable the task being able to affect sysui flags otherwise
+ // it may cause a flash
+ if (mTask.getActivityType() != mTargetActivityType) {
+ mTask.setCanAffectSystemUiFlags(false);
+ }
} else if (!mTask.isAttached()) {
// Apply the task's pending transaction in case it is detached and its transaction
// is not reachable.
mTask.getPendingTransaction().apply();
+
+ // Reset whether this task can affect the sysui flags
+ mTask.setCanAffectSystemUiFlags(true);
}
}
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 26871d1..deaf611 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -20,6 +20,7 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import android.util.ArraySet;
+import android.view.Display;
import android.view.Display.Mode;
import android.view.DisplayInfo;
@@ -95,18 +96,10 @@
return 0;
}
- // If app requests a certain refresh rate or mode, don't override it.
if (w.mAttrs.preferredRefreshRate != 0 || w.mAttrs.preferredDisplayModeId != 0) {
return w.mAttrs.preferredDisplayModeId;
}
- final String packageName = w.getOwningPackage();
-
- // If app is using Camera, force it to default (lower) refresh rate.
- if (mNonHighRefreshRatePackages.contains(packageName)) {
- return mLowRefreshRateMode.getModeId();
- }
-
return 0;
}
@@ -145,6 +138,44 @@
if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
return mLowRefreshRateMode.getRefreshRate();
}
+
+ final int preferredModeId = getPreferredModeId(w);
+ if (preferredModeId > 0) {
+ DisplayInfo info = w.getDisplayInfo();
+ if (info != null) {
+ for (Display.Mode mode : info.supportedModes) {
+ if (preferredModeId == mode.getModeId()) {
+ return mode.getRefreshRate();
+ }
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ float getPreferredMaxRefreshRate(WindowState w) {
+ // If app is animating, it's not able to control refresh rate because we want the animation
+ // to run in default refresh rate.
+ if (w.isAnimating(TRANSITION | PARENTS)) {
+ return 0;
+ }
+
+ // If app requests a certain refresh rate or mode, don't override it.
+ if (w.mAttrs.preferredDisplayModeId != 0) {
+ return 0;
+ }
+
+ if (w.mAttrs.preferredMaxDisplayRefreshRate > 0) {
+ return w.mAttrs.preferredMaxDisplayRefreshRate;
+ }
+
+ final String packageName = w.getOwningPackage();
+ // If app is using Camera, force it to default (lower) refresh rate.
+ if (mNonHighRefreshRatePackages.contains(packageName)) {
+ return mLowRefreshRateMode.getRefreshRate();
+ }
+
return 0;
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ab7e65c..ea80b8b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2139,7 +2139,8 @@
// Move the last recents animation transaction from original task to the new one.
if (task.mLastRecentsAnimationTransaction != null) {
rootTask.setLastRecentsAnimationTransaction(
- task.mLastRecentsAnimationTransaction);
+ task.mLastRecentsAnimationTransaction,
+ task.mLastRecentsAnimationOverlay);
task.clearLastRecentsAnimationTransaction();
}
@@ -2292,7 +2293,13 @@
boolean resumeFocusedTasksTopActivities(
Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions) {
+ return resumeFocusedTasksTopActivities(targetRootTask, target, targetOptions,
+ false /* deferPause */);
+ }
+ boolean resumeFocusedTasksTopActivities(
+ Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions,
+ boolean deferPause) {
if (!mTaskSupervisor.readyToResume()) {
return false;
}
@@ -2300,7 +2307,8 @@
boolean result = false;
if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea()
|| getTopDisplayFocusedRootTask() == targetRootTask)) {
- result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions);
+ result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions,
+ deferPause);
}
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
@@ -3046,7 +3054,7 @@
// There is a 1-to-1 relationship between root task and task when not in
// primary split-windowing mode.
if (task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && r.supportsSplitScreenWindowingMode()
+ && r.supportsSplitScreenWindowingModeInDisplayArea(task.getDisplayArea())
&& (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
|| windowingMode == WINDOWING_MODE_UNDEFINED)) {
return true;
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index ba18893..3c6c23b 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -520,7 +520,7 @@
/**
* Converts {@link AnimationType} to String.
*/
- private static String animationTypeToString(@AnimationType int type) {
+ static String animationTypeToString(@AnimationType int type) {
switch (type) {
case ANIMATION_TYPE_NONE: return "none";
case ANIMATION_TYPE_APP_TRANSITION: return "app_transition";
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index be01173..1eb4122 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -61,6 +61,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
@@ -201,7 +202,6 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -482,6 +482,9 @@
// Do not forget to reset this after reparenting.
// TODO: remove this once the recents animation is moved to the Shell
PictureInPictureSurfaceTransaction mLastRecentsAnimationTransaction;
+ // The content overlay to be applied with mLastRecentsAnimationTransaction
+ // TODO: remove this once the recents animation is moved to the Shell
+ SurfaceControl mLastRecentsAnimationOverlay;
static final int LAYER_RANK_INVISIBLE = -1;
// Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
@@ -585,13 +588,6 @@
ActivityRecord mLastPausedActivity = null;
/**
- * Activities that specify No History must be removed once the user navigates away from them.
- * If the device goes to sleep with such an activity in the paused state then we save it here
- * and finish it later if another activity replaces it on wakeup.
- */
- ActivityRecord mLastNoHistoryActivity = null;
-
- /**
* Current activity that is resumed, or null if there is none.
* Only set at leaf tasks.
*/
@@ -1083,25 +1079,6 @@
}
/**
- * Convenience method to reparent a task to the top or bottom position of the root task, with
- * an option to skip scheduling the picture-in-picture mode change.
- */
- boolean reparent(Task preferredRootTask, boolean toTop,
- @ReparentMoveRootTaskMode int moveRootTaskMode, boolean animate, boolean deferResume,
- boolean schedulePictureInPictureModeChange, String reason) {
- return reparent(preferredRootTask, toTop ? MAX_VALUE : 0, moveRootTaskMode, animate,
- deferResume, schedulePictureInPictureModeChange, reason);
- }
-
- /** Convenience method to reparent a task to a specific position of the root task. */
- boolean reparent(Task preferredRootTask, int position,
- @ReparentMoveRootTaskMode int moveRootTaskMode, boolean animate, boolean deferResume,
- String reason) {
- return reparent(preferredRootTask, position, moveRootTaskMode, animate, deferResume,
- true /* schedulePictureInPictureModeChange */, reason);
- }
-
- /**
* Reparents the task into a preferred root task, creating it if necessary.
*
* @param preferredRootTask the target root task to move this task
@@ -1670,6 +1647,14 @@
return isUidPresent;
}
+ ActivityRecord topActivityContainsStartingWindow() {
+ if (getParent() == null) {
+ return null;
+ }
+ return getActivity((r) -> r.getWindow(window ->
+ window.getBaseType() == TYPE_APPLICATION_STARTING) != null);
+ }
+
ActivityRecord topActivityWithStartingWindow() {
if (getParent() == null) {
return null;
@@ -1977,32 +1962,46 @@
@Override
public boolean supportsSplitScreenWindowingMode() {
- final Task topTask = getTopMostTask();
- return super.supportsSplitScreenWindowingMode()
- && (topTask == null || topTask.supportsSplitScreenWindowingModeInner());
+ return supportsSplitScreenWindowingModeInDisplayArea(getDisplayArea());
}
- private boolean supportsSplitScreenWindowingModeInner() {
+ boolean supportsSplitScreenWindowingModeInDisplayArea(@Nullable TaskDisplayArea tda) {
+ final Task topTask = getTopMostTask();
+ return super.supportsSplitScreenWindowingMode()
+ && (topTask == null || topTask.supportsSplitScreenWindowingModeInner(tda));
+ }
+
+ private boolean supportsSplitScreenWindowingModeInner(@Nullable TaskDisplayArea tda) {
return super.supportsSplitScreenWindowingMode()
&& mAtmService.mSupportsSplitScreenMultiWindow
- && supportsMultiWindow();
+ && supportsMultiWindowInDisplayArea(tda);
}
boolean supportsFreeform() {
- return mAtmService.mSupportsFreeformWindowManagement && supportsMultiWindow();
+ return supportsFreeformInDisplayArea(getDisplayArea());
+ }
+
+ /**
+ * @return whether this task supports freeform multi-window if it is in the given
+ * {@link TaskDisplayArea}.
+ */
+ boolean supportsFreeformInDisplayArea(@Nullable TaskDisplayArea tda) {
+ return mAtmService.mSupportsFreeformWindowManagement
+ && supportsMultiWindowInDisplayArea(tda);
}
boolean supportsMultiWindow() {
- return mAtmService.mSupportsMultiWindow
- && (isResizeable() || mAtmService.mDevEnableNonResizableMultiWindow);
+ return supportsMultiWindowInDisplayArea(getDisplayArea());
}
- // TODO(b/176061101) replace supportsMultiWindow() after fixing tests.
- boolean supportsMultiWindow2() {
+ /**
+ * @return whether this task supports multi-window if it is in the given
+ * {@link TaskDisplayArea}.
+ */
+ boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
if (!mAtmService.mSupportsMultiWindow) {
return false;
}
- final TaskDisplayArea tda = getDisplayArea();
if (tda == null) {
return false;
}
@@ -2938,20 +2937,6 @@
return bounds;
}
- /** Updates the task's bounds and override configuration to match what is expected for the
- * input root task. */
- void updateOverrideConfigurationForRootTask(Task inRootTask) {
- final Task rootTask = getRootTask();
-
- if (rootTask != null && rootTask == inRootTask) {
- return;
- }
-
- if (!inRootTask.inFreeformWindowingMode()) {
- setBounds(inRootTask.getRequestedOverrideBounds());
- }
- }
-
/** Returns the bounds that should be used to launch this task. */
Rect getLaunchBounds() {
final Task rootTask = getRootTask();
@@ -4340,7 +4325,12 @@
case WINDOWING_MODE_FULLSCREEN:
if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
// At least one of the split-screen stacks that covers this one is translucent.
- return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+ // When in split mode, home task will be reparented to the secondary split while
+ // leaving tasks not supporting split below. Due to
+ // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
+ // the bottom, this makes sure tasks not in split roots won't occlude home task
+ // unexpectedly.
+ return TASK_VISIBILITY_INVISIBLE;
}
break;
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
@@ -5396,6 +5386,10 @@
: WINDOWING_MODE_FULLSCREEN;
}
if (currentMode == WINDOWING_MODE_PINNED) {
+ // In the case that we've disabled affecting the SysUI flags as a part of seamlessly
+ // transferring the transform on the leash to the task, reset this state once we've
+ // actually entered pip
+ setCanAffectSystemUiFlags(true);
mRootWindowContainer.notifyActivityPipModeChanged(null);
}
if (likelyResolvedMode == WINDOWING_MODE_PINNED
@@ -5733,7 +5727,9 @@
ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
mPausingActivity = prev;
mLastPausedActivity = prev;
- mLastNoHistoryActivity = prev.isNoHistory() ? prev : null;
+ if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
+ mTaskSupervisor.mNoHistoryActivities.add(prev);
+ }
prev.setState(PAUSING, "startPausingLocked");
prev.getTask().touchActiveTime();
@@ -5779,13 +5775,13 @@
Slog.w(TAG, "Exception thrown during pause", e);
mPausingActivity = null;
mLastPausedActivity = null;
- mLastNoHistoryActivity = null;
+ mTaskSupervisor.mNoHistoryActivities.remove(prev);
}
}
} else {
mPausingActivity = null;
mLastPausedActivity = null;
- mLastNoHistoryActivity = null;
+ mTaskSupervisor.mNoHistoryActivities.remove(prev);
}
// If we are not going to sleep, we want to ensure the device is
@@ -5995,16 +5991,6 @@
return inPinnedWindowingMode();
}
- // TODO(NOW!)
- /**
- * Returns {@code true} if this is the top-most split-screen-primary or
- * split-screen-secondary root task, {@code false} otherwise.
- */
- boolean isTopSplitScreenRootTask() {
- return inSplitScreenWindowingMode()
- && this == getDisplayArea().getTopRootTaskInWindowingMode(getWindowingMode());
- }
-
void checkTranslucentActivityWaiting(ActivityRecord top) {
if (mTranslucentActivityWaiting != top) {
mUndrawnActivitiesBelowTopTranslucent.clear();
@@ -6080,6 +6066,7 @@
* @param prev The previously resumed activity, for when in the process
* of pausing; can be null to call from elsewhere.
* @param options Activity options.
+ * @param deferPause When {@code true}, this will not pause back tasks.
*
* @return Returns true if something is being resumed, or false if
* nothing happened.
@@ -6090,7 +6077,8 @@
* right activity for the current system state.
*/
@GuardedBy("mService")
- boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
+ boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options,
+ boolean deferPause) {
if (mInResumeTopActivity) {
// Don't even start recursing.
return false;
@@ -6103,7 +6091,7 @@
if (isLeafTask()) {
if (isFocusableAndVisible()) {
- someActivityResumed = resumeTopActivityInnerLocked(prev, options);
+ someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause);
}
} else {
int idx = mChildren.size() - 1;
@@ -6116,7 +6104,8 @@
break;
}
- someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options);
+ someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options,
+ deferPause);
// Doing so in order to prevent IndexOOB since hierarchy might changes while
// resuming activities, for example dismissing split-screen while starting
// non-resizeable activity.
@@ -6144,8 +6133,15 @@
return someActivityResumed;
}
+ /** @see #resumeTopActivityUncheckedLocked(ActivityRecord, ActivityOptions, boolean) */
@GuardedBy("mService")
- private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
+ boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
+ return resumeTopActivityUncheckedLocked(prev, options, false /* skipPause */);
+ }
+
+ @GuardedBy("mService")
+ private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options,
+ boolean deferPause) {
if (!mAtmService.isBooting() && !mAtmService.isBooted()) {
// Not ready yet!
return false;
@@ -6206,9 +6202,7 @@
// If we are sleeping, and there is no resumed activity, and the top activity is paused,
// well that is the state we want.
- if (shouldSleepOrShutDownActivities()
- && mLastPausedActivity == next
- && mRootWindowContainer.allPausedActivitiesComplete()) {
+ if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
executeAppTransition(options);
@@ -6232,14 +6226,6 @@
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
- // If we are currently pausing an activity, then don't do anything until that is done.
- if (!mRootWindowContainer.allPausedActivitiesComplete()) {
- ProtoLog.v(WM_DEBUG_STATES,
- "resumeTopActivityLocked: Skip resume: some activity pausing.");
-
- return false;
- }
-
mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
ActivityRecord lastResumed = null;
@@ -6251,7 +6237,7 @@
lastResumed = lastFocusedRootTask.getResumedActivity();
}
- boolean pausing = taskDisplayArea.pauseBackTasks(next);
+ boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
if (mResumedActivity != null) {
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity);
pausing |= startPausingLocked(false /* uiSleeping */, next,
@@ -6296,13 +6282,8 @@
// If the most recent activity was noHistory but was only stopped rather
// than stopped+finished because the device went to sleep, we need to make
// sure to finish it as we're making a new activity topmost.
- if (shouldSleepActivities() && mLastNoHistoryActivity != null
- && !mLastNoHistoryActivity.finishing
- && mLastNoHistoryActivity != next) {
- ProtoLog.d(WM_DEBUG_STATES, "no-history finish of %s on new resume",
- mLastNoHistoryActivity);
- mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
- mLastNoHistoryActivity = null;
+ if (shouldSleepActivities()) {
+ mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next);
}
if (prev != null && prev != next && next.nowVisible) {
@@ -7257,21 +7238,6 @@
task.setBounds(displayedBounds);
}
- /**
- * Until we can break this "set task bounds to same as root task bounds" behavior, this
- * basically resizes both root task and task bounds to the same bounds.
- */
- private void setTaskBounds(Rect bounds) {
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::setTaskBoundsInner,
- PooledLambda.__(Task.class), bounds);
- forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
- }
-
- private static void setTaskBoundsInner(Task task, Rect bounds) {
- task.setBounds(task.isResizeable() ? bounds : null);
- }
-
boolean willActivityBeVisible(IBinder token) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
@@ -7311,8 +7277,10 @@
isPausingDied = true;
}
if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
+ if (mLastPausedActivity.isNoHistory()) {
+ mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
+ }
mLastPausedActivity = null;
- mLastNoHistoryActivity = null;
}
return isPausingDied;
}
@@ -7348,8 +7316,6 @@
if (dumpAll) {
printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
" mLastPausedActivity: ", null);
- printed |= printThisActivity(pw, mLastNoHistoryActivity, dumpPackage,
- false, " mLastNoHistoryActivity: ", null);
}
printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter);
@@ -7531,51 +7497,6 @@
}
}
- void positionChildAt(Task task, int position) {
- if (task.getRootTask() != this) {
- throw new IllegalArgumentException("AS.positionChildAt: task=" + task
- + " is not a child of root task=" + this + " current parent="
- + task.getRootTask());
- }
-
- task.updateOverrideConfigurationForRootTask(this);
-
- final ActivityRecord topRunningActivity = task.topRunningActivityLocked();
- final boolean wasResumed = topRunningActivity == task.mResumedActivity;
-
- boolean toTop = position >= getChildCount();
- boolean includingParents = toTop || getDisplayArea().getNextFocusableRootTask(this,
- true /* ignoreCurrent */) == null;
- if (WindowManagerDebugConfig.DEBUG_ROOT_TASK) {
- Slog.i(TAG_WM, "positionChildAt: positioning task=" + task + " at " + position);
- }
- positionChildAt(position, task, includingParents);
- getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-
-
- // TODO: Investigate if this random code is really needed.
- if (task.voiceSession != null) {
- try {
- task.voiceSession.taskStarted(task.intent, task.mTaskId);
- } catch (RemoteException e) {
- }
- }
-
- if (wasResumed) {
- if (mResumedActivity != null) {
- Log.wtf(TAG, "mResumedActivity was already set when moving mResumedActivity from"
- + " other root task to this task mResumedActivity=" + mResumedActivity
- + " other mResumedActivity=" + topRunningActivity);
- }
- topRunningActivity.setState(RESUMED, "positionChildAt");
- }
-
- // The task might have already been running and its visibility needs to be synchronized with
- // the visibility of the root task / windows.
- ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
-
public void setAlwaysOnTop(boolean alwaysOnTop) {
// {@link #isAwaysonTop} overrides the original behavior which also evaluates if this
// task is force hidden, so super.isAlwaysOnTop() is used here to see whether the
@@ -7712,22 +7633,28 @@
reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);
}
- void setLastRecentsAnimationTransaction(
- @NonNull PictureInPictureSurfaceTransaction transaction) {
+ void setLastRecentsAnimationTransaction(@NonNull PictureInPictureSurfaceTransaction transaction,
+ @Nullable SurfaceControl overlay) {
mLastRecentsAnimationTransaction = new PictureInPictureSurfaceTransaction(transaction);
+ mLastRecentsAnimationOverlay = overlay;
}
void clearLastRecentsAnimationTransaction() {
mLastRecentsAnimationTransaction = null;
+ mLastRecentsAnimationOverlay = null;
// reset also the transform introduced by mLastRecentsAnimationTransaction
getPendingTransaction().setMatrix(mSurfaceControl, Matrix.IDENTITY_MATRIX, new float[9]);
}
void maybeApplyLastRecentsAnimationTransaction() {
if (mLastRecentsAnimationTransaction != null) {
+ if (mLastRecentsAnimationOverlay != null) {
+ getPendingTransaction().reparent(mLastRecentsAnimationOverlay, mSurfaceControl);
+ }
PictureInPictureSurfaceTransaction.apply(mLastRecentsAnimationTransaction,
mSurfaceControl, getPendingTransaction());
mLastRecentsAnimationTransaction = null;
+ mLastRecentsAnimationOverlay = null;
}
}
@@ -7846,8 +7773,8 @@
// Do not sleep activities in this root task if we're marked as focused and the keyguard
// is in the process of going away.
- if (isFocusedRootTaskOnDisplay()
- && mTaskSupervisor.getKeyguardController().isKeyguardGoingAway()
+ if (mTaskSupervisor.getKeyguardController().isKeyguardGoingAway()
+ && isFocusedRootTaskOnDisplay()
// Avoid resuming activities on secondary displays since we don't want bubble
// activities to be resumed while bubble is still collapsed.
// TODO(b/113840485): Having keyguard going away state for secondary displays.
@@ -7862,14 +7789,6 @@
return shouldSleepActivities() || mAtmService.mShuttingDown;
}
- /** Bounds of the root task without adjusting for other factors in the system like visibility
- * of root docked task.
- * Most callers should be using {@link ConfigurationContainer#getRequestedOverrideBounds} a
- * it takes into consideration other system factors. */
- void getRawBounds(Rect out) {
- out.set(getRawBounds());
- }
-
private Rect getRawBounds() {
return super.getBounds();
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 368e6dd..ae90a7d 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1614,18 +1614,18 @@
boolean supportsPip = mAtmService.mSupportsPictureInPicture;
if (supportsMultiWindow) {
if (task != null) {
- supportsSplitScreen = task.supportsSplitScreenWindowingMode();
- supportsFreeform = task.supportsFreeform();
- supportsMultiWindow = task.supportsMultiWindow()
+ supportsSplitScreen = task.supportsSplitScreenWindowingModeInDisplayArea(this);
+ supportsFreeform = task.supportsFreeformInDisplayArea(this);
+ supportsMultiWindow = task.supportsMultiWindowInDisplayArea(this)
// When the activity needs to be moved to PIP while the Task is not in PIP,
// it can be moved to a new created PIP Task, so WINDOWING_MODE_PINNED is
// always valid for Task as long as the device supports it.
|| (windowingMode == WINDOWING_MODE_PINNED && supportsPip);
} else if (r != null) {
- supportsSplitScreen = r.supportsSplitScreenWindowingMode();
- supportsFreeform = r.supportsFreeform();
+ supportsSplitScreen = r.supportsSplitScreenWindowingModeInDisplayArea(this);
+ supportsFreeform = r.supportsFreeformInDisplayArea(this);
supportsPip = r.supportsPictureInPicture();
- supportsMultiWindow = r.supportsMultiWindow();
+ supportsMultiWindow = r.supportsMultiWindowInDisplayArea(this);
}
}
@@ -2078,14 +2078,15 @@
task.finishAllActivitiesImmediately();
} else {
// Reparent task to corresponding launch root or display area.
- final WindowContainer launchRoot = task.supportsSplitScreenWindowingMode()
- ? toDisplayArea.getLaunchRootTask(
- task.getWindowingMode(),
- task.getActivityType(),
- null /* options */,
- null /* sourceTask */,
- 0 /* launchFlags */)
- : null;
+ final WindowContainer launchRoot =
+ task.supportsSplitScreenWindowingModeInDisplayArea(toDisplayArea)
+ ? toDisplayArea.getLaunchRootTask(
+ task.getWindowingMode(),
+ task.getActivityType(),
+ null /* options */,
+ null /* sourceTask */,
+ 0 /* launchFlags */)
+ : null;
task.reparent(launchRoot == null ? toDisplayArea : launchRoot, POSITION_TOP);
// Set the windowing mode to undefined by default to let the root task inherited the
@@ -2101,7 +2102,8 @@
if (lastReparentedRootTask != null) {
if (toDisplayArea.isSplitScreenModeActivated()
- && !lastReparentedRootTask.supportsSplitScreenWindowingMode()) {
+ && !lastReparentedRootTask.supportsSplitScreenWindowingModeInDisplayArea(
+ toDisplayArea)) {
// Dismiss split screen if the last reparented root task doesn't support split mode.
mAtmService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedRootTask();
@@ -2129,7 +2131,7 @@
}
@Override
- protected boolean isTaskDisplayArea() {
+ boolean isTaskDisplayArea() {
return true;
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 0bc7999..cc0471c 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -612,7 +612,7 @@
private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
TaskDisplayArea displayArea) {
- if (!activity.supportsFreeform() || activity.isResizeable()) {
+ if (!activity.supportsFreeformInDisplayArea(displayArea) || activity.isResizeable()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fb481b41..cc8ee60 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -184,7 +184,7 @@
Rect mainFrame = null;
final boolean playShiftUpAnimation = !task.inMultiWindowMode();
if (prepareAnimation && playShiftUpAnimation) {
- final ActivityRecord topActivity = task.topActivityWithStartingWindow();
+ final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
if (topActivity != null) {
final WindowState mainWindow =
topActivity.findMainWindow(false/* includeStartingApp */);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index d49b6a0..9ffb2b1 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -425,7 +425,7 @@
final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
// Exclude IME window snapshot when IME isn't proper to attach to app.
final boolean excludeIme = imeWindow != null && imeWindow.getSurfaceControl() != null
- && !task.getDisplayContent().isImeAttachedToApp();
+ && !task.getDisplayContent().shouldImeAttachedToApp();
final WindowState navWindow =
task.getDisplayContent().getDisplayPolicy().getNavigationBar();
// If config_attachNavBarToAppDuringTransition is true, the nav bar will be reparent to the
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ab5e498..8bee862 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -106,6 +106,7 @@
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -947,7 +948,7 @@
void updateDevEnableNonResizableMultiWindow() {
ContentResolver resolver = mContext.getContentResolver();
final boolean devEnableNonResizableMultiWindow = Settings.Global.getInt(resolver,
- DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0;
+ DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0;
mAtmService.mDevEnableNonResizableMultiWindow = devEnableNonResizableMultiWindow;
}
@@ -8090,8 +8091,13 @@
// This could prevent if there is no container animation, we still have to apply the
// pending transaction and exit waiting.
mAnimator.mNotifyWhenNoAnimation = true;
- while ((mAnimator.isAnimationScheduled()
- || mRoot.isAnimating(TRANSITION | CHILDREN)) && timeoutRemaining > 0) {
+ WindowContainer animatingContainer = null;
+ while (mAnimator.isAnimationScheduled() || timeoutRemaining > 0) {
+ animatingContainer = mRoot.getAnimatingContainer(TRANSITION | CHILDREN,
+ ANIMATION_TYPE_ALL);
+ if (animatingContainer == null) {
+ break;
+ }
long startTime = System.currentTimeMillis();
try {
mGlobalLock.wait(timeoutRemaining);
@@ -8101,9 +8107,13 @@
}
mAnimator.mNotifyWhenNoAnimation = false;
- if (mAnimator.isAnimationScheduled()
- || mRoot.isAnimating(TRANSITION | CHILDREN)) {
- Slog.w(TAG, "Timed out waiting for animations to complete.");
+ if (mAnimator.isAnimationScheduled() || animatingContainer != null) {
+ Slog.w(TAG, "Timed out waiting for animations to complete,"
+ + " animatingContainer=" + animatingContainer
+ + " animationType=" + SurfaceAnimator.animationTypeToString(
+ animatingContainer != null
+ ? animatingContainer.mSurfaceAnimator.getAnimationType()
+ : SurfaceAnimator.ANIMATION_TYPE_NONE));
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index c29211f..a82a478 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
@@ -37,8 +36,6 @@
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
@@ -53,7 +50,6 @@
import android.window.ITransitionPlayer;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowOrganizerController;
-import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
@@ -302,92 +298,12 @@
}
// Hierarchy changes
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
- if (!hops.isEmpty() && mService.isInLockTaskMode()) {
- Slog.w(TAG, "Attempt to perform hierarchy operations while in lock task mode...");
- } else {
- for (int i = 0, n = hops.size(); i < n; ++i) {
- final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
- switch (hop.getType()) {
- case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
- final WindowContainer wc = WindowContainer.fromBinder(
- hop.getContainer());
- final Task task = wc != null ? wc.asTask() : null;
- if (task != null) {
- task.getDisplayArea().setLaunchRootTask(task,
- hop.getWindowingModes(), hop.getActivityTypes());
- } else {
- throw new IllegalArgumentException(
- "Cannot set non-task as launch root: " + wc);
- }
- break;
- }
- case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: {
- final WindowContainer wc = WindowContainer.fromBinder(
- hop.getContainer());
- final Task task = wc != null ? wc.asTask() : null;
- if (task == null) {
- throw new IllegalArgumentException("Cannot set "
- + "non-task as launch root: " + wc);
- } else if (!task.mCreatedByOrganizer) {
- throw new UnsupportedOperationException("Cannot set "
- + "non-organized task as adjacent flag root: " + wc);
- } else if (task.mAdjacentTask == null) {
- throw new UnsupportedOperationException("Cannot set "
- + "non-adjacent task as adjacent flag root: " + wc);
- }
-
- final boolean clearRoot = hop.getToTop();
- task.getDisplayArea()
- .setLaunchAdjacentFlagRootTask(clearRoot ? null : task);
- break;
- }
- case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
- effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
- break;
- case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
- effects |= setAdjacentRootsHierarchyOp(hop);
- break;
- case HIERARCHY_OP_TYPE_REORDER:
- case HIERARCHY_OP_TYPE_REPARENT:
- final WindowContainer wc = WindowContainer.fromBinder(
- hop.getContainer());
- if (wc == null || !wc.isAttached()) {
- Slog.e(TAG, "Attempt to operate on detached container: " + wc);
- continue;
- }
- if (syncId >= 0) {
- addToSyncSet(syncId, wc);
- }
- if (transition != null) {
- transition.collect(wc);
- if (hop.isReparent()) {
- if (wc.getParent() != null) {
- // Collect the current parent. It's visibility may change as
- // a result of this reparenting.
- transition.collect(wc.getParent());
- }
- if (hop.getNewParent() != null) {
- final WindowContainer parentWc =
- WindowContainer.fromBinder(hop.getNewParent());
- if (parentWc == null) {
- Slog.e(TAG, "Can't resolve parent window from token");
- continue;
- }
- transition.collect(parentWc);
- }
- }
- }
- effects |= sanitizeAndApplyHierarchyOp(wc, hop);
- break;
- case HIERARCHY_OP_TYPE_LAUNCH_TASK:
- Bundle launchOpts = hop.getLaunchOptions();
- int taskId = launchOpts.getInt(
- WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
- launchOpts.remove(
- WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
- mService.startActivityFromRecents(taskId, launchOpts);
- break;
- }
+ final int hopSize = hops.size();
+ if (hopSize > 0) {
+ final boolean isInLockTaskMode = mService.isInLockTaskMode();
+ for (int i = 0; i < hopSize; ++i) {
+ effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
+ isInLockTaskMode);
}
}
// Queue-up bounds-change transactions for tasks which are now organized. Do
@@ -541,6 +457,96 @@
return effects[0];
}
+ private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
+ int syncId, @Nullable Transition transition, boolean isInLockTaskMode) {
+ final int type = hop.getType();
+ switch (type) {
+ case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
+ final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+ final Task task = wc != null ? wc.asTask() : null;
+ if (task != null) {
+ task.getDisplayArea().setLaunchRootTask(task,
+ hop.getWindowingModes(), hop.getActivityTypes());
+ } else {
+ throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc);
+ }
+ break;
+ }
+ case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: {
+ final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+ final Task task = wc != null ? wc.asTask() : null;
+ if (task == null) {
+ throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc);
+ } else if (!task.mCreatedByOrganizer) {
+ throw new UnsupportedOperationException(
+ "Cannot set non-organized task as adjacent flag root: " + wc);
+ } else if (task.mAdjacentTask == null) {
+ throw new UnsupportedOperationException(
+ "Cannot set non-adjacent task as adjacent flag root: " + wc);
+ }
+
+ final boolean clearRoot = hop.getToTop();
+ task.getDisplayArea().setLaunchAdjacentFlagRootTask(clearRoot ? null : task);
+ break;
+ }
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
+ effects |= setAdjacentRootsHierarchyOp(hop);
+ break;
+ }
+ // The following operations may change task order so they are skipped while in lock task
+ // mode. The above operations are still allowed because they don't move tasks. And it may
+ // be necessary such as clearing launch root after entering lock task mode.
+ if (isInLockTaskMode) {
+ Slog.w(TAG, "Skip applying hierarchy operation " + hop + " while in lock task mode");
+ return effects;
+ }
+
+ switch (type) {
+ case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
+ effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
+ break;
+ case HIERARCHY_OP_TYPE_REORDER:
+ case HIERARCHY_OP_TYPE_REPARENT:
+ final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+ if (wc == null || !wc.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on detached container: " + wc);
+ break;
+ }
+ if (syncId >= 0) {
+ addToSyncSet(syncId, wc);
+ }
+ if (transition != null) {
+ transition.collect(wc);
+ if (hop.isReparent()) {
+ if (wc.getParent() != null) {
+ // Collect the current parent. It's visibility may change as
+ // a result of this reparenting.
+ transition.collect(wc.getParent());
+ }
+ if (hop.getNewParent() != null) {
+ final WindowContainer parentWc =
+ WindowContainer.fromBinder(hop.getNewParent());
+ if (parentWc == null) {
+ Slog.e(TAG, "Can't resolve parent window from token");
+ break;
+ }
+ transition.collect(parentWc);
+ }
+ }
+ }
+ effects |= sanitizeAndApplyHierarchyOp(wc, hop);
+ break;
+ case HIERARCHY_OP_TYPE_LAUNCH_TASK:
+ final Bundle launchOpts = hop.getLaunchOptions();
+ final int taskId = launchOpts.getInt(
+ WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
+ launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
+ mService.startActivityFromRecents(taskId, launchOpts);
+ break;
+ }
+ return effects;
+ }
+
private int sanitizeAndApplyHierarchyOp(WindowContainer container,
WindowContainerTransaction.HierarchyOp hop) {
final Task task = container.asTask();
@@ -569,14 +575,15 @@
if (newParent.asTaskDisplayArea() != null) {
// For now, reparenting to displayarea is different from other reparents...
as.reparent(newParent.asTaskDisplayArea(), hop.getToTop());
- } else {
+ } else if (newParent.asTask() != null) {
if (newParent.inMultiWindowMode() && task.isLeafTask()) {
if (newParent.inPinnedWindowingMode()) {
Slog.w(TAG, "Can't support moving a task to another PIP window..."
+ " newParent=" + newParent + " task=" + task);
return 0;
}
- if (!task.supportsMultiWindow()) {
+ if (!task.supportsMultiWindowInDisplayArea(
+ newParent.asTask().getDisplayArea())) {
Slog.w(TAG, "Can't support task that doesn't support multi-window"
+ " mode in multi-window mode... newParent=" + newParent
+ " task=" + task);
@@ -586,6 +593,9 @@
task.reparent((Task) newParent,
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
+ } else {
+ throw new RuntimeException("Can only reparent task to another task or"
+ + " taskDisplayArea, but not " + newParent);
}
} else {
final Task rootTask = (Task) (
@@ -641,6 +651,9 @@
}
final boolean newParentInMultiWindow = newParent.inMultiWindowMode();
+ final TaskDisplayArea newParentTda = newParent.asTask() != null
+ ? newParent.asTask().getDisplayArea()
+ : newParent.asTaskDisplayArea();
final WindowContainer finalCurrentParent = currentParent;
Slog.i(TAG, "reparentChildrenTasksHierarchyOp"
+ " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop);
@@ -656,7 +669,7 @@
// are reparenting from.
return;
}
- if (newParentInMultiWindow && !task.supportsMultiWindow()) {
+ if (newParentInMultiWindow && !task.supportsMultiWindowInDisplayArea(newParentTda)) {
Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task to multi window,"
+ " task=" + task);
return;
@@ -770,44 +783,6 @@
}
@Override
- public boolean takeScreenshot(WindowContainerToken token, SurfaceControl outSurfaceControl) {
- mService.mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeScreenshot()");
- final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
- if (wc == null) {
- throw new RuntimeException("Invalid token in screenshot transaction");
- }
-
- final Rect bounds = new Rect();
- wc.getBounds(bounds);
- bounds.offsetTo(0, 0);
- SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers(
- wc.getSurfaceControl(), bounds, 1);
-
- if (buffer == null || buffer.getHardwareBuffer() == null) {
- return false;
- }
-
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- buffer.getHardwareBuffer());
- SurfaceControl screenshot = mService.mWindowManager.mSurfaceControlFactory.apply(null)
- .setName(wc.getName() + " - Organizer Screenshot")
- .setFormat(PixelFormat.TRANSLUCENT)
- .setParent(wc.getParentSurfaceControl())
- .setSecure(buffer.containsSecureLayers())
- .setCallsite("WindowOrganizerController.takeScreenshot")
- .setBLASTLayer()
- .build();
-
- SurfaceControl.Transaction transaction = mService.mWindowManager.mTransactionFactory.get();
- transaction.setBuffer(screenshot, graphicBuffer);
- transaction.setColorSpace(screenshot, buffer.getColorSpace());
- transaction.apply();
-
- outSurfaceControl.copyFrom(screenshot, "WindowOrganizerController.takeScreenshot");
- return true;
- }
-
- @Override
public void registerTransitionPlayer(ITransitionPlayer player) {
enforceTaskPermission("registerTransitionPlayer()");
final long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index 3e099fb..249880e 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -31,9 +31,7 @@
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.rotationresolver.RotationResolverInternal;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -66,7 +64,6 @@
private static final boolean USE_GRAVITY_SENSOR = false;
private static final int DEFAULT_BATCH_LATENCY = 100000;
- private static final int DEFAULT_ROTATION_RESOLVER_ENABLED = 0; // disabled
private static final String KEY_ROTATION_RESOLVER_TIMEOUT = "rotation_resolver_timeout_millis";
private static final long DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS = 700L;
@@ -278,11 +275,7 @@
* screen rotation.
*/
@VisibleForTesting
- boolean isRotationResolverEnabled() {
- return Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.CAMERA_AUTOROTATE, DEFAULT_ROTATION_RESOLVER_ENABLED,
- UserHandle.USER_CURRENT) == 1;
- }
+ abstract boolean isRotationResolverEnabled();
/**
* Called when the rotation view of the device has changed.
@@ -1076,6 +1069,9 @@
private boolean mRotationEvaluationScheduled;
private long mRotationResolverTimeoutMillis;
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ private int mCurrentCallbackId = 0;
+ private Runnable mCancelRotationResolverRequest;
+
OrientationSensorJudge() {
super();
setupRotationResolverParameters();
@@ -1142,8 +1138,6 @@
RotationResolverInternal.class);
}
- final CancellationSignal cancellationSignal = new CancellationSignal();
-
String packageName = null;
if (mActivityTaskManagerInternal != null) {
final WindowProcessController controller =
@@ -1155,16 +1149,40 @@
}
}
+ mCurrentCallbackId++;
+
+ if (mCancelRotationResolverRequest != null) {
+ getHandler().removeCallbacks(mCancelRotationResolverRequest);
+ }
+ final CancellationSignal cancellationSignal = new CancellationSignal();
+ mCancelRotationResolverRequest = cancellationSignal::cancel;
+ getHandler().postDelayed(
+ mCancelRotationResolverRequest, mRotationResolverTimeoutMillis);
+
mRotationResolverService.resolveRotation(
new RotationResolverInternal.RotationResolverCallbackInternal() {
+ private final int mCallbackId = mCurrentCallbackId;
@Override
public void onSuccess(int result) {
- finalizeRotation(result);
+ finalizeRotationIfFresh(result);
}
@Override
public void onFailure(int error) {
- finalizeRotation(reportedRotation);
+ finalizeRotationIfFresh(reportedRotation);
+ }
+
+ private void finalizeRotationIfFresh(int rotation) {
+ // Ignore the callback if it's not the latest.
+ if (mCallbackId == mCurrentCallbackId) {
+ getHandler().removeCallbacks(mCancelRotationResolverRequest);
+ finalizeRotation(rotation);
+ } else {
+ Slog.d(TAG, String.format(
+ "An outdated callback received [%s vs. %s]. Ignoring "
+ + "it.",
+ mCallbackId, mCurrentCallbackId));
+ }
}
},
packageName,
@@ -1172,8 +1190,6 @@
mCurrentRotation,
mRotationResolverTimeoutMillis,
cancellationSignal);
- getHandler().postDelayed(cancellationSignal::cancel,
- mRotationResolverTimeoutMillis);
} else {
finalizeRotation(reportedRotation);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 540035f..b93312a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -751,11 +751,11 @@
int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
/**
- * This is the frame rate which is passed to SurfaceFlinger if the window is part of the
- * high refresh rate deny list. The variable is cached, so we do not send too many updates to
- * SF.
+ * This is the frame rate which is passed to SurfaceFlinger if the window set a
+ * preferredDisplayModeId or is part of the high refresh rate deny list.
+ * The variable is cached, so we do not send too many updates to SF.
*/
- float mDenyListFrameRate = 0f;
+ float mAppPreferredFrameRate = 0f;
static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
@@ -2309,7 +2309,7 @@
// When the window configuration changed, we need to update the IME control target in
// case the app may lose the IME inets control when exiting from split-screen mode, or the
// IME parent may failed to attach to the app during rotating the screen.
- // See DisplayContent#isImeAttachedToApp, DisplayContent#isImeControlledByApp
+ // See DisplayContent#shouldImeAttachedToApp, DisplayContent#isImeControlledByApp
if (windowConfigChanged) {
getDisplayContent().updateImeControlTarget();
}
@@ -2409,18 +2409,22 @@
final boolean startingWindow = mAttrs.type == TYPE_APPLICATION_STARTING;
if (startingWindow) {
ProtoLog.d(WM_DEBUG_STARTING_WINDOW, "Starting window removed %s", this);
- }
-
- if (startingWindow && StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
- // cancel the remove starting window animation on shell
+ // Cancel the remove starting window animation on shell. The main window might changed
+ // during animating, checking for all windows would be safer.
if (mActivityRecord != null) {
- final WindowState mainWindow =
- mActivityRecord.findMainWindow(false/* includeStartingApp */);
- if (mainWindow != null && mainWindow.isSelfAnimating(0 /* flags */,
- ANIMATION_TYPE_STARTING_REVEAL)) {
- mainWindow.cancelAnimation();
- }
+ mActivityRecord.forAllWindowsUnchecked(w -> {
+ if (w.isSelfAnimating(0, ANIMATION_TYPE_STARTING_REVEAL)) {
+ w.cancelAnimation();
+ return true;
+ }
+ return false;
+ }, true);
}
+ } else if (mAttrs.type == TYPE_BASE_APPLICATION
+ && isSelfAnimating(0, ANIMATION_TYPE_STARTING_REVEAL)) {
+ // Cancel the remove starting window animation in case the binder dead before remove
+ // splash window.
+ cancelAnimation();
}
ProtoLog.v(WM_DEBUG_FOCUS, "Remove client=%x, surfaceController=%s Callers=%s",
@@ -2428,7 +2432,6 @@
mWinAnimator.mSurfaceController,
Debug.getCallers(5));
-
final long origId = Binder.clearCallingIdentity();
try {
@@ -2682,6 +2685,13 @@
}
}
+ /**
+ * Move the touch gesture from the currently touched window on this display to this window.
+ */
+ public boolean transferTouch() {
+ return mWmService.mInputManager.transferTouch(mInputChannelToken);
+ }
+
void disposeInputChannel() {
if (mDeadWindowEventReceiver != null) {
mDeadWindowEventReceiver.dispose();
@@ -5409,10 +5419,11 @@
}
final float refreshRate = refreshRatePolicy.getPreferredRefreshRate(this);
- if (mDenyListFrameRate != refreshRate) {
- mDenyListFrameRate = refreshRate;
+ if (mAppPreferredFrameRate != refreshRate) {
+ mAppPreferredFrameRate = refreshRate;
getPendingTransaction().setFrameRate(
- mSurfaceControl, mDenyListFrameRate, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
+ mSurfaceControl, mAppPreferredFrameRate,
+ Surface.FRAME_RATE_COMPATIBILITY_EXACT, Surface.CHANGE_FRAME_RATE_ALWAYS);
}
}
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 6721ecf..bfd8005 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -66,7 +66,7 @@
android::sp<IStats> statsHal = new StatsHal();
const android::status_t err = statsHal->registerAsService();
- LOG_ALWAYS_FATAL_IF(err != android::OK, "Cannot register HIDL %s: %d", IStats::descriptor, err);
+ ALOGW_IF(err != android::OK, "Cannot register HIDL %s: %d", IStats::descriptor, err);
}
} // namespace
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index cca62b9..eb9c8db 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1834,8 +1834,19 @@
}
}
-static void nativeSetPointerSpeed(JNIEnv* /* env */,
- jclass /* clazz */, jlong ptr, jint speed) {
+static jboolean nativeTransferTouch(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jobject destChannelTokenObj) {
+ sp<IBinder> destChannelToken = ibinderForJavaObject(env, destChannelTokenObj);
+
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ if (im->getInputManager()->getDispatcher()->transferTouch(destChannelToken)) {
+ return JNI_TRUE;
+ } else {
+ return JNI_FALSE;
+ }
+}
+
+static void nativeSetPointerSpeed(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint speed) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
im->setPointerSpeed(speed);
@@ -2308,6 +2319,7 @@
{"nativeSetSystemUiLightsOut", "(JZ)V", (void*)nativeSetSystemUiLightsOut},
{"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;Z)Z",
(void*)nativeTransferTouchFocus},
+ {"nativeTransferTouch", "(JLandroid/os/IBinder;)Z", (void*)nativeTransferTouch},
{"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed},
{"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches},
{"nativeSetInteractive", "(JZ)V", (void*)nativeSetInteractive},
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index db52683..f439777 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -293,8 +293,8 @@
auto mode = read<int8_t>(metadata).value_or(STDIN);
if (mode == LOCAL_FILE) {
// local file and possibly signature
- return openLocalFile(env, jni, shellCommand, size,
- std::string(metadata.data, metadata.size));
+ auto dataSize = le32toh(read<int32_t>(metadata).value_or(0));
+ return openLocalFile(env, jni, shellCommand, size, std::string(metadata.data, dataSize));
}
if (!shellCommand) {
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index 646aabd..dd9a525 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -54,6 +54,7 @@
jmethodID method_reportMeasurementData;
jmethodID method_satellitePvtBuilderBuild;
jmethodID method_satellitePvtBuilderCtor;
+jmethodID method_satellitePvtBuilderSetFlags;
jmethodID method_satellitePvtBuilderSetPositionEcef;
jmethodID method_satellitePvtBuilderSetVelocityEcef;
jmethodID method_satellitePvtBuilderSetClockInfo;
@@ -89,6 +90,9 @@
jclass satellitePvtBuilder = env->FindClass("android/location/SatellitePvt$Builder");
class_satellitePvtBuilder = (jclass)env->NewGlobalRef(satellitePvtBuilder);
method_satellitePvtBuilderCtor = env->GetMethodID(class_satellitePvtBuilder, "<init>", "()V");
+ method_satellitePvtBuilderSetFlags =
+ env->GetMethodID(class_satellitePvtBuilder, "setFlags",
+ "(I)Landroid/location/SatellitePvt$Builder;");
method_satellitePvtBuilderSetPositionEcef =
env->GetMethodID(class_satellitePvtBuilder, "setPositionEcef",
"(Landroid/location/SatellitePvt$PositionEcef;)"
@@ -313,22 +317,33 @@
if (measurement.flags & static_cast<uint32_t>(GnssMeasurement::HAS_SATELLITE_PVT)) {
const SatellitePvt& satellitePvt = measurement.satellitePvt;
- jobject positionEcef = env->NewObject(class_positionEcef, method_positionEcef,
- satellitePvt.satPosEcef.posXMeters,
- satellitePvt.satPosEcef.posYMeters,
- satellitePvt.satPosEcef.posZMeters,
- satellitePvt.satPosEcef.ureMeters);
- jobject velocityEcef =
- env->NewObject(class_velocityEcef, method_velocityEcef,
- satellitePvt.satVelEcef.velXMps, satellitePvt.satVelEcef.velYMps,
- satellitePvt.satVelEcef.velZMps, satellitePvt.satVelEcef.ureRateMps);
- jobject clockInfo = env->NewObject(class_clockInfo, method_clockInfo,
- satellitePvt.satClockInfo.satHardwareCodeBiasMeters,
- satellitePvt.satClockInfo.satTimeCorrectionMeters,
- satellitePvt.satClockInfo.satClkDriftMps);
+ uint16_t satFlags = static_cast<uint16_t>(satellitePvt.flags);
+ jobject positionEcef = nullptr;
+ jobject velocityEcef = nullptr;
+ jobject clockInfo = nullptr;
+
+ if (satFlags & SatellitePvt::HAS_POSITION_VELOCITY_CLOCK_INFO) {
+ positionEcef = env->NewObject(class_positionEcef, method_positionEcef,
+ satellitePvt.satPosEcef.posXMeters,
+ satellitePvt.satPosEcef.posYMeters,
+ satellitePvt.satPosEcef.posZMeters,
+ satellitePvt.satPosEcef.ureMeters);
+ velocityEcef =
+ env->NewObject(class_velocityEcef, method_velocityEcef,
+ satellitePvt.satVelEcef.velXMps, satellitePvt.satVelEcef.velYMps,
+ satellitePvt.satVelEcef.velZMps,
+ satellitePvt.satVelEcef.ureRateMps);
+ clockInfo = env->NewObject(class_clockInfo, method_clockInfo,
+ satellitePvt.satClockInfo.satHardwareCodeBiasMeters,
+ satellitePvt.satClockInfo.satTimeCorrectionMeters,
+ satellitePvt.satClockInfo.satClkDriftMps);
+ }
+
jobject satellitePvtBuilderObject =
env->NewObject(class_satellitePvtBuilder, method_satellitePvtBuilderCtor);
+ env->CallObjectMethod(satellitePvtBuilderObject, method_satellitePvtBuilderSetFlags,
+ satellitePvt.flags);
env->CallObjectMethod(satellitePvtBuilderObject, method_satellitePvtBuilderSetPositionEcef,
positionEcef);
env->CallObjectMethod(satellitePvtBuilderObject, method_satellitePvtBuilderSetVelocityEcef,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ef7360d..23a67b7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -28,6 +28,7 @@
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
+import static android.app.admin.DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED;
import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED;
@@ -883,12 +884,6 @@
synchronized (getLockObject()) {
// Check whether the user is affiliated, *before* removing its data.
boolean isRemovedUserAffiliated = isUserAffiliatedWithDeviceLocked(userHandle);
- if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
- // Disable network and security logging
- mInjector.securityLogSetLoggingEnabledProperty(false);
- mSecurityLogMonitor.stop();
- setNetworkLoggingActiveInternal(false);
- }
removeUserData(userHandle);
if (!isRemovedUserAffiliated) {
// We discard the logs when unaffiliated users are deleted (so that the
@@ -1779,6 +1774,7 @@
}
void removeUserData(int userHandle) {
+ final boolean isOrgOwned;
synchronized (getLockObject()) {
if (userHandle == UserHandle.USER_SYSTEM) {
Slogf.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring.");
@@ -1786,6 +1782,9 @@
}
updatePasswordQualityCacheForUserGroup(userHandle);
mPolicyCache.onUserRemoved(userHandle);
+
+ isOrgOwned = mOwners.isProfileOwnerOfOrganizationOwnedDevice(userHandle);
+
mOwners.removeProfileOwner(userHandle);
mOwners.writeProfileOwner(userHandle);
@@ -1799,6 +1798,14 @@
policyFile.delete();
Slogf.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath());
}
+ if (isOrgOwned) {
+ final UserInfo primaryUser = mUserManager.getPrimaryUser();
+ if (primaryUser != null) {
+ clearOrgOwnedProfileOwnerDeviceWidePolicies(primaryUser.id);
+ } else {
+ Slogf.wtf(LOG_TAG, "Was unable to get primary user.");
+ }
+ }
}
/**
@@ -3537,6 +3544,7 @@
"Caller must be shell or hold MANAGE_PROFILE_AND_DEVICE_OWNERS to call "
+ "forceRemoveActiveAdmin");
mInjector.binderWithCleanCallingIdentity(() -> {
+ boolean isOrgOwnedProfile = false;
synchronized (getLockObject()) {
if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) {
throw new SecurityException("Attempt to remove non-test admin "
@@ -3548,13 +3556,7 @@
clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
}
if (isProfileOwner(adminReceiver, userHandle)) {
- if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
- UserHandle parentUserHandle = UserHandle.of(getProfileParentId(userHandle));
- mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
- false, parentUserHandle);
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER,
- false, parentUserHandle);
- }
+ isOrgOwnedProfile = isProfileOwnerOfOrganizationOwnedDevice(userHandle);
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver,
userHandle, /* parent */ false);
clearProfileOwnerLocked(admin, userHandle);
@@ -3562,11 +3564,26 @@
}
// Remove the admin skipping sending the broadcast.
removeAdminArtifacts(adminReceiver, userHandle);
+
+ // In case of PO on org owned device, clean device-wide policies and restrictions.
+ if (isOrgOwnedProfile) {
+ final UserHandle parentUser = UserHandle.of(getProfileParentId(userHandle));
+ clearOrgOwnedProfileOwnerUserRestrictions(parentUser);
+ clearOrgOwnedProfileOwnerDeviceWidePolicies(parentUser.getIdentifier());
+ }
+
Slogf.i(LOG_TAG, "Admin " + adminReceiver + " removed from user " + userHandle);
});
}
- private void clearDeviceOwnerUserRestrictionLocked(UserHandle userHandle) {
+ private void clearOrgOwnedProfileOwnerUserRestrictions(UserHandle parentUserHandle) {
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false, parentUserHandle);
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_ADD_USER, false, parentUserHandle);
+ }
+
+ private void clearDeviceOwnerUserRestriction(UserHandle userHandle) {
// ManagedProvisioning/DPC sets DISALLOW_ADD_USER. Clear to recover to the original state
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, userHandle)) {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false, userHandle);
@@ -6710,25 +6727,16 @@
// when wipeData is _not_ called on the parent instance, it implies relinquishing
// control over the device, wiping only the work profile. So the user restriction
// on profile removal needs to be removed first.
-
- mInjector.binderWithCleanCallingIdentity(() -> {
- // Clear restriction as user.
- mUserManager.setUserRestriction(
- UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false,
- UserHandle.SYSTEM);
- mUserManager.setUserRestriction(
- UserManager.DISALLOW_ADD_USER, false, UserHandle.SYSTEM);
-
- // Device-wide policies set by the profile owner need to be cleaned up here.
- mLockPatternUtils.setDeviceOwnerInfo(null);
- });
+ final UserHandle parentUser = UserHandle.of(getProfileParentId(userId));
+ mInjector.binderWithCleanCallingIdentity(
+ () -> clearOrgOwnedProfileOwnerUserRestrictions(parentUser));
}
}
DevicePolicyEventLogger event = DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON)
.setInt(flags)
- .setStrings(calledOnParentInstance ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
- ;
+ .setStrings(calledOnParentInstance ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT);
+
final String adminName;
final ComponentName adminComp;
if (admin != null) {
@@ -6755,6 +6763,55 @@
wipeDataNoLock(adminComp, flags, internalReason, wipeReasonForUser, userId);
}
+ /**
+ * Clears device wide policies enforced by COPE PO when relinquishing the device. This method
+ * should be invoked once the admin is gone, so that all methods that rely on calculating
+ * aggregate policy (e.g. strong auth timeout) from all admins aren't affected by its policies.
+ * This method assumes that there is no other device or profile owners left on the device.
+ * Shouldn't be called from binder thread without clearing identity.
+ */
+ private void clearOrgOwnedProfileOwnerDeviceWidePolicies(@UserIdInt int parentId) {
+ Slogf.i(LOG_TAG, "Cleaning up device-wide policies left over from org-owned profile...");
+ // Lockscreen message
+ mLockPatternUtils.setDeviceOwnerInfo(null);
+ // Wifi config lockdown
+ mInjector.settingsGlobalPutInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0);
+ // Security logging
+ if (mInjector.securityLogGetLoggingEnabledProperty()) {
+ mSecurityLogMonitor.stop();
+ mInjector.securityLogSetLoggingEnabledProperty(false);
+ }
+ // Network logging
+ setNetworkLoggingActiveInternal(false);
+
+ // System update policy.
+ final boolean hasSystemUpdatePolicy;
+ synchronized (getLockObject()) {
+ hasSystemUpdatePolicy = mOwners.getSystemUpdatePolicy() != null;
+ if (hasSystemUpdatePolicy) {
+ mOwners.clearSystemUpdatePolicy();
+ mOwners.writeDeviceOwner();
+ }
+ }
+ if (hasSystemUpdatePolicy) {
+ mContext.sendBroadcastAsUser(
+ new Intent(ACTION_SYSTEM_UPDATE_POLICY_CHANGED), UserHandle.SYSTEM);
+ }
+
+ // Unsuspend personal apps if needed.
+ suspendPersonalAppsInternal(parentId, false);
+
+ // Notify FRP agent, LSS and WindowManager to ensure they don't hold on to stale policies.
+ final int frpAgentUid = getFrpManagementAgentUid();
+ if (frpAgentUid > 0) {
+ notifyResetProtectionPolicyChanged(frpAgentUid);
+ }
+ mLockSettingsInternal.refreshStrongAuthTimeout(parentId);
+ updateScreenCaptureDisabled(parentId, getScreenCaptureDisabled(null, parentId, false));
+
+ Slogf.i(LOG_TAG, "Cleaning up device-wide policies done.");
+ }
+
private void wipeDataNoLock(ComponentName admin, int flags, String internalReason,
String wipeReasonForUser, int userId) {
wtfIfInLock();
@@ -6822,13 +6879,8 @@
saveSettingsLocked(caller.getUserId());
}
- final Intent intent = new Intent(
- DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED).addFlags(
- Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND);
-
- mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(intent,
- UserHandle.getUserHandleForUid(frpManagementAgentUid),
- android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION));
+ mInjector.binderWithCleanCallingIdentity(
+ () -> notifyResetProtectionPolicyChanged(frpManagementAgentUid));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_FACTORY_RESET_PROTECTION)
@@ -6836,6 +6888,16 @@
.write();
}
+ // Shouldn't be called from binder thread without clearing identity.
+ private void notifyResetProtectionPolicyChanged(int frpManagementAgentUid) {
+ final Intent intent = new Intent(
+ DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED).addFlags(
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent,
+ UserHandle.getUserHandleForUid(frpManagementAgentUid),
+ permission.MANAGE_FACTORY_RESET_PROTECTION);
+ }
+
@Override
public FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(
@Nullable ComponentName who) {
@@ -8506,7 +8568,7 @@
mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
- clearDeviceOwnerUserRestrictionLocked(UserHandle.of(userId));
+ clearDeviceOwnerUserRestriction(UserHandle.of(userId));
mInjector.securityLogSetLoggingEnabledProperty(false);
mSecurityLogMonitor.stop();
setNetworkLoggingActiveInternal(false);
@@ -12936,8 +12998,7 @@
mOwners.writeDeviceOwner();
}
mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(
- new Intent(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED),
- UserHandle.SYSTEM));
+ new Intent(ACTION_SYSTEM_UPDATE_POLICY_CHANGED), UserHandle.SYSTEM));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_SYSTEM_UPDATE_POLICY)
.setAdmin(who)
@@ -14513,14 +14574,10 @@
* apps.
*/
@Override
- public void forceUpdateUserSetupComplete() {
- final CallerIdentity caller = getCallerIdentity();
+ public void forceUpdateUserSetupComplete(@UserIdInt int userId) {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
- Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(),
- "Caller has to be in user 0");
- final int userId = UserHandle.USER_SYSTEM;
boolean isUserCompleted = mInjector.settingsSecureGetIntForUser(
Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0;
DevicePolicyData policy = getUserData(userId);
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 51b270c..8b816d0 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -983,7 +983,8 @@
bool enableReadTimeouts = ifs.readTimeoutsRequested();
std::lock_guard l(mMountOperationLock);
- auto status = mVold->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
+ auto status = mVold->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts,
+ ifs.metricsKey);
if (status.isOk()) {
// Store states.
ifs.setReadLogsEnabled(enableReadLogs);
@@ -1173,7 +1174,8 @@
return -EINVAL;
}
if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
- LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
+ LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile [" << normPath
+ << "]: " << err;
return err;
}
if (params.size > 0) {
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 68a28b2..ce3d514 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -56,8 +56,10 @@
}
binder::Status setIncFsMountOptions(
const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
- bool enableReadLogs, bool enableReadTimeouts) const final {
- return mInterface->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
+ bool enableReadLogs, bool enableReadTimeouts,
+ const std::string& sysfsName) const final {
+ return mInterface->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts,
+ sysfsName);
}
private:
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index c0ef7ba..39e2ee3 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -58,7 +58,7 @@
const std::string& targetDir) const = 0;
virtual binder::Status setIncFsMountOptions(
const os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs,
- bool enableReadTimeouts) const = 0;
+ bool enableReadTimeouts, const std::string& sysfsName) const = 0;
};
class DataLoaderManagerWrapper {
diff --git a/services/incremental/path.cpp b/services/incremental/path.cpp
index bf4e9616..73e00ae 100644
--- a/services/incremental/path.cpp
+++ b/services/incremental/path.cpp
@@ -171,7 +171,9 @@
}
details::CStrWrapper::CStrWrapper(std::string_view sv) {
- if (sv[sv.size()] == '\0') {
+ if (!sv.data()) {
+ mCstr = "";
+ } else if (sv[sv.size()] == '\0') {
mCstr = sv.data();
} else {
mCopy.emplace(sv);
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 6c9310b..fae654e 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -56,10 +56,10 @@
MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir));
MOCK_CONST_METHOD2(bindMount,
binder::Status(const std::string& sourceDir, const std::string& argetDir));
- MOCK_CONST_METHOD3(
+ MOCK_CONST_METHOD4(
setIncFsMountOptions,
binder::Status(const ::android::os::incremental::IncrementalFileSystemControlParcel&,
- bool, bool));
+ bool, bool, const std::string&));
void mountIncFsFails() {
ON_CALL(*this, mountIncFs(_, _, _, _, _))
@@ -83,12 +83,12 @@
ON_CALL(*this, bindMount(_, _)).WillByDefault(Return(binder::Status::ok()));
}
void setIncFsMountOptionsFails() const {
- ON_CALL(*this, setIncFsMountOptions(_, _, _))
+ ON_CALL(*this, setIncFsMountOptions(_, _, _, _))
.WillByDefault(Return(
binder::Status::fromExceptionCode(1, String8("failed to set options"))));
}
void setIncFsMountOptionsSuccess() {
- ON_CALL(*this, setIncFsMountOptions(_, _, _))
+ ON_CALL(*this, setIncFsMountOptions(_, _, _, _))
.WillByDefault(Invoke(this, &MockVoldService::setIncFsMountOptionsOk));
}
binder::Status getInvalidControlParcel(const std::string& imagePath,
@@ -108,7 +108,7 @@
}
binder::Status setIncFsMountOptionsOk(
const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
- bool enableReadLogs, bool enableReadTimeouts) {
+ bool enableReadLogs, bool enableReadTimeouts, const std::string& sysfsName) {
mReadLogsEnabled = enableReadLogs;
mReadTimeoutsEnabled = enableReadTimeouts;
return binder::Status::ok();
@@ -1451,9 +1451,9 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// on startLoading
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
// We are calling setIncFsMountOptions(true).
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1475,8 +1475,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(2);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1503,8 +1503,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(2);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(2);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1544,8 +1544,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(3);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(3);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1585,8 +1585,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(5);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(3);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(5);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(3);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1660,9 +1660,9 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// We are calling setIncFsMountOptions(true).
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
// setIncFsMountOptions(false) is called on the callback.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(2);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// After callback is called, disable read logs and remove callback.
@@ -1685,8 +1685,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// checkPermission fails, no calls to set opitions, start or stop WatchingMode.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(0);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
@@ -1705,8 +1705,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// checkPermission fails, no calls to set opitions, start or stop WatchingMode.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(0);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
@@ -1726,8 +1726,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// We are calling setIncFsMountOptions.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
// setIncFsMountOptions fails, no calls to start or stop WatchingMode.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1e4b248..7e718e5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1793,7 +1793,7 @@
t.traceEnd();
t.traceBegin("StartFontManagerService");
- mSystemServiceManager.startService(FontManagerService.Lifecycle.class);
+ mSystemServiceManager.startService(new FontManagerService.Lifecycle(context, safeMode));
t.traceEnd();
t.traceBegin("StartTextServicesManager");
diff --git a/services/net/TEST_MAPPING b/services/net/TEST_MAPPING
index 7025dd1..7eca1f9 100644
--- a/services/net/TEST_MAPPING
+++ b/services/net/TEST_MAPPING
@@ -2,6 +2,9 @@
"imports": [
{
"path": "frameworks/base/core/java/android/net"
+ },
+ {
+ "path": "packages/modules/Wifi/framework"
}
]
-}
\ No newline at end of file
+}
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 5222511..d5e1cd6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -175,6 +175,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.LongConsumer;
@@ -2269,17 +2270,46 @@
() -> CompatChanges.isChangeEnabled(
eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS),
anyString(), any(UserHandle.class)));
- final long minWindow = 73;
+ final int minWindow = 73;
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
+ final Random random = new Random(42);
+
// 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
for (int window = 1; window <= minWindow; window++) {
final PendingIntent pi = getNewMockPendingIntent();
+ final long futurity = random.nextInt(minWindow);
+
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ TEST_CALLING_UID, null);
+
+ final long minAllowed = (long) (futurity * 0.75); // This will always be <= minWindow.
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(Math.max(minAllowed, window), a.windowLength);
+ }
+
+ for (int window = 1; window <= minWindow; window++) {
+ final PendingIntent pi = getNewMockPendingIntent();
+ final long futurity = 2 * minWindow + window; // implies (0.75 * futurity) > minWindow
+
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ TEST_CALLING_UID, null);
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(minWindow, a.windowLength);
+ }
+
+ for (int i = 0; i < 20; i++) {
+ final long window = minWindow + random.nextInt(100);
+ final PendingIntent pi = getNewMockPendingIntent();
setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null);
assertEquals(1, mService.mAlarmStore.size());
final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
- assertEquals(minWindow, a.windowLength);
+ assertEquals(window, a.windowLength);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 92e4ec9..00246dd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -61,8 +61,6 @@
import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManagerInternal;
-import android.location.LocationManagerInternal.LocationTagInfo;
-import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
@@ -134,6 +132,8 @@
private Random mRandom;
@Mock
+ private LocationProviderManager.StateChangedListener mStateChangedListener;
+ @Mock
private LocationManagerInternal mInternal;
@Mock
private Context mContext;
@@ -167,14 +167,14 @@
mInjector.getUserInfoHelper().startUser(OTHER_USER);
mPassive = new PassiveLocationProviderManager(mContext, mInjector);
- mPassive.startManager();
+ mPassive.startManager(null);
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY);
mProvider.setProviderAllowed(true);
mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
- mManager.startManager();
+ mManager.startManager(mStateChangedListener);
mManager.setRealProvider(mProvider);
}
@@ -219,18 +219,18 @@
}
@Test
- public void testAttributionTags() {
- OnProviderLocationTagsChangeListener listener = mock(
- OnProviderLocationTagsChangeListener.class);
- mManager.setOnProviderLocationTagsChangeListener(listener);
-
+ public void testStateChangedListener() {
mProvider.setExtraAttributionTags(Collections.singleton("extra"));
- ArgumentCaptor<LocationTagInfo> captor = ArgumentCaptor.forClass(LocationTagInfo.class);
- verify(listener, times(2)).onLocationTagsChanged(captor.capture());
+ ArgumentCaptor<AbstractLocationProvider.State> captorOld = ArgumentCaptor.forClass(
+ AbstractLocationProvider.State.class);
+ ArgumentCaptor<AbstractLocationProvider.State> captorNew = ArgumentCaptor.forClass(
+ AbstractLocationProvider.State.class);
+ verify(mStateChangedListener, timeout(TIMEOUT_MS).times(2)).onStateChanged(eq(NAME),
+ captorOld.capture(), captorNew.capture());
- assertThat(captor.getAllValues().get(0).getTags()).isEmpty();
- assertThat(captor.getAllValues().get(1).getTags()).containsExactly("extra", "attribution");
+ assertThat(captorOld.getAllValues().get(1).extraAttributionTags).isEmpty();
+ assertThat(captorNew.getAllValues().get(1).extraAttributionTags).containsExactly("extra");
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index b367203..0dcc069 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -24,6 +24,7 @@
import static org.testng.Assert.expectThrows;
+import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.SearchResult;
@@ -39,16 +40,19 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import com.android.server.appsearch.proto.DocumentProto;
import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
import com.android.server.appsearch.proto.PersistType;
import com.android.server.appsearch.proto.PropertyConfigProto;
import com.android.server.appsearch.proto.PropertyProto;
+import com.android.server.appsearch.proto.PutResultProto;
import com.android.server.appsearch.proto.SchemaProto;
import com.android.server.appsearch.proto.SchemaTypeConfigProto;
import com.android.server.appsearch.proto.SearchResultProto;
import com.android.server.appsearch.proto.SearchSpecProto;
+import com.android.server.appsearch.proto.StatusProto;
import com.android.server.appsearch.proto.StringIndexingConfig;
import com.android.server.appsearch.proto.TermMatchType;
@@ -86,8 +90,6 @@
/*logger=*/ null);
}
- // TODO(b/175430168) add test to verify reset is working properly.
-
/**
* Ensure that we can rewrite an incoming schema type by adding the database as a prefix. While
* also keeping any other existing schema types that may already be part of Icing's persisted
@@ -486,6 +488,142 @@
}
@Test
+ public void testReset() throws Exception {
+ // Setup the index
+ Context context = ApplicationProvider.getApplicationContext();
+ File appsearchDir = mTemporaryFolder.newFolder();
+ AppSearchImpl appSearchImpl =
+ AppSearchImpl.create(
+ appsearchDir,
+ context,
+ VisibilityStore.NO_OP_USER_ID,
+ /*globalQuerierPackage=*/ "",
+ /*logger=*/ null);
+
+ // Insert schema
+ List<AppSearchSchema> schemas =
+ ImmutableList.of(
+ new AppSearchSchema.Builder("Type1").build(),
+ new AppSearchSchema.Builder("Type2").build());
+ appSearchImpl.setSchema(
+ context.getPackageName(),
+ "database1",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert a valid doc
+ GenericDocument validDoc =
+ new GenericDocument.Builder<>("namespace1", "id1", "Type1").build();
+ appSearchImpl.putDocument(
+ context.getPackageName(), "database1", validDoc, /*logger=*/ null);
+
+ // Query it via global query. We use the same code again later so this is to make sure we
+ // have our global query configured right.
+ SearchResultPage results =
+ appSearchImpl.globalQuery(
+ /*queryExpression=*/ "",
+ new SearchSpec.Builder().addFilterSchemas("Type1").build(),
+ context.getPackageName(),
+ VisibilityStore.NO_OP_USER_ID,
+ /*logger=*/ null);
+ assertThat(results.getResults()).hasSize(1);
+ assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc);
+
+ // Create a doc with a malformed namespace
+ DocumentProto invalidDoc =
+ DocumentProto.newBuilder()
+ .setNamespace("invalidNamespace")
+ .setUri("id2")
+ .setSchema(context.getPackageName() + "$database1/Type1")
+ .build();
+ AppSearchException e =
+ expectThrows(
+ AppSearchException.class,
+ () -> PrefixUtil.getPrefix(invalidDoc.getNamespace()));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ "The prefixed value \"invalidNamespace\" doesn't contain a valid database"
+ + " name");
+
+ // Insert the invalid doc with an invalid namespace right into icing
+ PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc);
+ assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK);
+
+ // Create a logger for capturing initialization to make sure we are logging the recovery
+ // process correctly.
+ AppSearchLoggerTest.TestLogger testLogger = new AppSearchLoggerTest.TestLogger();
+
+ // Initialize AppSearchImpl. This should cause a reset.
+ appSearchImpl.close();
+ appSearchImpl =
+ AppSearchImpl.create(
+ appsearchDir,
+ context,
+ VisibilityStore.NO_OP_USER_ID,
+ /*globalQuerierPackage=*/ context.getPackageName(),
+ testLogger);
+
+ // Check recovery state
+ InitializeStats initStats = testLogger.mInitializeStats;
+ assertThat(initStats).isNotNull();
+ assertThat(initStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
+ assertThat(initStats.hasDeSync()).isFalse();
+ assertThat(initStats.getDocumentStoreRecoveryCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+ // TODO(b/187879464): There should not be a recovery here, but icing lib reports one if the
+ // doc had no tokens. Once the mentioned bug is fixed, uncomment this.
+ // assertThat(initStats.getIndexRestorationCause())
+ // .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+ assertThat(initStats.getSchemaStoreRecoveryCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+ assertThat(initStats.getDocumentStoreDataStatus())
+ .isEqualTo(InitializeStats.DOCUMENT_STORE_DATA_STATUS_NO_DATA_LOSS);
+ assertThat(initStats.hasReset()).isTrue();
+ assertThat(initStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+
+ // Make sure all our data is gone
+ assertThat(appSearchImpl.getSchema(context.getPackageName(), "database1").getSchemas())
+ .isEmpty();
+ results =
+ appSearchImpl.globalQuery(
+ /*queryExpression=*/ "",
+ new SearchSpec.Builder().addFilterSchemas("Type1").build(),
+ context.getPackageName(),
+ VisibilityStore.NO_OP_USER_ID,
+ /*logger=*/ null);
+ assertThat(results.getResults()).isEmpty();
+
+ // Make sure the index can now be used successfully
+ appSearchImpl.setSchema(
+ context.getPackageName(),
+ "database1",
+ Collections.singletonList(new AppSearchSchema.Builder("Type1").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert a valid doc
+ appSearchImpl.putDocument(
+ context.getPackageName(), "database1", validDoc, /*logger=*/ null);
+
+ // Query it via global query.
+ results =
+ appSearchImpl.globalQuery(
+ /*queryExpression=*/ "",
+ new SearchSpec.Builder().addFilterSchemas("Type1").build(),
+ context.getPackageName(),
+ VisibilityStore.NO_OP_USER_ID,
+ /*logger=*/ null);
+ assertThat(results.getResults()).hasSize(1);
+ assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc);
+ }
+
+ @Test
public void testRewriteSearchSpec_oneInstance() throws Exception {
SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
index 2aad275..b6bb394 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
@@ -68,7 +68,7 @@
}
// Test only not thread safe.
- public class TestLogger implements AppSearchLogger {
+ public static class TestLogger implements AppSearchLogger {
@Nullable CallStats mCallStats;
@Nullable PutDocumentStats mPutDocumentStats;
@Nullable InitializeStats mInitializeStats;
@@ -193,8 +193,7 @@
public void testAppSearchLoggerHelper_testCopyNativeStats_search() {
int nativeLatencyMillis = 4;
int nativeNumTerms = 5;
- // TODO(b/185804196) query length needs to be added in the native stats.
- // int nativeQueryLength = 6;
+ int nativeQueryLength = 6;
int nativeNumNamespacesFiltered = 7;
int nativeNumSchemaTypesFiltered = 8;
int nativeRequestedPageSize = 9;
@@ -211,6 +210,7 @@
QueryStatsProto.newBuilder()
.setLatencyMs(nativeLatencyMillis)
.setNumTerms(nativeNumTerms)
+ .setQueryLength(nativeQueryLength)
.setNumNamespacesFiltered(nativeNumNamespacesFiltered)
.setNumSchemaTypesFiltered(nativeNumSchemaTypesFiltered)
.setRequestedPageSize(nativeRequestedPageSize)
@@ -235,7 +235,7 @@
SearchStats sStats = qBuilder.build();
assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
assertThat(sStats.getTermCount()).isEqualTo(nativeNumTerms);
- // assertThat(sStats.getNativeQueryLength()).isEqualTo(nativeQueryLength);
+ assertThat(sStats.getQueryLength()).isEqualTo(nativeQueryLength);
assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(nativeNumNamespacesFiltered);
assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(nativeNumSchemaTypesFiltered);
assertThat(sStats.getRequestedPageSize()).isEqualTo(nativeRequestedPageSize);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
index 0fe3903..77e0135 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
@@ -110,7 +110,7 @@
.TOKENIZER_TYPE_PLAIN)
.build())
.addProperty(
- new AppSearchSchema.Int64PropertyConfig.Builder("pubDate")
+ new AppSearchSchema.LongPropertyConfig.Builder("pubDate")
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.build())
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
index 8fe1139..d3feb12 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
@@ -75,10 +75,14 @@
.setPropertyName(propertyKeyString)
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setExactMatchPosition(29)
- .setExactMatchBytes(3)
- .setWindowPosition(26)
- .setWindowBytes(6)))
+ .setExactMatchBytePosition(29)
+ .setExactMatchByteLength(3)
+ .setExactMatchUtf16Position(29)
+ .setExactMatchUtf16Length(3)
+ .setWindowBytePosition(26)
+ .setWindowByteLength(6)
+ .setWindowUtf16Position(26)
+ .setWindowUtf16Length(6)))
.build();
SearchResultProto searchResultProto =
SearchResultProto.newBuilder()
@@ -168,19 +172,27 @@
.setPropertyName("senderName")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setExactMatchPosition(0)
- .setExactMatchBytes(4)
- .setWindowPosition(0)
- .setWindowBytes(9)))
+ .setExactMatchBytePosition(0)
+ .setExactMatchByteLength(4)
+ .setExactMatchUtf16Position(0)
+ .setExactMatchUtf16Length(4)
+ .setWindowBytePosition(0)
+ .setWindowByteLength(9)
+ .setWindowUtf16Position(0)
+ .setWindowUtf16Length(9)))
.addEntries(
SnippetProto.EntryProto.newBuilder()
.setPropertyName("senderEmail")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setExactMatchPosition(0)
- .setExactMatchBytes(20)
- .setWindowPosition(0)
- .setWindowBytes(20)))
+ .setExactMatchBytePosition(0)
+ .setExactMatchByteLength(20)
+ .setExactMatchUtf16Position(0)
+ .setExactMatchUtf16Length(20)
+ .setWindowBytePosition(0)
+ .setWindowByteLength(20)
+ .setWindowUtf16Position(0)
+ .setWindowUtf16Length(20)))
.build();
SearchResultProto searchResultProto =
SearchResultProto.newBuilder()
@@ -251,19 +263,27 @@
.setPropertyName("sender.name")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setExactMatchPosition(0)
- .setExactMatchBytes(4)
- .setWindowPosition(0)
- .setWindowBytes(9)))
+ .setExactMatchBytePosition(0)
+ .setExactMatchByteLength(4)
+ .setExactMatchUtf16Position(0)
+ .setExactMatchUtf16Length(4)
+ .setWindowBytePosition(0)
+ .setWindowByteLength(9)
+ .setWindowUtf16Position(0)
+ .setWindowUtf16Length(9)))
.addEntries(
SnippetProto.EntryProto.newBuilder()
.setPropertyName("sender.email[1]")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setExactMatchPosition(0)
- .setExactMatchBytes(21)
- .setWindowPosition(0)
- .setWindowBytes(21)))
+ .setExactMatchBytePosition(0)
+ .setExactMatchByteLength(21)
+ .setExactMatchUtf16Position(0)
+ .setExactMatchUtf16Length(21)
+ .setWindowBytePosition(0)
+ .setWindowByteLength(21)
+ .setWindowUtf16Position(0)
+ .setWindowUtf16Length(21)))
.build();
SearchResultProto searchResultProto =
SearchResultProto.newBuilder()
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
index 2d03981..a71e532 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
@@ -168,7 +168,9 @@
.setSchemaStoreRecoveryLatencyMillis(nativeSchemaStoreRecoveryLatencyMillis)
.setDocumentStoreDataStatus(nativeDocumentStoreDataStatus)
.setDocumentCount(nativeNumDocuments)
- .setSchemaTypeCount(nativeNumSchemaTypes);
+ .setSchemaTypeCount(nativeNumSchemaTypes)
+ .setHasReset(true)
+ .setResetStatusCode(AppSearchResult.RESULT_INVALID_SCHEMA);
final InitializeStats iStats = iStatsBuilder.build();
assertThat(iStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
@@ -192,6 +194,8 @@
assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus);
assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments);
assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes);
+ assertThat(iStats.hasReset()).isTrue();
+ assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_INVALID_SCHEMA);
}
@Test
@@ -297,4 +301,28 @@
assertThat(sStats.getBackwardsIncompatibleTypeChangeCount())
.isEqualTo(backwardsIncompatibleTypeChangeCount);
}
+
+ @Test
+ public void testAppSearchStats_RemoveStats() {
+ int nativeLatencyMillis = 1;
+ @RemoveStats.DeleteType int deleteType = 2;
+ int documentDeletedCount = 3;
+
+ final RemoveStats rStats =
+ new RemoveStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
+ .setStatusCode(TEST_STATUS_CODE)
+ .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
+ .setNativeLatencyMillis(nativeLatencyMillis)
+ .setDeleteType(deleteType)
+ .setDeletedDocumentCount(documentDeletedCount)
+ .build();
+
+ assertThat(rStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+ assertThat(rStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
+ assertThat(rStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+ assertThat(rStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
+ assertThat(rStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+ assertThat(rStats.getDeleteType()).isEqualTo(deleteType);
+ assertThat(rStats.getDeletedDocumentCount()).isEqualTo(documentDeletedCount);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
index 5de8a7a..e5bdafd 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
@@ -89,7 +89,7 @@
CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo(
TEST_DEFAULT_SAMPLING_RATIO);
assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+ CallStats.CALL_TYPE_SEARCH).mSamplingRatio).isEqualTo(
TEST_DEFAULT_SAMPLING_RATIO);
assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo(
@@ -103,7 +103,7 @@
int querySamplingRatio = 2;
final SparseIntArray samplingRatios = new SparseIntArray();
samplingRatios.put(CallStats.CALL_TYPE_PUT_DOCUMENT, putDocumentSamplingRatio);
- samplingRatios.put(CallStats.CALL_TYPE_QUERY, querySamplingRatio);
+ samplingRatios.put(CallStats.CALL_TYPE_SEARCH, querySamplingRatio);
PlatformLogger logger = new PlatformLogger(
ApplicationProvider.getApplicationContext(),
UserHandle.USER_NULL,
@@ -127,7 +127,7 @@
CallStats.CALL_TYPE_PUT_DOCUMENT).mSamplingRatio).isEqualTo(
putDocumentSamplingRatio);
assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+ CallStats.CALL_TYPE_SEARCH).mSamplingRatio).isEqualTo(
querySamplingRatio);
}
@@ -196,6 +196,12 @@
}
@Test
+ public void testCalculateHashCode_MD5_strIsNull() throws
+ NoSuchAlgorithmException, UnsupportedEncodingException {
+ assertThat(PlatformLogger.calculateHashCodeMd5(/*str=*/ null)).isEqualTo(-1);
+ }
+
+ @Test
public void testShouldLogForTypeLocked_trueWhenSampleRatioIsOne() {
final int samplingRatio = 1;
final String testPackageName = "packageName";
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
new file mode 100644
index 0000000..b8fbe34
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.face.ISession;
+import android.os.Handler;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.LockoutCache;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+@Presubmit
+@SmallTest
+public class SensorTest {
+
+ private static final String TAG = "SensorTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final byte[] HAT = new byte[69];
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
+ @Mock
+ private Sensor.HalSessionCallback.Callback mHalSessionCallback;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+
+ private final TestLooper mLooper = new TestLooper();
+ private final LockoutCache mLockoutCache = new LockoutCache();
+
+ private UserAwareBiometricScheduler mScheduler;
+ private Sensor.HalSessionCallback mHalCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
+
+ mScheduler = new UserAwareBiometricScheduler(TAG,
+ null /* gestureAvailabilityDispatcher */,
+ () -> USER_ID,
+ mUserSwitchCallback);
+ mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
+ TAG, mScheduler, SENSOR_ID,
+ USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
+ }
+
+ @Test
+ public void halSessionCallback_respondsToResetLockout() throws Exception {
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ mHalCallback.onLockoutCleared();
+ return null;
+ }).when(mSession).resetLockout(any());
+ mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED);
+
+ mScheduler.scheduleClientMonitor(new FaceResetLockoutClient(mContext,
+ () -> mSession, USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache,
+ mLockoutResetDispatcher));
+ mLooper.dispatchAll();
+
+ verifyNotLocked();
+ }
+
+ @Test
+ public void halSessionCallback_respondsToUnprovokedResetLockout() {
+ mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED);
+
+ mHalCallback.onLockoutCleared();
+ mLooper.dispatchAll();
+
+ verifyNotLocked();
+ }
+
+ private void verifyNotLocked() {
+ assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
+ verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 0b59be6..39c51d5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -18,6 +18,10 @@
import static junit.framework.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -26,6 +30,7 @@
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
import android.os.IBinder;
import android.os.UserManager;
@@ -42,8 +47,12 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.IntStream;
@Presubmit
@SmallTest
@@ -51,6 +60,7 @@
private static final String TAG = "Face10Test";
private static final int SENSOR_ID = 1;
+ private static final int USER_ID = 20;
@Mock
private Context mContext;
@@ -86,10 +96,18 @@
FaceSensorProperties.TYPE_UNKNOWN, supportsFaceDetection, supportsSelfIllumination,
resetLockoutRequiresChallenge);
+ Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST"));
mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mScheduler);
mBinder = new Binder();
}
+ private void tick(long seconds) {
+ waitForIdle();
+ Face10.sSystemClock = Clock.fixed(Instant.ofEpochSecond(
+ Face10.sSystemClock.instant().getEpochSecond() + seconds),
+ ZoneId.of("PST"));
+ }
+
@Test
public void getAuthenticatorId_doesNotCrashWhenIdNotFound() {
assertEquals(0, mFace10.getAuthenticatorId(0 /* sensorId */, 111 /* userId */));
@@ -104,6 +122,59 @@
}
@Test
+ public void scheduleGenerateChallenge_cachesResult() {
+ final IFaceServiceReceiver[] mocks = IntStream.range(0, 3)
+ .mapToObj(i -> mock(IFaceServiceReceiver.class))
+ .toArray(IFaceServiceReceiver[]::new);
+ for (IFaceServiceReceiver mock : mocks) {
+ mFace10.scheduleGenerateChallenge(SENSOR_ID, USER_ID, mBinder, mock, TAG);
+ tick(10);
+ }
+ tick(120);
+ mFace10.scheduleGenerateChallenge(
+ SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+ waitForIdle();
+
+ verify(mScheduler, times(2))
+ .scheduleClientMonitor(isA(FaceGenerateChallengeClient.class), any());
+ }
+
+ @Test
+ public void scheduleRevokeChallenge_waitsUntilEmpty() {
+ final long challenge = 22;
+ final IFaceServiceReceiver[] mocks = IntStream.range(0, 3)
+ .mapToObj(i -> mock(IFaceServiceReceiver.class))
+ .toArray(IFaceServiceReceiver[]::new);
+ for (IFaceServiceReceiver mock : mocks) {
+ mFace10.scheduleGenerateChallenge(SENSOR_ID, USER_ID, mBinder, mock, TAG);
+ tick(10);
+ }
+ for (IFaceServiceReceiver mock : mocks) {
+ mFace10.scheduleRevokeChallenge(SENSOR_ID, USER_ID, mBinder, TAG, challenge);
+ tick(10);
+ }
+ waitForIdle();
+
+ verify(mScheduler).scheduleClientMonitor(isA(FaceRevokeChallengeClient.class), any());
+ }
+
+ @Test
+ public void scheduleRevokeChallenge_doesNotWaitForever() {
+ mFace10.scheduleGenerateChallenge(
+ SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+ mFace10.scheduleGenerateChallenge(
+ SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+ tick(10000);
+ mFace10.scheduleGenerateChallenge(
+ SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+ mFace10.scheduleRevokeChallenge(
+ SENSOR_ID, USER_ID, mBinder, TAG, 8 /* challenge */);
+ waitForIdle();
+
+ verify(mScheduler).scheduleClientMonitor(isA(FaceRevokeChallengeClient.class), any());
+ }
+
+ @Test
public void halServiceDied_resetsScheduler() {
// It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
// serviceDied directly.
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
new file mode 100644
index 0000000..55dc035
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.hidl;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalUint64;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@SmallTest
+public class FaceGenerateChallengeClientTest {
+
+ private static final String TAG = "FaceGenerateChallengeClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final long START_TIME = 5000;
+ private static final long CHALLENGE = 200;
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Mock
+ private IBiometricsFace mIBiometricsFace;
+ @Mock
+ private IFaceServiceReceiver mClientReceiver;
+ @Mock
+ private IFaceServiceReceiver mOtherReceiver;
+ @Mock
+ private BaseClientMonitor.Callback mMonitorCallback;
+
+ private FaceGenerateChallengeClient mClient;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ final OptionalUint64 challenge = new OptionalUint64();
+ challenge.value = CHALLENGE;
+ when(mIBiometricsFace.generateChallenge(anyInt())).thenReturn(challenge);
+
+ mClient = new FaceGenerateChallengeClient(mContext, () -> mIBiometricsFace, new Binder(),
+ new ClientMonitorCallbackConverter(mClientReceiver), USER_ID,
+ TAG, SENSOR_ID, START_TIME);
+ }
+
+ @Test
+ public void getCreatedAt() {
+ assertEquals(START_TIME, mClient.getCreatedAt());
+ }
+
+ @Test
+ public void reuseResult_whenNotReady() throws Exception {
+ mClient.reuseResult(mOtherReceiver);
+ verify(mOtherReceiver, never()).onChallengeGenerated(anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void reuseResult_whenReady() throws Exception {
+ mClient.start(mMonitorCallback);
+ mClient.reuseResult(mOtherReceiver);
+ verify(mOtherReceiver).onChallengeGenerated(eq(SENSOR_ID), eq(USER_ID), eq(CHALLENGE));
+ }
+
+ @Test
+ public void reuseResult_whenReallyReady() throws Exception {
+ mClient.reuseResult(mOtherReceiver);
+ mClient.start(mMonitorCallback);
+ verify(mOtherReceiver).onChallengeGenerated(eq(SENSOR_ID), eq(USER_ID), eq(CHALLENGE));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
new file mode 100644
index 0000000..5dfc248
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.Handler;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.LockoutCache;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+@Presubmit
+@SmallTest
+public class SensorTest {
+
+ private static final String TAG = "SensorTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final byte[] HAT = new byte[69];
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
+ @Mock
+ private Sensor.HalSessionCallback.Callback mHalSessionCallback;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+
+ private final TestLooper mLooper = new TestLooper();
+ private final LockoutCache mLockoutCache = new LockoutCache();
+
+ private UserAwareBiometricScheduler mScheduler;
+ private Sensor.HalSessionCallback mHalCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
+
+ mScheduler = new UserAwareBiometricScheduler(TAG,
+ null /* gestureAvailabilityDispatcher */,
+ () -> USER_ID,
+ mUserSwitchCallback);
+ mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
+ TAG, mScheduler, SENSOR_ID,
+ USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
+ }
+
+ @Test
+ public void halSessionCallback_respondsToResetLockout() throws Exception {
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ mHalCallback.onLockoutCleared();
+ return null;
+ }).when(mSession).resetLockout(any());
+ mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED);
+
+ mScheduler.scheduleClientMonitor(new FingerprintResetLockoutClient(mContext,
+ () -> mSession, USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache,
+ mLockoutResetDispatcher));
+ mLooper.dispatchAll();
+
+ verifyNotLocked();
+ }
+
+ @Test
+ public void halSessionCallback_respondsToUnprovokedResetLockout() {
+ mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED);
+
+ mHalCallback.onLockoutCleared();
+ mLooper.dispatchAll();
+
+ verifyNotLocked();
+ }
+
+ private void verifyNotLocked() {
+ assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
+ verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index c54dffc..2270943 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -41,6 +41,7 @@
import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE;
import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_TURN_PROFILE_ON_NOTIFICATION;
import static com.android.server.devicepolicy.DpmMockContext.CALLER_USER_HANDLE;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.testutils.TestUtils.assertExpectException;
import static com.google.common.truth.Truth.assertThat;
@@ -82,6 +83,7 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.PasswordMetrics;
+import android.app.admin.SystemUpdatePolicy;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
@@ -3888,37 +3890,28 @@
public void testForceUpdateUserSetupComplete_permission() {
// GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted
assertExpectException(SecurityException.class, /* messageRegex =*/ null,
- () -> dpm.forceUpdateUserSetupComplete());
- }
-
- @Test
- public void testForceUpdateUserSetupComplete_systemUser() {
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
- // GIVEN calling from user 20
- mContext.binder.callingUid = DpmMockContext.CALLER_UID;
- assertExpectException(SecurityException.class, /* messageRegex =*/ null,
- () -> dpm.forceUpdateUserSetupComplete());
+ () -> dpm.forceUpdateUserSetupComplete(UserHandle.USER_SYSTEM));
}
@Test
public void testForceUpdateUserSetupComplete_forcesUpdate() {
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ final int userId = UserHandle.getUserId(mContext.binder.callingUid);
- final int userId = UserHandle.USER_SYSTEM;
// GIVEN userComplete is false in SettingsProvider
setUserSetupCompleteForUser(false, userId);
// GIVEN userComplete is true in DPM
DevicePolicyData userData = new DevicePolicyData(userId);
userData.mUserSetupComplete = true;
- dpms.mUserData.put(UserHandle.USER_SYSTEM, userData);
+ dpms.mUserData.put(userId, userData);
assertThat(dpms.hasUserSetupCompleted()).isTrue();
- dpm.forceUpdateUserSetupComplete();
+ dpm.forceUpdateUserSetupComplete(userId);
- // THEN the state in dpms is not changed
+ // THEN the state in dpms is changed
assertThat(dpms.hasUserSetupCompleted()).isFalse();
}
@@ -3948,10 +3941,9 @@
// Enabling logging should not change the timestamp.
dpm.setSecurityLoggingEnabled(admin1, true);
- verify(getServices().settings)
- .securityLogSetLoggingEnabledProperty(true);
- when(getServices().settings.securityLogGetLoggingEnabledProperty())
- .thenReturn(true);
+ verify(getServices().settings).securityLogSetLoggingEnabledProperty(true);
+
+ when(getServices().settings.securityLogGetLoggingEnabledProperty()).thenReturn(true);
assertThat(dpm.getLastSecurityLogRetrievalTime()).isEqualTo(-1);
// Retrieving the logs should update the timestamp.
@@ -4776,8 +4768,8 @@
when(getServices().iactivityManager.getCurrentUser())
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
// Get mock reason string since we throw an IAE with empty string input.
- when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)).
- thenReturn("Just a test string.");
+ when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe))
+ .thenReturn("Just a test string.");
dpm.wipeData(0);
verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed(
@@ -4785,6 +4777,68 @@
}
@Test
+ public void testWipeDataManagedProfileOnOrganizationOwnedDevice() throws Exception {
+ setupProfileOwner();
+ configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
+
+ // Even if the caller is the managed profile, the current user is the user 0
+ when(getServices().iactivityManager.getCurrentUser())
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+ // Get mock reason string since we throw an IAE with empty string input.
+ when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe))
+ .thenReturn("Just a test string.");
+ when(getServices().userManager.getProfileParent(CALLER_USER_HANDLE))
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+ when(getServices().userManager.getPrimaryUser())
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+
+ // Set some device-wide policies:
+ // Security logging
+ when(getServices().settings.securityLogGetLoggingEnabledProperty()).thenReturn(true);
+ // System update policy
+ dpms.mOwners.setSystemUpdatePolicy(SystemUpdatePolicy.createAutomaticInstallPolicy());
+ // Make it look as if FRP agent is present.
+ when(dpms.mMockInjector.getPersistentDataBlockManagerInternal().getAllowedUid())
+ .thenReturn(12345 /* some UID in user 0 */);
+ // Make personal apps look suspended
+ dpms.getUserData(UserHandle.USER_SYSTEM).mAppsSuspended = true;
+
+ clearInvocations(getServices().iwindowManager);
+
+ dpm.wipeData(0);
+ verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed(CALLER_USER_HANDLE);
+
+ // Make sure COPE restrictions are lifted:
+ verify(getServices().userManager).setUserRestriction(
+ UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false, UserHandle.SYSTEM);
+ verify(getServices().userManager).setUserRestriction(
+ UserManager.DISALLOW_ADD_USER, false, UserHandle.SYSTEM);
+
+ // Some device-wide policies are getting cleaned-up after the user is removed.
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_REMOVED, CALLER_USER_HANDLE);
+
+ // Screenlock info should be removed
+ verify(getServices().lockPatternUtils).setDeviceOwnerInfo(null);
+ // Wifi config lockdown should be lifted
+ verify(getServices().settings).settingsGlobalPutInt(
+ Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0);
+ // System update policy should be removed
+ assertThat(dpms.mOwners.getSystemUpdatePolicy()).isNull();
+ // FRP agent should be notified
+ verify(mContext.spiedContext, times(0)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
+ MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ // Refresh strong auth timeout and screen capture
+ verify(getServices().lockSettingsInternal).refreshStrongAuthTimeout(UserHandle.USER_SYSTEM);
+ verify(getServices().iwindowManager).refreshScreenCaptureDisabled(UserHandle.USER_SYSTEM);
+ // Unsuspend personal apps
+ verify(getServices().packageManagerInternal)
+ .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM);
+ }
+
+ @Test
public void testWipeDataManagedProfileDisallowed() throws Exception {
final int MANAGED_PROFILE_USER_ID = 15;
final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index cda659f..fb2db22 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -23,10 +23,11 @@
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
-import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER;
+import static com.android.server.display.DisplayModeDirector.Vote.INVALID_SIZE;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -137,7 +138,14 @@
}
}
assertThat(defaultMode).isNotNull();
+ return createDirectorFromModeArray(modes, defaultMode);
+ }
+ private DisplayModeDirector createDirectorFromModeArray(Display.Mode[] modes,
+ Display.Mode defaultMode) {
+ DisplayModeDirector director =
+ new DisplayModeDirector(mContext, mHandler, mInjector);
+ director.setLoggingEnabled(true);
SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
supportedModesByDisplay.put(DISPLAY_ID, modes);
director.injectSupportedModesByDisplay(supportedModesByDisplay);
@@ -218,8 +226,9 @@
votesByDisplay.put(DISPLAY_ID, votes);
float error = FLOAT_TOLERANCE / 4;
votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(0, 60));
- votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forRefreshRates(60 + error, 60 + error));
- votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE,
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forRefreshRates(60 + error, 60 + error));
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forRefreshRates(60 - error, 60 - error));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
@@ -230,44 +239,159 @@
}
@Test
- public void testFlickerHasLowerPriorityThanUser() {
- assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
- assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE);
+ public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle() {
+ assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE
+ < Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+ assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE
+ < Vote.PRIORITY_APP_REQUEST_SIZE);
- DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
+ assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH
+ > Vote.PRIORITY_LOW_POWER_MODE);
+
+ Display.Mode[] modes = new Display.Mode[4];
+ modes[0] = new Display.Mode(
+ /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
+ modes[1] = new Display.Mode(
+ /*modeId=*/2, /*width=*/2000, /*height=*/2000, 60);
+ modes[2] = new Display.Mode(
+ /*modeId=*/3, /*width=*/1000, /*height=*/1000, 90);
+ modes[3] = new Display.Mode(
+ /*modeId=*/4, /*width=*/2000, /*height=*/2000, 90);
+
+ DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(DISPLAY_ID, votes);
- votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
+ Display.Mode appRequestedMode = modes[1];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(2);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max)
+ .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min);
+
+ votes.clear();
+ appRequestedMode = modes[3];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(90, 90));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(4);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max)
+ .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min);
+
+ votes.clear();
+ appRequestedMode = modes[3];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(4);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max)
+ .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min);
+
+ votes.clear();
+ appRequestedMode = modes[1];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(90, 90));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(2);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max)
+ .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min);
+ }
+
+ @Test
+ public void testLPMHasHigherPriorityThanUser() {
+ assertTrue(Vote.PRIORITY_LOW_POWER_MODE > Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+ assertTrue(Vote.PRIORITY_LOW_POWER_MODE > Vote.PRIORITY_APP_REQUEST_SIZE);
+
+
+ Display.Mode[] modes = new Display.Mode[4];
+ modes[0] = new Display.Mode(
+ /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
+ modes[1] = new Display.Mode(
+ /*modeId=*/2, /*width=*/2000, /*height=*/2000, 60);
+ modes[2] = new Display.Mode(
+ /*modeId=*/3, /*width=*/1000, /*height=*/1000, 90);
+ modes[3] = new Display.Mode(
+ /*modeId=*/4, /*width=*/2000, /*height=*/2000, 90);
+
+ DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ Display.Mode appRequestedMode = modes[1];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(60, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(2);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
votes.clear();
- votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
+ appRequestedMode = modes[3];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(4);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
votes.clear();
- votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
+ appRequestedMode = modes[3];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
- assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
-
- votes.clear();
- votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
- director.injectVotesByDisplay(votesByDisplay);
- desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(2);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+
+ votes.clear();
+ appRequestedMode = modes[1];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(90, 90));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(4);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
}
@Test
@@ -275,19 +399,30 @@
// Confirm that the app request range doesn't include flicker or min refresh rate settings,
// but does include everything else.
assertTrue(
- PRIORITY_FLICKER < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
+ Vote.PRIORITY_FLICKER_REFRESH_RATE
+ < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE
< Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
- assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE
+ assertTrue(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE
>= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
- DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
+ Display.Mode[] modes = new Display.Mode[3];
+ modes[0] = new Display.Mode(
+ /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
+ modes[1] = new Display.Mode(
+ /*modeId=*/75, /*width=*/2000, /*height=*/2000, 75);
+ modes[2] = new Display.Mode(
+ /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
+
+ DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(DISPLAY_ID, votes);
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(60);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
@@ -297,22 +432,24 @@
Vote.forRefreshRates(90, Float.POSITIVE_INFINITY));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(90);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f);
assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f);
- votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(75, 75));
+ Display.Mode appRequestedMode = modes[1];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(75);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75);
- assertThat(desiredSpecs.appRequestRefreshRateRange.min)
- .isWithin(FLOAT_TOLERANCE)
- .of(75);
- assertThat(desiredSpecs.appRequestRefreshRateRange.max)
- .isWithin(FLOAT_TOLERANCE)
- .of(75);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f);
}
void verifySpecsWithRefreshRateSettings(DisplayModeDirector director, float minFps,
@@ -374,18 +511,29 @@
@Test
public void testVotingWithAlwaysRespectAppRequest() {
- DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
+ Display.Mode[] modes = new Display.Mode[3];
+ modes[0] = new Display.Mode(
+ /*modeId=*/50, /*width=*/1000, /*height=*/1000, 50);
+ modes[1] = new Display.Mode(
+ /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
+ modes[2] = new Display.Mode(
+ /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
+
+ DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
+
+
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(DISPLAY_ID, votes);
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(0, 60));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(0, 60));
votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(60, 90));
- votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
+ Display.Mode appRequestedMode = modes[2];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(60, 60));
votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60));
-
-
director.injectVotesByDisplay(votesByDisplay);
+
assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
@@ -396,8 +544,8 @@
director.setShouldAlwaysRespectAppRequestedMode(true);
assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue();
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
- assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isAtMost(50);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90);
assertThat(desiredSpecs.baseModeId).isEqualTo(90);
director.setShouldAlwaysRespectAppRequestedMode(false);
@@ -497,7 +645,8 @@
// Inject votes
SparseArray<Vote> votes = new SparseArray<>();
votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(1920, 1080));
- votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(50, 50));
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(60));
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(DISPLAY_ID, votes);
director.injectVotesByDisplay(votesByDisplay);
@@ -592,14 +741,19 @@
// Sensor reads 20 lux,
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
- Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
assertVoteForRefreshRate(vote, 90 /*fps*/);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNotNull();
+ assertThat(vote.disableRefreshRateSwitching).isTrue();
setBrightness(125);
// Sensor reads 1000 lux,
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
- vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertThat(vote).isNull();
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
assertThat(vote).isNull();
}
@@ -635,15 +789,20 @@
// Sensor reads 2000 lux,
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
- Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertThat(vote).isNull();
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
assertThat(vote).isNull();
setBrightness(255);
// Sensor reads 9000 lux,
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
- vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
assertVoteForRefreshRate(vote, 60 /*fps*/);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNotNull();
+ assertThat(vote.disableRefreshRateSwitching).isTrue();
}
@Test
@@ -724,6 +883,336 @@
assertNull(vote);
}
+ @Test
+ public void testAppRequestMaxRefreshRate() {
+ // Confirm that the app max request range doesn't include flicker or min refresh rate
+ // settings but does include everything else.
+ assertTrue(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE
+ >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
+
+ Display.Mode[] modes = new Display.Mode[3];
+ modes[0] = new Display.Mode(
+ /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
+ modes[1] = new Display.Mode(
+ /*modeId=*/75, /*width=*/1000, /*height=*/1000, 75);
+ modes[2] = new Display.Mode(
+ /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
+
+ DisplayModeDirector director = createDirectorFromModeArray(modes, modes[1]);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f);
+
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
+ Vote.forRefreshRates(90, Float.POSITIVE_INFINITY));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f);
+
+ votes.put(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, Vote.forRefreshRates(0, 75));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.min).isZero();
+ assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75);
+ }
+
+ @Test
+ public void testAppRequestObserver_modeId() {
+ DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
+ director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0);
+
+ Vote appRequestRefreshRate =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+ assertNotNull(appRequestRefreshRate);
+ assertThat(appRequestRefreshRate.refreshRateRange.min).isZero();
+ assertThat(appRequestRefreshRate.refreshRateRange.max).isPositiveInfinity();
+ assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
+ assertThat(appRequestRefreshRate.baseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
+ assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
+
+ Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+ assertNotNull(appRequestSize);
+ assertThat(appRequestSize.refreshRateRange.min).isZero();
+ assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity();
+ assertThat(appRequestSize.disableRefreshRateSwitching).isFalse();
+ assertThat(appRequestSize.baseModeRefreshRate).isZero();
+ assertThat(appRequestSize.height).isEqualTo(1000);
+ assertThat(appRequestSize.width).isEqualTo(1000);
+
+ Vote appRequestMaxRefreshRate =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE);
+ assertNull(appRequestMaxRefreshRate);
+
+ director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 90, 0);
+
+ appRequestRefreshRate =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+ assertNotNull(appRequestRefreshRate);
+ assertThat(appRequestRefreshRate.refreshRateRange.min).isZero();
+ assertThat(appRequestRefreshRate.refreshRateRange.max).isPositiveInfinity();
+ assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
+ assertThat(appRequestRefreshRate.baseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
+ assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
+
+ appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+ assertNotNull(appRequestSize);
+ assertThat(appRequestSize.refreshRateRange.min).isZero();
+ assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity();
+ assertThat(appRequestSize.height).isEqualTo(1000);
+ assertThat(appRequestSize.width).isEqualTo(1000);
+
+ appRequestMaxRefreshRate =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE);
+ assertNull(appRequestMaxRefreshRate);
+ }
+
+ @Test
+ public void testAppRequestObserver_maxRefreshRate() {
+ DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
+ director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90);
+ Vote appRequestRefreshRate =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+ assertNull(appRequestRefreshRate);
+
+ Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+ assertNull(appRequestSize);
+
+ Vote appRequestMaxRefreshRate =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE);
+ assertNotNull(appRequestMaxRefreshRate);
+ assertThat(appRequestMaxRefreshRate.refreshRateRange.min).isZero();
+ assertThat(appRequestMaxRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(appRequestMaxRefreshRate.height).isEqualTo(INVALID_SIZE);
+ assertThat(appRequestMaxRefreshRate.width).isEqualTo(INVALID_SIZE);
+
+ director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60);
+ appRequestRefreshRate =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+ assertNull(appRequestRefreshRate);
+
+ appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+ assertNull(appRequestSize);
+
+ appRequestMaxRefreshRate =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE);
+ assertNotNull(appRequestMaxRefreshRate);
+ assertThat(appRequestMaxRefreshRate.refreshRateRange.min).isZero();
+ assertThat(appRequestMaxRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(appRequestMaxRefreshRate.height).isEqualTo(INVALID_SIZE);
+ assertThat(appRequestMaxRefreshRate.width).isEqualTo(INVALID_SIZE);
+ }
+
+ @Test
+ public void testAppRequestObserver_modeIdAndMaxRefreshRate() {
+ DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
+ director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90);
+
+ Vote appRequestRefreshRate =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+ assertNotNull(appRequestRefreshRate);
+ assertThat(appRequestRefreshRate.refreshRateRange.min).isZero();
+ assertThat(appRequestRefreshRate.refreshRateRange.max).isPositiveInfinity();
+ assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
+ assertThat(appRequestRefreshRate.baseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
+ assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
+
+ Vote appRequestSize =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+ assertNotNull(appRequestSize);
+ assertThat(appRequestSize.refreshRateRange.min).isZero();
+ assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity();
+ assertThat(appRequestSize.height).isEqualTo(1000);
+ assertThat(appRequestSize.width).isEqualTo(1000);
+
+ Vote appRequestMaxRefreshRate =
+ director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE);
+ assertNotNull(appRequestMaxRefreshRate);
+ assertThat(appRequestMaxRefreshRate.refreshRateRange.min).isZero();
+ assertThat(appRequestMaxRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(appRequestMaxRefreshRate.height).isEqualTo(INVALID_SIZE);
+ assertThat(appRequestMaxRefreshRate.width).isEqualTo(INVALID_SIZE);
+ }
+
+ @Test
+ public void testAppRequestsIsTheDefaultMode() {
+ Display.Mode[] modes = new Display.Mode[2];
+ modes[0] = new Display.Mode(
+ /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
+ modes[1] = new Display.Mode(
+ /*modeId=*/2, /*width=*/1000, /*height=*/1000, 90);
+
+ DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(1);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isAtMost(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90);
+
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ Display.Mode appRequestedMode = modes[1];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(2);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isAtMost(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90);
+ }
+
+ @Test
+ public void testDisableRefreshRateSwitchingVote() {
+ DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
+ Vote.forRefreshRates(90, Float.POSITIVE_INFINITY));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(50);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(50);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(50);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE,
+ Vote.forRefreshRates(70, Float.POSITIVE_INFINITY));
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
+ Vote.forRefreshRates(80, Float.POSITIVE_INFINITY));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 90));
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(80);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(80);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(80);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE,
+ Vote.forRefreshRates(90, Float.POSITIVE_INFINITY));
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
+ Vote.forRefreshRates(80, Float.POSITIVE_INFINITY));
+ votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 90));
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(90);
+ }
+
+ @Test
+ public void testBaseModeIdInPrimaryRange() {
+ DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(70));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(50);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(55));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(55);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, Vote.forRefreshRates(0, 52));
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(55));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(55);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, Vote.forRefreshRates(0, 58));
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(55));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(58);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(55);
+ }
+
+ @Test
+ public void testStaleAppVote() {
+ Display.Mode[] modes = new Display.Mode[4];
+ modes[0] = new Display.Mode(
+ /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
+ modes[1] = new Display.Mode(
+ /*modeId=*/2, /*width=*/2000, /*height=*/2000, 60);
+ modes[2] = new Display.Mode(
+ /*modeId=*/3, /*width=*/1000, /*height=*/1000, 90);
+ modes[3] = new Display.Mode(
+ /*modeId=*/4, /*width=*/2000, /*height=*/2000, 90);
+
+ DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ Display.Mode appRequestedMode = modes[3];
+ votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
+ appRequestedMode.getPhysicalHeight()));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(4);
+
+ // Change mode Id's to simulate that a hotplug has occurred.
+ Display.Mode[] newModes = new Display.Mode[4];
+ newModes[0] = new Display.Mode(
+ /*modeId=*/5, /*width=*/1000, /*height=*/1000, 60);
+ newModes[1] = new Display.Mode(
+ /*modeId=*/6, /*width=*/2000, /*height=*/2000, 60);
+ newModes[2] = new Display.Mode(
+ /*modeId=*/7, /*width=*/1000, /*height=*/1000, 90);
+ newModes[3] = new Display.Mode(
+ /*modeId=*/8, /*width=*/2000, /*height=*/2000, 90);
+
+ SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
+ supportedModesByDisplay.put(DISPLAY_ID, newModes);
+ director.injectSupportedModesByDisplay(supportedModesByDisplay);
+ SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
+ defaultModesByDisplay.put(DISPLAY_ID, newModes[0]);
+ director.injectDefaultModeByDisplay(defaultModesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(8);
+ }
+
private void assertVoteForRefreshRate(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
final DisplayModeDirector.RefreshRateRange expectedRange =
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 5caff3d..f9b8373 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -92,7 +92,7 @@
}
@Override
- public void tryToCreateTypeface(File file) throws IOException {
+ public void tryToCreateTypeface(File file) throws Throwable {
}
}
@@ -139,9 +139,11 @@
private File mUpdatableFontFilesDir;
private File mConfigFile;
private List<File> mPreinstalledFontDirs;
- private Supplier<Long> mCurrentTimeSupplier = () -> CURRENT_TIME;
- private Function<Map<String, File>, FontConfig> mConfigSupplier =
+ private final Supplier<Long> mCurrentTimeSupplier = () -> CURRENT_TIME;
+ private final Function<Map<String, File>, FontConfig> mConfigSupplier =
(map) -> SystemFonts.getSystemFontConfig(map, 0, 0);
+ private FakeFontFileParser mParser;
+ private FakeFsverityUtil mFakeFsverityUtil;
@SuppressWarnings("ResultOfMethodCallIgnored")
@Before
@@ -159,6 +161,8 @@
dir.mkdir();
}
mConfigFile = new File(mCacheDir, "config.xml");
+ mParser = new FakeFontFileParser();
+ mFakeFsverityUtil = new FakeFsverityUtil();
}
@After
@@ -169,13 +173,11 @@
@Test
public void construct() throws Exception {
long expectedModifiedDate = CURRENT_TIME / 2;
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
config.lastModifiedMillis = expectedModifiedDate;
writeConfig(config, mConfigFile);
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dirForPreparation.loadFontFileMap();
assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
@@ -198,13 +200,13 @@
.isNotEqualTo(expectedModifiedDate);
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getPostScriptMap()).containsKey("foo");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(3);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(3);
assertThat(dir.getPostScriptMap()).containsKey("bar");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4);
// Outdated font dir should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(2);
assertNamedFamilyExists(dir.getSystemFontConfig(), "foobar");
@@ -219,10 +221,8 @@
@Test
public void construct_empty() {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getPostScriptMap()).isEmpty();
@@ -231,10 +231,8 @@
@Test
public void construct_missingFsverity() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
@@ -249,10 +247,10 @@
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
- fakeFsverityUtil.remove(
+ mFakeFsverityUtil.remove(
dirForPreparation.getPostScriptMap().get("foo").getAbsolutePath());
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getPostScriptMap()).isEmpty();
@@ -263,10 +261,8 @@
@Test
public void construct_fontNameMismatch() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
@@ -285,7 +281,7 @@
FileUtils.stringToFile(dirForPreparation.getPostScriptMap().get("foo"), "bar,4");
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getPostScriptMap()).isEmpty();
@@ -296,8 +292,6 @@
@Test
public void construct_olderThanPreinstalledFont() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
Function<Map<String, File>, FontConfig> configSupplier = (map) -> {
FontConfig.Font fooFont = new FontConfig.Font(
new File(mPreinstalledFontDirs.get(0), "foo.ttf"), null, "foo",
@@ -314,7 +308,7 @@
};
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, configSupplier);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
@@ -334,14 +328,14 @@
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,1,bar");
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,2,bar");
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, configSupplier);
dir.loadFontFileMap();
// For foo.ttf, preinstalled font (revision 5) should be used.
assertThat(dir.getPostScriptMap()).doesNotContainKey("foo");
// For bar.ttf, updated font (revision 4) should be used.
assertThat(dir.getPostScriptMap()).containsKey("bar");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4);
// Outdated font dir should be deleted.
// We don't delete bar.ttf in this case, because it's normal that OTA updates preinstalled
// fonts.
@@ -352,10 +346,8 @@
@Test
public void construct_failedToLoadConfig() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
new File("/dev/null"), mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getPostScriptMap()).isEmpty();
@@ -364,10 +356,8 @@
@Test
public void construct_afterBatchFailure() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
@@ -389,12 +379,12 @@
}
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
// The state should be rolled back as a whole if one of the update requests fail.
assertThat(dir.getPostScriptMap()).containsKey("foo");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(dir.getFontFamilyMap()).containsKey("foobar");
FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar");
assertThat(foobar.getFontList()).hasSize(1);
@@ -404,10 +394,8 @@
@Test
public void loadFontFileMap_twice() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test",
@@ -420,17 +408,15 @@
@Test
public void installFontFile() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("test");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
File fontFile = dir.getPostScriptMap().get("test");
assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644);
File fontDir = fontFile.getParentFile();
@@ -439,10 +425,8 @@
@Test
public void installFontFile_upgrade() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -452,10 +436,10 @@
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("test");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
assertThat(mapBeforeUpgrade).containsKey("test");
assertWithMessage("Older fonts should not be deleted until next loadFontFileMap")
- .that(parser.getRevision(mapBeforeUpgrade.get("test"))).isEqualTo(1);
+ .that(mParser.getRevision(mapBeforeUpgrade.get("test"))).isEqualTo(1);
// Check that updatedFontDirs is pruned.
assertWithMessage("config.updatedFontDirs should only list latest active dirs")
.that(readConfig(mConfigFile).updatedFontDirs)
@@ -464,15 +448,13 @@
@Test
public void installFontFile_systemFontHasPSNameDifferentFromFileName() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
// Setup the environment that the system installed font file named "foo.ttf" has PostScript
// name "bar".
File file = new File(mPreinstalledFontDirs.get(0), "foo.ttf");
FileUtils.stringToFile(file, "foo.ttf,1,bar");
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, (map) -> {
FontConfig.Font font = new FontConfig.Font(
file, null, "bar", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT),
@@ -489,7 +471,7 @@
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("bar");
assertThat(dir.getPostScriptMap().size()).isEqualTo(1);
- assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
File fontFile = dir.getPostScriptMap().get("bar");
assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644);
File fontDir = fontFile.getParentFile();
@@ -498,10 +480,8 @@
@Test
public void installFontFile_sameVersion() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -510,15 +490,13 @@
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("test");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
}
@Test
public void installFontFile_downgrade() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -533,7 +511,7 @@
}
assertThat(dir.getPostScriptMap()).containsKey("test");
assertWithMessage("Font should not be downgraded to an older revision")
- .that(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
+ .that(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
// Check that updatedFontDirs is not updated.
assertWithMessage("config.updatedFontDirs should only list latest active dirs")
.that(readConfig(mConfigFile).updatedFontDirs)
@@ -542,10 +520,8 @@
@Test
public void installFontFile_multiple() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -554,17 +530,15 @@
dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2,bar",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("foo");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(dir.getPostScriptMap()).containsKey("bar");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
}
@Test
public void installFontFile_batch() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -572,17 +546,15 @@
newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE),
newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("foo");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(dir.getPostScriptMap()).containsKey("bar");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
}
@Test
public void installFontFile_invalidSignature() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -600,46 +572,40 @@
@Test
public void installFontFile_preinstalled_upgrade() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"),
"test.ttf,1,test");
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("test");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
}
@Test
public void installFontFile_preinstalled_sameVersion() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"),
"test.ttf,1,test");
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("test");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
}
@Test
public void installFontFile_preinstalled_downgrade() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
File file = new File(mPreinstalledFontDirs.get(0), "test.ttf");
FileUtils.stringToFile(file, "test.ttf,2,test");
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, (map) -> {
FontConfig.Font font = new FontConfig.Font(
file, null, "test", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null,
@@ -664,8 +630,6 @@
@Test
public void installFontFile_failedToWriteConfigXml() throws Exception {
long expectedModifiedDate = 1234567890;
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"),
"test.ttf,1,test");
@@ -680,7 +644,7 @@
assertThat(readonlyDir.setWritable(false, false)).isTrue();
try {
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
readonlyFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -702,7 +666,6 @@
@Test
public void installFontFile_failedToParsePostScript() throws Exception {
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir,
new UpdatableFontDir.FontFileParser() {
@@ -725,7 +688,7 @@
@Override
public void tryToCreateTypeface(File file) throws IOException {
}
- }, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
+ }, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
try {
@@ -741,7 +704,6 @@
@Test
public void installFontFile_failedToParsePostScriptName_invalidFont() throws Exception {
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir,
new UpdatableFontDir.FontFileParser() {
@@ -763,7 +725,7 @@
@Override
public void tryToCreateTypeface(File file) throws IOException {
}
- }, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
+ }, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
try {
@@ -779,31 +741,29 @@
@Test
public void installFontFile_failedToCreateTypeface() throws Exception {
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
- FakeFontFileParser parser = new FakeFontFileParser();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir,
new UpdatableFontDir.FontFileParser() {
@Override
public String getPostScriptName(File file) throws IOException {
- return parser.getPostScriptName(file);
+ return mParser.getPostScriptName(file);
}
@Override
public String buildFontFileName(File file) throws IOException {
- return parser.buildFontFileName(file);
+ return mParser.buildFontFileName(file);
}
@Override
public long getRevision(File file) throws IOException {
- return parser.getRevision(file);
+ return mParser.getRevision(file);
}
@Override
public void tryToCreateTypeface(File file) throws IOException {
throw new IOException();
}
- }, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
+ }, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
try {
@@ -820,16 +780,15 @@
@Test
public void installFontFile_renameToPsNameFailure() throws Exception {
UpdatableFontDir.FsverityUtil fakeFsverityUtil = new UpdatableFontDir.FsverityUtil() {
- private final FakeFsverityUtil mFake = new FakeFsverityUtil();
@Override
public boolean hasFsverity(String path) {
- return mFake.hasFsverity(path);
+ return mFakeFsverityUtil.hasFsverity(path);
}
@Override
public void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException {
- mFake.setUpFsverity(path, pkcs7Signature);
+ mFakeFsverityUtil.setUpFsverity(path, pkcs7Signature);
}
@Override
@@ -837,9 +796,8 @@
return false;
}
};
- FakeFontFileParser parser = new FakeFontFileParser();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, fakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -856,10 +814,8 @@
@Test
public void installFontFile_batchFailure() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -875,15 +831,13 @@
}
// The state should be rolled back as a whole if one of the update requests fail.
assertThat(dir.getPostScriptMap()).containsKey("foo");
- assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
+ assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
}
@Test
public void addFontFamily() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -902,10 +856,8 @@
@Test
public void addFontFamily_noName() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -924,10 +876,8 @@
@Test
public void addFontFamily_fontNotAvailable() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -944,10 +894,8 @@
@Test
public void getSystemFontConfig() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
// We assume we have monospace.
@@ -976,10 +924,8 @@
@Test
public void getSystemFontConfig_preserveFirstFontFamily() throws Exception {
- FakeFontFileParser parser = new FakeFontFileParser();
- FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty();
@@ -1001,6 +947,24 @@
assertThat(updated).isNotEqualTo(firstFontFamily);
}
+ @Test
+ public void deleteAllFiles() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dirForPreparation = new UpdatableFontDir(
+ mUpdatableFontFilesDir, parser, fakeFsverityUtil,
+ mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
+ dirForPreparation.loadFontFileMap();
+ dirForPreparation.update(Collections.singletonList(
+ newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE)));
+ assertThat(mConfigFile.exists()).isTrue();
+ assertThat(mUpdatableFontFilesDir.list()).hasLength(1);
+
+ UpdatableFontDir.deleteAllFiles(mUpdatableFontFilesDir, mConfigFile);
+ assertThat(mConfigFile.exists()).isFalse();
+ assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
+ }
+
private FontUpdateRequest newFontUpdateRequest(String content, String signature)
throws Exception {
File file = File.createTempFile("font", "ttf", mCacheDir);
@@ -1011,12 +975,12 @@
}
private static FontUpdateRequest newAddFontFamilyRequest(String xml) throws Exception {
- XmlPullParser parser = Xml.newPullParser();
+ XmlPullParser mParser = Xml.newPullParser();
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
- parser.setInput(is, "UTF-8");
- parser.nextTag();
+ mParser.setInput(is, "UTF-8");
+ mParser.nextTag();
- FontConfig.FontFamily fontFamily = FontListParser.readFamily(parser, "", null, true);
+ FontConfig.FontFamily fontFamily = FontListParser.readFamily(mParser, "", null, true);
List<FontUpdateRequest.Font> fonts = new ArrayList<>();
for (FontConfig.Font font : fontFamily.getFontList()) {
String name = font.getFile().getName();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index c08857c..6cc8d471 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -68,10 +68,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -97,7 +99,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index ee9de07..97bd066 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -70,10 +70,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiControlService hdmiControlService =
@@ -89,7 +91,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index d5df071..29c9b40 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -71,10 +71,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiControlService hdmiControlService =
@@ -85,7 +87,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 4f97c26..650ffe9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -80,10 +80,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -109,7 +111,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
index 678f8b2..fa5cb67 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
@@ -103,8 +103,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -129,7 +127,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index 45409c8..29f62b5 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
@@ -96,9 +97,10 @@
mContextSpy = spy(new ContextWrapper(
InstrumentationRegistry.getInstrumentation().getTargetContext()));
- PowerManager powerManager = new PowerManager(
- mContextSpy, mIPowerManagerMock, mIThermalServiceMock, new Handler(mLooper));
- doReturn(powerManager).when(mContextSpy).getSystemService(Context.POWER_SERVICE);
+
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mLooper)));
doReturn(true).when(mIPowerManagerMock).isInteractive();
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 38a44c6..7911c40 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -94,8 +94,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -183,7 +181,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
};
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 1ac0150..524ad62 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -92,8 +92,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -139,8 +137,14 @@
}
@Override
+ boolean isPowerStandbyOrTransient() {
+ return false;
+ }
+
+ @Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
};
@@ -168,6 +172,8 @@
mPlaybackLogicalAddress = mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress();
mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
mNativeWrapper.clearResultMessages();
+ mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
+ HdmiProperties.playback_device_action_on_routing_control_values.NONE;
}
@Test
@@ -698,9 +704,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -720,9 +729,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -742,9 +754,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -764,9 +779,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -786,9 +804,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -808,9 +829,37 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
+ }
+
+ @Test
+ public void handleOnStandby_CecMessageReceived() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV);
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
+ mHdmiCecLocalDevicePlayback.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
}
@Test
@@ -1291,6 +1340,22 @@
}
@Test
+ public void handleSetStreamPath_Dreaming() throws RemoteException {
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+ mWokenUp = false;
+
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+ mPlaybackPhysicalAddress);
+
+ assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+ assertThat(mWokenUp).isTrue();
+ }
+
+ @Test
public void handleSetStreamPath_otherDevice_None() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 8ee983f..59711a6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -86,8 +86,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -118,7 +116,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 1c7ff42..572ffd9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -75,10 +75,12 @@
Context contextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
Looper myLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(contextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper));
- when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(contextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(contextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
+ when(contextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(contextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(contextSpy) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index b1d77d0..0cf212c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -17,6 +17,7 @@
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
@@ -47,6 +48,7 @@
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
+import android.sysprop.HdmiProperties;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -192,10 +194,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, null);
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, null));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, null));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
@@ -826,4 +830,71 @@
assertThat(mHdmiControlServiceSpy.dispatchMessageToLocalDevice(message))
.isEqualTo(Constants.ABORT_REFUSED);
}
+
+ @Test
+ public void readDeviceTypes_readsIntegerDeviceTypes() {
+ doReturn(Arrays.asList(new Integer[]{DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM}))
+ .when(mHdmiControlServiceSpy).getDeviceTypes();
+ doReturn(Arrays.asList(new HdmiProperties.cec_device_types_values[]{}))
+ .when(mHdmiControlServiceSpy).getCecDeviceTypes();
+
+ assertThat(mHdmiControlServiceSpy.readDeviceTypes())
+ .containsExactly(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM);
+ }
+
+ @Test
+ public void readDeviceTypes_readsEnumDeviceTypes() {
+ doReturn(Arrays.asList(new Integer[]{})).when(mHdmiControlServiceSpy).getDeviceTypes();
+ doReturn(Arrays.asList(
+ new HdmiProperties.cec_device_types_values[]{
+ HdmiProperties.cec_device_types_values.PLAYBACK_DEVICE,
+ HdmiProperties.cec_device_types_values.AUDIO_SYSTEM
+ }))
+ .when(mHdmiControlServiceSpy).getCecDeviceTypes();
+
+ assertThat(mHdmiControlServiceSpy.readDeviceTypes())
+ .containsExactly(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM);
+ }
+
+ @Test
+ public void readDeviceTypes_readsEnumOverIntegerDeviceTypes() {
+ doReturn(Arrays.asList(new Integer[]{DEVICE_TV}))
+ .when(mHdmiControlServiceSpy).getDeviceTypes();
+ doReturn(Arrays.asList(
+ new HdmiProperties.cec_device_types_values[]{
+ HdmiProperties.cec_device_types_values.PLAYBACK_DEVICE,
+ HdmiProperties.cec_device_types_values.AUDIO_SYSTEM
+ }))
+ .when(mHdmiControlServiceSpy).getCecDeviceTypes();
+
+ assertThat(mHdmiControlServiceSpy.readDeviceTypes())
+ .containsExactly(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM);
+ }
+
+ @Test
+ public void readDeviceTypes_doesNotReadNullEnumDeviceType() {
+ doReturn(Arrays.asList(new Integer[]{})).when(mHdmiControlServiceSpy).getDeviceTypes();
+ doReturn(Arrays.asList(
+ new HdmiProperties.cec_device_types_values[]{
+ HdmiProperties.cec_device_types_values.PLAYBACK_DEVICE,
+ HdmiProperties.cec_device_types_values.AUDIO_SYSTEM,
+ null
+ }))
+ .when(mHdmiControlServiceSpy).getCecDeviceTypes();
+
+ assertThat(mHdmiControlServiceSpy.readDeviceTypes())
+ .containsExactly(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM);
+ }
+
+ @Test
+ public void readDeviceTypes_doesNotReadNullIntegerDeviceType() {
+ doReturn(Arrays.asList(new Integer[]{DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM, null}))
+ .when(mHdmiControlServiceSpy).getDeviceTypes();
+ doReturn(Arrays.asList(new HdmiProperties.cec_device_types_values[]{}))
+ .when(mHdmiControlServiceSpy).getCecDeviceTypes();
+
+ assertThat(mHdmiControlServiceSpy.readDeviceTypes())
+ .containsExactly(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM);
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 826438fc..4cd17e8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -91,10 +91,12 @@
setHdmiControlEnabled(hdmiControlEnabled);
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -120,7 +122,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 53b4b49..a9880c0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -78,10 +78,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy,
@@ -108,7 +110,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 865eb7a..2cf4ef1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -78,10 +78,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
Looper myLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -107,7 +109,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 3581206..b2ddbf0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -50,12 +50,14 @@
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.GenericDocument;
-import android.app.appsearch.IAppSearchBatchResultCallback;
-import android.app.appsearch.IAppSearchManager;
-import android.app.appsearch.IAppSearchResultCallback;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SetSchemaResponse;
+import android.app.appsearch.aidl.AppSearchBatchResultParcel;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
@@ -659,7 +661,8 @@
public void setSchema(String packageName, String databaseName, List<Bundle> schemaBundles,
List<String> schemasNotPlatformSurfaceable,
Map<String, List<Bundle>> schemasPackageAccessibleBundles, boolean forceOverride,
- int userId, int version, IAppSearchResultCallback callback) throws RemoteException {
+ int userId, int version, long binderCallStartTimeMillis,
+ IAppSearchResultCallback callback) throws RemoteException {
for (Map.Entry<String, List<Bundle>> entry :
schemasPackageAccessibleBundles.entrySet()) {
final String key = entry.getKey();
@@ -675,7 +678,9 @@
}
}
final SetSchemaResponse response = new SetSchemaResponse.Builder().build();
- callback.onResult(AppSearchResult.newSuccessfulResult(response.getBundle()));
+ callback.onResult(
+ new AppSearchResultParcel(
+ AppSearchResult.newSuccessfulResult(response.getBundle())));
}
@Override
@@ -711,12 +716,13 @@
}
docMap.put(doc.getId(), doc);
}
- callback.onResult(builder.build());
+ callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
}
@Override
public void getDocuments(String packageName, String databaseName, String namespace,
List<String> ids, Map<String, List<String>> typePropertyPaths, int userId,
+ long binderCallStartTimeMillis,
IAppSearchBatchResultCallback callback) throws RemoteException {
final AppSearchBatchResult.Builder<String, Bundle> builder =
new AppSearchBatchResult.Builder<>();
@@ -737,19 +743,21 @@
}
}
}
- callback.onResult(builder.build());
+ callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
}
@Override
public void query(String packageName, String databaseName, String queryExpression,
- Bundle searchSpecBundle, int userId, IAppSearchResultCallback callback)
+ Bundle searchSpecBundle, int userId, long binderCallStartTimeMillis,
+ IAppSearchResultCallback callback)
throws RemoteException {
final String key = getKey(userId, databaseName);
if (!mDocumentMap.containsKey(key)) {
final Bundle page = new Bundle();
page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, new ArrayList<>());
- callback.onResult(AppSearchResult.newSuccessfulResult(page));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
return;
}
final List<GenericDocument> documents = new ArrayList<>(mDocumentMap.get(key).values());
@@ -765,12 +773,14 @@
resultBundles.add(resultBundle);
}
page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
- callback.onResult(AppSearchResult.newSuccessfulResult(page));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
}
@Override
public void globalQuery(String packageName, String queryExpression, Bundle searchSpecBundle,
- int userId, IAppSearchResultCallback callback) throws RemoteException {
+ int userId, long binderCallStartTimeMillis, IAppSearchResultCallback callback)
+ throws RemoteException {
ignore(callback);
}
@@ -780,7 +790,8 @@
final Bundle page = new Bundle();
page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, new ArrayList<>());
- callback.onResult(AppSearchResult.newSuccessfulResult(page));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
}
@Override
@@ -813,7 +824,8 @@
@Override
public void removeByDocumentId(String packageName, String databaseName, String namespace,
- List<String> ids, int userId, IAppSearchBatchResultCallback callback)
+ List<String> ids, int userId, long binderCallStartTimeMillis,
+ IAppSearchBatchResultCallback callback)
throws RemoteException {
final AppSearchBatchResult.Builder<String, Void> builder =
new AppSearchBatchResult.Builder<>();
@@ -835,20 +847,23 @@
}
}
}
- callback.onResult(builder.build());
+ callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
}
@Override
public void removeByQuery(String packageName, String databaseName, String queryExpression,
- Bundle searchSpecBundle, int userId, IAppSearchResultCallback callback)
+ Bundle searchSpecBundle, int userId, long binderCallStartTimeMillis,
+ IAppSearchResultCallback callback)
throws RemoteException {
final String key = getKey(userId, databaseName);
if (!mDocumentMap.containsKey(key)) {
- callback.onResult(AppSearchResult.newSuccessfulResult(null));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
return;
}
mDocumentMap.get(key).clear();
- callback.onResult(AppSearchResult.newSuccessfulResult(null));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
}
@Override
@@ -858,12 +873,14 @@
}
@Override
- public void persistToDisk(int userId) throws RemoteException {
+ public void persistToDisk(int userId, long binderCallStartTimeMillis)
+ throws RemoteException {
}
@Override
- public void initialize(int userId, IAppSearchResultCallback callback)
+ public void initialize(int userId, long binderCallStartTimeMillis,
+ IAppSearchResultCallback callback)
throws RemoteException {
ignore(callback);
}
@@ -878,7 +895,8 @@
}
private void ignore(IAppSearchResultCallback callback) throws RemoteException {
- callback.onResult(AppSearchResult.newSuccessfulResult(null));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
new file mode 100644
index 0000000..94ac9be
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
+import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
+import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
+import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
+import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
+import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.os.Build;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CompatibilityModeTest {
+
+ private boolean mCompatibilityModeEnabled;;
+ private AndroidPackage mMockAndroidPackage;
+ private PackageUserState mMockUserState;
+
+ @Before
+ public void setUp() {
+ mCompatibilityModeEnabled = PackageParser.sCompatibilityModeEnabled;
+ mMockAndroidPackage = mock(AndroidPackage.class);
+ mMockUserState = mock(PackageUserState.class);
+ mMockUserState.installed = true;
+ when(mMockUserState.isAvailable(anyInt())).thenReturn(true);
+ when(mMockUserState.getAllOverlayPaths()).thenReturn(null);
+ }
+
+ @After
+ public void tearDown() {
+ setGlobalCompatibilityMode(mCompatibilityModeEnabled);
+ }
+
+ // The following tests ensure that apps with target SDK of Cupcake always use compat mode.
+
+ @Test
+ public void testGlobalCompatModeEnabled_oldApp_supportAllScreens_usesCompatMode() {
+ setGlobalCompatibilityMode(true);
+ final int flags = (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS
+ | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
+ | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
+ final ApplicationInfo info =
+ generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
+ assertThat(info.usesCompatibilityMode(), is(true));
+ }
+
+ @Test
+ public void testGlobalCompatModeEnabled_oldApp_supportSomeScreens_usesCompatMode() {
+ setGlobalCompatibilityMode(true);
+ final int flags = (FLAG_SUPPORTS_LARGE_SCREENS
+ | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
+ | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
+ final ApplicationInfo info =
+ generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
+ assertThat(info.usesCompatibilityMode(), is(true));
+ }
+
+ @Test
+ public void testGlobalCompatModeEnabled_oldApp_supportOnlyOneScreen_usesCompatMode() {
+ setGlobalCompatibilityMode(true);
+ final int flags = FLAG_SUPPORTS_NORMAL_SCREENS;
+ final ApplicationInfo info =
+ generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
+ assertThat(info.usesCompatibilityMode(), is(true));
+ }
+
+ @Test
+ public void testGlobalCompatModeEnabled_oldApp_DoesntSupportAllScreens_usesCompatMode() {
+ setGlobalCompatibilityMode(true);
+ final ApplicationInfo info =
+ generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, 0 /*flags*/);
+ assertThat(info.usesCompatibilityMode(), is(true));
+ }
+
+ @Test
+ public void testGlobalCompatModeDisabled_oldApp_supportAllScreens_usesCompatMode() {
+ setGlobalCompatibilityMode(false);
+ final int flags = (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS
+ | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
+ | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
+ final ApplicationInfo info =
+ generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
+ assertThat(info.usesCompatibilityMode(), is(true));
+ }
+
+ @Test
+ public void testGlobalCompatModeDisabled_oldApp_supportSomeScreens_usesCompatMode() {
+ setGlobalCompatibilityMode(false);
+ final int flags = (FLAG_SUPPORTS_LARGE_SCREENS
+ | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
+ | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
+ final ApplicationInfo info =
+ generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
+ assertThat(info.usesCompatibilityMode(), is(true));
+ }
+
+ @Test
+ public void testGlobalCompatModeDisabled_oldApp_supportOnlyOneScreen_usesCompatMode() {
+ setGlobalCompatibilityMode(false);
+ final int flags = FLAG_SUPPORTS_NORMAL_SCREENS;
+ final ApplicationInfo info =
+ generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
+ assertThat(info.usesCompatibilityMode(), is(true));
+ }
+
+ @Test
+ public void testGlobalCompatModeDisabled_oldApp_doesntSupportAllScreens_usesCompatMode() {
+ setGlobalCompatibilityMode(false);
+ final ApplicationInfo info =
+ generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, 0 /*flags*/);
+ assertThat(info.usesCompatibilityMode(), is(true));
+ }
+
+ // The following tests ensure that apps with newer target SDK use compat mode as expected.
+
+ @Test
+ public void testGlobalCompatModeEnabled_newApp_supportAllScreens_doesntUseCompatMode() {
+ setGlobalCompatibilityMode(true);
+ final int flags = (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS
+ | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
+ | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
+ final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
+ assertThat(info.usesCompatibilityMode(), is(false));
+ }
+
+ @Test
+ public void testGlobalCompatModeEnabled_newApp_supportSomeScreens_doesntUseCompatMode() {
+ setGlobalCompatibilityMode(true);
+ final int flags = (FLAG_SUPPORTS_LARGE_SCREENS
+ | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
+ | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
+ final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
+ assertThat(info.usesCompatibilityMode(), is(false));
+ }
+
+ @Test
+ public void testGlobalCompatModeEnabled_newApp_supportOnlyOneScreen_doesntUseCompatMode() {
+ setGlobalCompatibilityMode(true);
+ final int flags = FLAG_SUPPORTS_NORMAL_SCREENS;
+ final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
+ assertThat(info.usesCompatibilityMode(), is(false));
+ }
+
+ @Test
+ public void testGlobalCompatModeEnabled_newApp_doesntSupportAllScreens_usesCompatMode() {
+ setGlobalCompatibilityMode(true);
+ final ApplicationInfo info =
+ generateMockApplicationInfo(Build.VERSION_CODES.DONUT, 0 /*flags*/);
+ assertThat(info.usesCompatibilityMode(), is(true));
+ }
+
+ @Test
+ public void testGlobalCompatModeDisabled_newApp_supportAllScreens_doesntUseCompatMode() {
+ setGlobalCompatibilityMode(false);
+ final int flags = (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS
+ | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
+ | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
+ final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
+ assertThat(info.usesCompatibilityMode(), is(false));
+ }
+
+ @Test
+ public void testGlobalCompatModeDisabled_newApp_supportSomeScreens_doesntUseCompatMode() {
+ setGlobalCompatibilityMode(false);
+ final int flags = (FLAG_SUPPORTS_LARGE_SCREENS
+ | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
+ | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
+ final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
+ assertThat(info.usesCompatibilityMode(), is(false));
+ }
+
+ @Test
+ public void testGlobalCompatModeDisabled_newApp_supportOnlyOneScreen_doesntUseCompatMode() {
+ setGlobalCompatibilityMode(false);
+ final int flags = FLAG_SUPPORTS_NORMAL_SCREENS;
+ final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
+ assertThat(info.usesCompatibilityMode(), is(false));
+ }
+
+ @Test
+ public void testGlobalCompatModeDisabled_newApp_doesntSupportAllScreens_doesntUseCompatMode() {
+ setGlobalCompatibilityMode(false);
+ final ApplicationInfo info =
+ generateMockApplicationInfo(Build.VERSION_CODES.DONUT, 0 /*flags*/);
+ assertThat(info.usesCompatibilityMode(), is(false));
+ }
+
+ private ApplicationInfo generateMockApplicationInfo(int targetSdkVersion, int flags) {
+ final ApplicationInfo info = new ApplicationInfo();
+ info.targetSdkVersion = targetSdkVersion;
+ info.flags |= flags;
+ when(mMockAndroidPackage.toAppInfoWithoutState()).thenReturn(info);
+ return PackageInfoWithoutStateUtils.generateApplicationInfoUnchecked(mMockAndroidPackage,
+ 0 /*flags*/, mMockUserState, 0 /*userId*/, false /*assignUserFields*/);
+ }
+
+ private void setGlobalCompatibilityMode(boolean enabled) {
+ if (PackageParser.sCompatibilityModeEnabled == enabled) {
+ return;
+ }
+ PackageParser.setCompatibilityModeEnabled(enabled);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 709b009..1b6bddc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -25,6 +25,7 @@
import android.util.LongSparseArray;
import com.android.internal.util.ArrayUtils;
+import com.android.server.utils.WatchedArrayMap;
import java.io.File;
import java.io.IOException;
@@ -33,7 +34,7 @@
public class KeySetManagerServiceTest extends AndroidTestCase {
- private ArrayMap<String, PackageSetting> mPackagesMap;
+ private WatchedArrayMap<String, PackageSetting> mPackagesMap;
private KeySetManagerService mKsms;
public PackageSetting generateFakePackageSetting(String name) {
@@ -46,7 +47,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mPackagesMap = new ArrayMap<String, PackageSetting>();
+ mPackagesMap = new WatchedArrayMap<String, PackageSetting>();
mKsms = new KeySetManagerService(mPackagesMap);
}
@@ -94,7 +95,8 @@
}
public void testEncodePublicKey() throws IOException {
- ArrayMap<String, PackageSetting> packagesMap = new ArrayMap<String, PackageSetting>();
+ WatchedArrayMap<String, PackageSetting> packagesMap =
+ new WatchedArrayMap<String, PackageSetting>();
KeySetManagerService ksms = new KeySetManagerService(packagesMap);
PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index a231169..29f4aa9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -47,7 +47,6 @@
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
@@ -64,6 +63,7 @@
import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.utils.WatchableTester;
+import com.android.server.utils.WatchedArrayMap;
import com.google.common.truth.Truth;
@@ -1202,9 +1202,8 @@
private void verifyKeySetMetaData(Settings settings)
throws ReflectiveOperationException, IllegalAccessException {
- ArrayMap<String, PackageSetting> packages =
- settings.mPackages.untrackedStorage();
- KeySetManagerService ksms = settings.mKeySetManagerService;
+ WatchedArrayMap<String, PackageSetting> packages = settings.mPackages;
+ KeySetManagerService ksms = settings.getKeySetManagerService();
/* verify keyset and public key ref counts */
assertThat(KeySetUtils.getKeySetRefCount(ksms, 1), is(2));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index 478aa41..9598a00 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -15,20 +15,11 @@
*/
package com.android.server.pm;
-import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
-
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
import android.app.PendingIntent;
-import android.app.appsearch.PackageIdentifier;
-import android.content.pm.AppSearchShortcutInfo;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
-
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-
-import java.util.Random;
/**
* Tests for {@link android.app.appsearch.AppSearchManager} and relevant APIs in ShortcutManager.
@@ -37,26 +28,6 @@
*/
public class ShortcutManagerTest12 extends BaseShortcutManagerTest {
- public void testUpdateShortcutVisibility_updatesShortcutSchema() {
- if (!DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.DARK_LAUNCH_REMOTE_PREDICTION_SERVICE_ENABLED,
- false)) {
- // no-op if app-search integration is disabled.
- return;
- }
- final byte[] cert = new byte[20];
- new Random().nextBytes(cert);
-
- runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- mManager.updateShortcutVisibility(CALLING_PACKAGE_2, cert, true);
- assertTrue(mMockAppSearchManager.mSchemasPackageAccessible.containsKey(
- AppSearchShortcutInfo.SCHEMA_TYPE));
- assertTrue(mMockAppSearchManager.mSchemasPackageAccessible.get(
- AppSearchShortcutInfo.SCHEMA_TYPE).get(0).equals(
- new PackageIdentifier(CALLING_PACKAGE_2, cert)));
- });
- }
-
public void testGetShortcutIntents_ReturnsMutablePendingIntents() throws RemoteException {
setDefaultLauncher(USER_0, LAUNCHER_1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 57436f8..581ff54 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -509,7 +509,13 @@
.ignored("Checked separately in test")}
reqFeatures=${this.reqFeatures?.joinToString { it.dumpToString() }}
requestedPermissions=${this.requestedPermissions?.contentToString()}
- requestedPermissionsFlags=${this.requestedPermissionsFlags?.contentToString()}
+ requestedPermissionsFlags=${
+ this.requestedPermissionsFlags?.map {
+ // Newer flags are stripped
+ it and (PackageInfo.REQUESTED_PERMISSION_REQUIRED
+ or PackageInfo.REQUESTED_PERMISSION_GRANTED)
+ }?.joinToString()
+ }
requiredAccountType=${this.requiredAccountType}
requiredForAllUsers=${this.requiredForAllUsers}
restrictedAccountType=${this.restrictedAccountType}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index 9001b3d..443476c 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -48,7 +48,7 @@
private static final float PRECISION = 0.001f;
private static final int GPS_MODE = 0; // LOCATION_MODE_NO_CHANGE
private static final int DEFAULT_GPS_MODE =
- PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
+ PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
private static final int SOUND_TRIGGER_MODE = 0; // SOUND_TRIGGER_MODE_ALL_ENABLED
private static final int DEFAULT_SOUND_TRIGGER_MODE =
PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY;
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS b/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS
new file mode 100644
index 0000000..08276f5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/power/batterysaver/OWNERS
\ No newline at end of file
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 c502ed5..825e53e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -100,6 +100,7 @@
private String mVersionString;
private final Set<ComponentName> mDefaults = new ArraySet();
private ManagedServices mService;
+ private String mUserSetString;
private static final String SETTING = "setting";
private static final String SECONDARY_SETTING = "secondary_setting";
@@ -138,7 +139,7 @@
profileIds.add(13);
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
- mVersionString = "2";
+ mVersionString = "4";
mExpectedPrimary = new ArrayMap<>();
mExpectedSecondary = new ArrayMap<>();
mExpectedPrimaryPackages = new ArrayMap<>();
@@ -1363,6 +1364,7 @@
public void loadDefaults_noVersionWithDefaults() throws Exception {
resetComponentsAndPackages();
mDefaults.add(new ComponentName("default", "class"));
+ mVersionString = null;
loadXml(mService);
assertEquals(mService.getDefaultComponents(), mDefaults);
}
@@ -1421,12 +1423,34 @@
resetComponentsAndPackages();
mDefaults.add(new ComponentName("default", "class"));
mDefaultsString = "xml/class";
- mVersionString = String.valueOf(mService.DB_VERSION);
loadXml(mService);
assertEquals(mService.getDefaultComponents(),
new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
}
+ @Test
+ public void upgradeUserSet_versionThree() throws Exception {
+ resetComponentsAndPackages();
+
+ List<UserInfo> users = new ArrayList<>();
+ users.add(new UserInfo(98, "98", 0));
+ users.add(new UserInfo(99, "99", 0));
+ for (UserInfo user : users) {
+ when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
+ }
+
+ mDefaultsString = "xml/class";
+ mVersionString = "3";
+ mUserSetString = "xml/class";
+ loadXml(mService);
+
+ //Test services without overriding upgradeUserSet() remain unchanged
+ assertEquals(new ArraySet(Arrays.asList(mUserSetString)),
+ mService.mUserSetServices.get(98));
+ assertEquals(new ArraySet(Arrays.asList(mUserSetString)),
+ mService.mUserSetServices.get(99));
+ assertEquals(new ArrayMap(), mService.mIsUserChanged);
+ }
private void resetComponentsAndPackages() {
ArrayMap<Integer, ArrayMap<Integer, String>> empty = new ArrayMap(1);
@@ -1468,11 +1492,17 @@
xml.append("<" + ManagedServices.TAG_MANAGED_SERVICES + " "
+ ManagedServices.ATT_USER_ID + "=\"99\" "
+ ManagedServices.ATT_IS_PRIMARY + "=\"true\" "
- + ManagedServices.ATT_APPROVED_LIST + "=\"990\" />\n");
+ + ManagedServices.ATT_APPROVED_LIST + "=\"990\" "
+ + (mUserSetString != null ? ManagedServices.ATT_USER_SET + "=\""
+ + mUserSetString + "\" " : "")
+ + "/>\n");
xml.append("<" + ManagedServices.TAG_MANAGED_SERVICES + " "
+ ManagedServices.ATT_USER_ID + "=\"98\" "
+ ManagedServices.ATT_IS_PRIMARY + "=\"false\" "
- + ManagedServices.ATT_APPROVED_LIST + "=\"981\" />\n");
+ + ManagedServices.ATT_APPROVED_LIST + "=\"981\" "
+ + (mUserSetString != null ? ManagedServices.ATT_USER_SET + "=\""
+ + mUserSetString + "\" " : "")
+ + " />\n");
xml.append("</" + xmlTag + ">");
return xml.toString();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 6722fff..054a401 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
@@ -41,11 +42,13 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.testing.TestableContext;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.TypedXmlPullParser;
import android.util.Xml;
+import com.android.internal.util.function.TriPredicate;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
@@ -132,6 +135,68 @@
}
@Test
+ public void testReadXml_userDisabled() throws Exception {
+ String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
+ + "<service_listing approved=\"\" user=\"0\" primary=\"true\""
+ + "user_changed=\"true\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ TriPredicate<String, Integer, String> allowedManagedServicePackages =
+ mNm::canUseManagedServices;
+
+ parser.nextTag();
+ mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+ ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(0);
+
+ // approved should not be null
+ assertNotNull(approved);
+ assertEquals(new ArraySet<>(), approved.get(true));
+ }
+
+ @Test
+ public void testReadXml_upgradeUserSet() throws Exception {
+ String xml = "<enabled_assistants version=\"3\" defaults=\"b/b\">"
+ + "<service_listing approved=\"\" user=\"0\" primary=\"true\""
+ + "user_set_services=\"b/b\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ TriPredicate<String, Integer, String> allowedManagedServicePackages =
+ mNm::canUseManagedServices;
+
+ parser.nextTag();
+ mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+ verify(mAssistants, times(1)).upgradeUserSet();
+ assertTrue(mAssistants.mIsUserChanged.get(0));
+ }
+
+ @Test
+ public void testReadXml_multiApproved() throws Exception {
+ String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
+ + "<service_listing approved=\"a/a:b/b\" user=\"0\" primary=\"true\""
+ + "user_changed=\"true\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+
+ parser.nextTag();
+ mAssistants.readXml(parser, null, false, UserHandle.USER_ALL);
+
+ assertEquals(1, mAssistants.getAllowedComponents(0).size());
+ assertEquals(new ArrayList(Arrays.asList(new ComponentName("a", "a"))),
+ mAssistants.getAllowedComponents(0));
+ }
+
+ @Test
public void testXmlUpgradeExistingApprovedComponents() throws Exception {
String xml = "<enabled_assistants version=\"2\" defaults=\"b\\b\">"
+ "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index ab54526..50ebffc31 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -197,7 +197,7 @@
si.packageName = "new";
si.name = "comp";
si.metaData = new Bundle();
- si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1,2");
+ si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1|2");
mListeners.ensureFilters(si, 0);
@@ -213,7 +213,7 @@
si.name = "comp";
si.metaData = new Bundle();
si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES,
- "conversations,ALERTING");
+ "conversations|ALERTING");
mListeners.ensureFilters(si, 0);
@@ -243,7 +243,7 @@
si.packageName = "new";
si.name = "comp";
si.metaData = new Bundle();
- si.metaData.putString(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, "1,2");
+ si.metaData.putString(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, "1|2");
mListeners.ensureFilters(si, 0);
@@ -259,7 +259,7 @@
si.name = "comp";
si.metaData = new Bundle();
si.metaData.putString(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES,
- "1,alerting");
+ "1|alerting");
mListeners.ensureFilters(si, 0);
@@ -274,7 +274,7 @@
si.packageName = "new";
si.name = "comp";
si.metaData = new Bundle();
- si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1,2");
+ si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1|2");
si.metaData.putInt(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, 1);
mListeners.ensureFilters(si, 0);
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index de4698d..985b2d5 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -41,6 +41,7 @@
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
+ <uses-permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT"/>
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
@@ -75,6 +76,7 @@
<activity android:name="com.android.server.wm.ActivityOptionsTest$MainActivity"
android:turnScreenOn="true"
android:showWhenLocked="true" />
+ <activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity" />
</application>
<instrumentation
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index ddaf3ab..9a89626 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2034,20 +2034,29 @@
.setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
.build();
- // Non-resizable
+ // Not allow non-resizable
mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = false;
assertFalse(activity.supportsSplitScreenWindowingMode());
// Force resizable
mAtm.mForceResizableActivities = true;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = false;
assertTrue(activity.supportsSplitScreenWindowingMode());
- // Allow non-resizable
+ // Use development option to allow non-resizable
mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = true;
assertTrue(activity.supportsSplitScreenWindowingMode());
+
+ // Always allow non-resizable
+ mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = 1;
+ mAtm.mDevEnableNonResizableMultiWindow = false;
+ assertTrue(activity.supportsSplitScreenWindowingMode());
}
@Test
@@ -2058,20 +2067,29 @@
.setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
.build();
- // Non-resizable
+ // Not allow non-resizable
mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = false;
assertFalse(activity.supportsFreeform());
// Force resizable
mAtm.mForceResizableActivities = true;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = false;
assertTrue(activity.supportsFreeform());
- // Allow non-resizable
+ // Use development option to allow non-resizable
mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = true;
assertTrue(activity.supportsFreeform());
+
+ // Always allow non-resizable
+ mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = 1;
+ mAtm.mDevEnableNonResizableMultiWindow = false;
+ assertTrue(activity.supportsFreeform());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 618de21..2558259 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -320,8 +320,8 @@
.build();
final Task task = activity.getTask();
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
}
@Test
@@ -336,14 +336,14 @@
// Device config as not support.
mAtm.mSupportsNonResizableMultiWindow = -1;
- assertFalse(activity.supportsMultiWindow2());
- assertFalse(task.supportsMultiWindow2());
+ assertFalse(activity.supportsMultiWindow());
+ assertFalse(task.supportsMultiWindow());
// Device config as always support.
mAtm.mSupportsNonResizableMultiWindow = 1;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
// The default config is relying on the screen size.
mAtm.mSupportsNonResizableMultiWindow = 0;
@@ -351,14 +351,14 @@
// Supports on large screen.
tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
// Not supports on small screen.
tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp - 1;
- assertFalse(activity.supportsMultiWindow2());
- assertFalse(task.supportsMultiWindow2());
+ assertFalse(activity.supportsMultiWindow());
+ assertFalse(task.supportsMultiWindow());
}
@Test
@@ -381,14 +381,14 @@
// Ignore the activity min width/height for determine multi window eligibility.
mAtm.mRespectsActivityMinWidthHeightMultiWindow = -1;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
// Always check the activity min width/height.
mAtm.mRespectsActivityMinWidthHeightMultiWindow = 1;
- assertFalse(activity.supportsMultiWindow2());
- assertFalse(task.supportsMultiWindow2());
+ assertFalse(activity.supportsMultiWindow());
+ assertFalse(task.supportsMultiWindow());
// The default config is relying on the screen size.
mAtm.mRespectsActivityMinWidthHeightMultiWindow = 0;
@@ -396,14 +396,14 @@
// Ignore on large screen.
tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
// Check on small screen.
tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp - 1;
- assertFalse(activity.supportsMultiWindow2());
- assertFalse(task.supportsMultiWindow2());
+ assertFalse(activity.supportsMultiWindow());
+ assertFalse(task.supportsMultiWindow());
}
@Test
@@ -429,14 +429,14 @@
// Always check the activity min width/height.
mAtm.mSupportsNonResizableMultiWindow = 1;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
// The default config is relying on the screen size. Check for small screen
mAtm.mSupportsNonResizableMultiWindow = 0;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index e09606e..6d4454b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -349,7 +349,9 @@
doReturn(imeSurfaceParent).when(mDisplayContent).computeImeParent();
spyOn(imeContainer);
- mDisplayContent.updateImeParent();
+ mDisplayContent.setImeInputTarget(startingWin);
+ mDisplayContent.onConfigurationChanged(new Configuration());
+ verify(mDisplayContent).updateImeParent();
// Force reassign the relative layer when the IME surface parent is changed.
verify(imeContainer).assignRelativeLayer(any(), eq(imeSurfaceParent), anyInt(), eq(true));
@@ -1275,6 +1277,7 @@
// Assume that the display rotation is changed so it is frozen in preparation for animation.
doReturn(true).when(rotationAnim).hasScreenshot();
mWm.mDisplayFrozen = true;
+ displayContent.getDisplayRotation().setRotation((displayContent.getRotation() + 1) % 4);
displayContent.setRotationAnimation(rotationAnim);
// The fade rotation animation also starts to hide some non-app windows.
assertNotNull(displayContent.getFadeRotationAnimationController());
@@ -1904,7 +1907,7 @@
mDisplayContent.setImeInputTarget(appWin2);
mDisplayContent.computeImeTarget(true);
assertEquals(appWin2, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
- assertTrue(mDisplayContent.isImeAttachedToApp());
+ assertTrue(mDisplayContent.shouldImeAttachedToApp());
verify(mDisplayContent, atLeast(1)).attachAndShowImeScreenshotOnTarget();
verify(mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(appWin1.getTask());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 1bddd7b..c47ffcf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -239,6 +239,35 @@
}
@Test
+ public void testInterceptGlobalDragDropIgnoresOtherWindows() {
+ WindowState globalInterceptWindow = createDropTargetWindow("Global drag test window", 0);
+ globalInterceptWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
+
+ // Necessary for now since DragState.sendDragStartedLocked() will recycle drag events
+ // immediately after dispatching, which is a problem when using mockito arguments captor
+ // because it returns and modifies the same drag event
+ TestIWindow iwindow = (TestIWindow) mWindow.mClient;
+ final ArrayList<DragEvent> dragEvents = new ArrayList<>();
+ iwindow.setDragEventJournal(dragEvents);
+ TestIWindow globalInterceptIWindow = (TestIWindow) globalInterceptWindow.mClient;
+ final ArrayList<DragEvent> globalInterceptWindowDragEvents = new ArrayList<>();
+ globalInterceptIWindow.setDragEventJournal(globalInterceptWindowDragEvents);
+
+ startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ,
+ createClipDataForActivity(null, mock(UserHandle.class)), () -> {
+ // Verify the start-drag event is sent for the intercept window but not the
+ // other window
+ assertTrue(dragEvents.isEmpty());
+ assertTrue(globalInterceptWindowDragEvents.get(0).getAction()
+ == ACTION_DRAG_STARTED);
+
+ mTarget.reportDropWindow(globalInterceptWindow.mInputChannelToken, 0, 0);
+ mTarget.handleMotionEvent(false, 0, 0);
+ mToken = globalInterceptWindow.mClient.asBinder();
+ });
+ }
+
+ @Test
public void testValidateAppActivityArguments() {
final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
index 325bca4..1ee646c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
@@ -77,12 +77,12 @@
assertNotNull("Window state is created", appWindow);
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority doesn't change.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
// Call the function a few times.
appWindow.updateFrameRateSelectionPriorityIfNeeded();
@@ -92,7 +92,7 @@
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
any(SurfaceControl.class), anyInt());
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
- any(SurfaceControl.class), anyInt(), anyInt());
+ any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
}
@Test
@@ -101,16 +101,16 @@
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
.getPreferredModeId(appWindow), 0);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
.getPreferredRefreshRate(appWindow), 0, FLOAT_TOLERANCE);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority stays MAX_VALUE.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
@@ -119,38 +119,38 @@
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes to 1.
assertEquals(appWindow.mFrameRateSelectionPriority, 1);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 1);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
- any(SurfaceControl.class), anyInt(), anyInt());
+ any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
}
@Test
public void testApplicationInFocusWithModeId() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
// Application is in focus.
appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 1);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
// Update the mode ID to a requested number.
appWindow.mAttrs.preferredDisplayModeId = 1;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 0);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 60, FLOAT_TOLERANCE);
// Remove the mode ID request.
appWindow.mAttrs.preferredDisplayModeId = 0;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 1);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
// Verify we called actions on Transactions correctly.
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
@@ -160,14 +160,14 @@
verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 1);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
- any(SurfaceControl.class), anyInt(), anyInt());
+ any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
}
@Test
public void testApplicationNotInFocusWithModeId() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
@@ -175,28 +175,28 @@
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// The window is not in focus.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
// Update the mode ID to a requested number.
appWindow.mAttrs.preferredDisplayModeId = 1;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 2);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 60, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 2);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
- any(SurfaceControl.class), anyInt(), anyInt());
+ any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
}
@Test
public void testApplicationNotInFocusWithoutModeId() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
@@ -204,19 +204,19 @@
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// The window is not in focus.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
// Make sure that the mode ID is not set.
appWindow.mAttrs.preferredDisplayModeId = 0;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority doesn't change.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
- assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
- any(SurfaceControl.class), anyInt(), anyInt());
+ any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
}
@Test
@@ -233,7 +233,7 @@
appWindow.updateFrameRateSelectionPriorityIfNeeded();
assertEquals(RefreshRatePolicy.LAYER_PRIORITY_UNSET, appWindow.mFrameRateSelectionPriority);
- assertEquals(60, appWindow.mDenyListFrameRate, FLOAT_TOLERANCE);
+ assertEquals(60, appWindow.mAppPreferredFrameRate, FLOAT_TOLERANCE);
// Call the function a few times.
appWindow.updateFrameRateSelectionPriorityIfNeeded();
@@ -243,6 +243,7 @@
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
any(SurfaceControl.class), anyInt());
verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
- appWindow.getSurfaceControl(), 60, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
+ appWindow.getSurfaceControl(), 60,
+ Surface.FRAME_RATE_COMPATIBILITY_EXACT, Surface.CHANGE_FRAME_RATE_ALWAYS);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 23d57b8..3082a5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -516,20 +516,15 @@
final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
- mDefaultDisplay.mDisplayId, false);
+ eq(mDefaultDisplay.mDisplayId), eq(false));
verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE);
- final WindowContainer parent = navToken.getParent();
- final NavBarFadeAnimationController navBarFadeAnimationController =
- mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
-
mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
+ verify(mController).restoreNavigationBarFromApp(eq(true));
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
- mDefaultDisplay.mDisplayId, true);
+ eq(mDefaultDisplay.mDisplayId), eq(true));
verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
- verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
- verify(navBarFadeAnimationController).fadeWindowToken(true);
}
@Test
@@ -543,20 +538,18 @@
final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
- mDefaultDisplay.mDisplayId, false);
+ eq(mDefaultDisplay.mDisplayId), eq(false));
verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE);
final WindowContainer parent = navToken.getParent();
- final NavBarFadeAnimationController navBarFadeAnimationController =
- mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ verify(mController).restoreNavigationBarFromApp(eq(false));
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
- mDefaultDisplay.mDisplayId, true);
+ eq(mDefaultDisplay.mDisplayId), eq(true));
verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
- verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
}
@Test
@@ -571,39 +564,6 @@
}
@Test
- public void testFadeRotationAfterAttachAndBeforeRestore_notRestoreNavImmediately() {
- setupForShouldAttachNavBarDuringTransition();
- final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
- final ActivityRecord homeActivity = createHomeActivity();
- initializeRecentsAnimationController(mController, homeActivity);
-
- final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken;
- final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
-
- verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
- mDefaultDisplay.mDisplayId, false);
- verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
- verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE);
-
- final WindowContainer parent = navToken.getParent();
- final NavBarFadeAnimationController navBarFadeAnimationController =
- mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
-
- FadeRotationAnimationController mockController =
- mock(FadeRotationAnimationController.class);
- doReturn(mockController).when(mDefaultDisplay).getFadeRotationAnimationController();
-
- mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
- verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
- mDefaultDisplay.mDisplayId, true);
- verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
- verify(mockController).setOnShowRunnable(any());
- verify(transaction, times(0)).reparent(navToken.getSurfaceControl(),
- parent.getSurfaceControl());
- verify(navBarFadeAnimationController, times(0)).fadeWindowToken(true);
- }
-
- @Test
public void testAttachNavBarInSplitScreenMode() {
setupForShouldAttachNavBarDuringTransition();
final ActivityRecord primary = createActivityRecordWithParentTask(mDefaultDisplay,
@@ -619,20 +579,18 @@
final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
- mDefaultDisplay.mDisplayId, false);
+ eq(mDefaultDisplay.mDisplayId), eq(false));
verify(navWindow).setSurfaceTranslationY(-secondary.getBounds().top);
verify(transaction).reparent(navToken.getSurfaceControl(), secondary.getSurfaceControl());
reset(navWindow);
mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
final WindowContainer parent = navToken.getParent();
- final NavBarFadeAnimationController navBarFadeAnimationController =
- mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
- mDefaultDisplay.mDisplayId, true);
+ eq(mDefaultDisplay.mDisplayId), eq(true));
verify(navWindow).setSurfaceTranslationY(0);
verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
- verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
+ verify(mController).restoreNavigationBarFromApp(eq(false));
}
@Test
@@ -696,12 +654,8 @@
mDefaultDisplay.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
mWm.setRecentsAnimationController(mController);
doReturn(navBar).when(mController).getNavigationBarWindow();
- final NavBarFadeAnimationController mockNavBarFadeAnimationController =
- mock(NavBarFadeAnimationController.class);
final DisplayPolicy displayPolicy = spy(mDefaultDisplay.getDisplayPolicy());
doReturn(displayPolicy).when(mDefaultDisplay).getDisplayPolicy();
- doReturn(mockNavBarFadeAnimationController).when(displayPolicy)
- .getNavBarFadeAnimationController();
}
private static void initializeRecentsAnimationController(RecentsAnimationController controller,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index ef3c7ae..20b987d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -71,9 +71,11 @@
cameraUsingWindow.mAttrs.packageName = "com.android.test";
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
mPolicy.addNonHighRefreshRatePackage("com.android.test");
- assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(60, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
mPolicy.removeNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
@@ -109,6 +111,7 @@
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@Test
@@ -123,6 +126,7 @@
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@Test
@@ -132,13 +136,31 @@
cameraUsingWindow.mAttrs.packageName = "com.android.test";
mPolicy.addNonHighRefreshRatePackage("com.android.test");
- assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(60, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
cameraUsingWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
cameraUsingWindow.getPendingTransaction(), mock(AnimationAdapter.class),
false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testAppMaxRefreshRate() {
+ final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, "window");
+ window.mAttrs.preferredMaxDisplayRefreshRate = 60f;
+ assertEquals(0, mPolicy.getPreferredModeId(window));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(window), FLOAT_TOLERANCE);
+ assertEquals(60, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE);
+
+ window.mActivityRecord.mSurfaceAnimator.startAnimation(
+ window.getPendingTransaction(), mock(AnimationAdapter.class),
+ false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
+ assertEquals(0, mPolicy.getPreferredModeId(window));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(window), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index e280a36..0c6545c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -586,38 +586,30 @@
@Test
public void testShouldBeVisible_SplitScreen() {
- final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- // Home root task should always be fullscreen for this test.
- doReturn(false).when(homeRootTask).supportsSplitScreenWindowingMode();
+ // task not supporting split should be fullscreen for this test.
+ final Task notSupportingSplitTask = createTaskForShouldBeVisibleTest(
+ mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ doReturn(false).when(notSupportingSplitTask).supportsSplitScreenWindowingMode();
final Task splitScreenPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final Task splitScreenSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- // Home root task shouldn't be visible if both halves of split-screen are opaque.
+ // root task not supporting split shouldn't be visible if both halves of split-screen are
+ // opaque.
doReturn(false).when(splitScreenPrimary).isTranslucent(any());
doReturn(false).when(splitScreenSecondary).isTranslucent(any());
- assertFalse(homeRootTask.shouldBeVisible(null /* starting */));
+ assertFalse(notSupportingSplitTask.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
- splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
- splitScreenSecondary.getVisibility(null /* starting */));
- // Home root task should be visible if one of the halves of split-screen is translucent.
+ // root task not supporting split shouldn't be visible if one of the halves of split-screen
+ // is translucent.
doReturn(true).when(splitScreenPrimary).isTranslucent(any());
- assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
+ assertFalse(notSupportingSplitTask.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- homeRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
- splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
- splitScreenSecondary.getVisibility(null /* starting */));
final Task splitScreenSecondary2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 7614579..9267285 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -560,7 +560,7 @@
// Verify the target task should resume its activity.
verify(rootTask, times(1)).resumeTopActivityUncheckedLocked(
- eq(activity), eq(null /* targetOptions */));
+ eq(activity), eq(null /* targetOptions */), eq(false));
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
new file mode 100644
index 0000000..f542e29
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+import android.view.PointerIcon;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Build/Install/Run:
+ * atest WmTests:ScreenshotTests
+ */
+@SmallTest
+@Presubmit
+public class ScreenshotTests {
+ private static final int BUFFER_WIDTH = 100;
+ private static final int BUFFER_HEIGHT = 100;
+
+ private final Instrumentation mInstrumentation = getInstrumentation();
+
+ @Rule
+ public ActivityTestRule<ScreenshotActivity> mActivityRule =
+ new ActivityTestRule<>(ScreenshotActivity.class);
+
+ private ScreenshotActivity mActivity;
+
+ @Before
+ public void setup() {
+ mActivity = mActivityRule.getActivity();
+ mInstrumentation.waitForIdleSync();
+ }
+
+ @Test
+ public void testScreenshotSecureLayers() {
+ SurfaceControl secureSC = new SurfaceControl.Builder()
+ .setName("SecureChildSurfaceControl")
+ .setBLASTLayer()
+ .setCallsite("makeSecureSurfaceControl")
+ .setSecure(true)
+ .build();
+
+ SurfaceControl.Transaction t = mActivity.addChildSc(secureSC);
+ mInstrumentation.waitForIdleSync();
+
+ GraphicBuffer buffer = GraphicBuffer.create(BUFFER_WIDTH, BUFFER_HEIGHT,
+ PixelFormat.RGBA_8888,
+ GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
+ | GraphicBuffer.USAGE_SW_WRITE_RARELY);
+
+ Canvas canvas = buffer.lockCanvas();
+ canvas.drawColor(Color.RED);
+ buffer.unlockCanvasAndPost(canvas);
+
+ t.show(secureSC)
+ .setBuffer(secureSC, buffer)
+ .setColorSpace(secureSC, ColorSpace.get(ColorSpace.Named.SRGB))
+ .apply(true);
+
+ SurfaceControl.LayerCaptureArgs args = new SurfaceControl.LayerCaptureArgs.Builder(secureSC)
+ .setCaptureSecureLayers(true)
+ .setChildrenOnly(false)
+ .build();
+ SurfaceControl.ScreenshotHardwareBuffer hardwareBuffer = SurfaceControl.captureLayers(args);
+ assertNotNull(hardwareBuffer);
+
+ Bitmap screenshot = hardwareBuffer.asBitmap();
+ assertNotNull(screenshot);
+
+ Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
+ screenshot.recycle();
+
+ int numMatchingPixels = PixelChecker.getNumMatchingPixels(swBitmap,
+ new PixelColor(PixelColor.RED));
+ long sizeOfBitmap = swBitmap.getWidth() * swBitmap.getHeight();
+ boolean success = numMatchingPixels == sizeOfBitmap;
+ swBitmap.recycle();
+
+ assertTrue(success);
+ }
+
+ public static class ScreenshotActivity extends Activity {
+ private static final long WAIT_TIMEOUT_S = 5;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().getDecorView().setPointerIcon(
+ PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+ SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ mHandler.post(() -> {
+ t.merge(getWindow().getRootSurfaceControl().buildReparentTransaction(
+ surfaceControl));
+ countDownLatch.countDown();
+ });
+
+ try {
+ countDownLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ }
+ return t;
+ }
+ }
+
+ public abstract static class PixelChecker {
+ static int getNumMatchingPixels(Bitmap bitmap, PixelColor pixelColor) {
+ int numMatchingPixels = 0;
+ for (int x = 0; x < bitmap.getWidth(); x++) {
+ for (int y = 0; y < bitmap.getHeight(); y++) {
+ int color = bitmap.getPixel(x, y);
+ if (matchesColor(pixelColor, color)) {
+ numMatchingPixels++;
+ }
+ }
+ }
+ return numMatchingPixels;
+ }
+
+ static boolean matchesColor(PixelColor expectedColor, int color) {
+ final float red = Color.red(color);
+ final float green = Color.green(color);
+ final float blue = Color.blue(color);
+ final float alpha = Color.alpha(color);
+
+ return alpha <= expectedColor.mMaxAlpha
+ && alpha >= expectedColor.mMinAlpha
+ && red <= expectedColor.mMaxRed
+ && red >= expectedColor.mMinRed
+ && green <= expectedColor.mMaxGreen
+ && green >= expectedColor.mMinGreen
+ && blue <= expectedColor.mMaxBlue
+ && blue >= expectedColor.mMinBlue;
+ }
+ }
+
+ public static class PixelColor {
+ public static final int BLACK = 0xFF000000;
+ public static final int RED = 0xFF0000FF;
+ public static final int GREEN = 0xFF00FF00;
+ public static final int BLUE = 0xFFFF0000;
+ public static final int YELLOW = 0xFF00FFFF;
+ public static final int MAGENTA = 0xFFFF00FF;
+ public static final int WHITE = 0xFFFFFFFF;
+
+ public static final int TRANSPARENT_RED = 0x7F0000FF;
+ public static final int TRANSPARENT_BLUE = 0x7FFF0000;
+ public static final int TRANSPARENT = 0x00000000;
+
+ // Default to black
+ public short mMinAlpha;
+ public short mMaxAlpha;
+ public short mMinRed;
+ public short mMaxRed;
+ public short mMinBlue;
+ public short mMaxBlue;
+ public short mMinGreen;
+ public short mMaxGreen;
+
+ public PixelColor(int color) {
+ short alpha = (short) ((color >> 24) & 0xFF);
+ short blue = (short) ((color >> 16) & 0xFF);
+ short green = (short) ((color >> 8) & 0xFF);
+ short red = (short) (color & 0xFF);
+
+ mMinAlpha = (short) getMinValue(alpha);
+ mMaxAlpha = (short) getMaxValue(alpha);
+ mMinRed = (short) getMinValue(red);
+ mMaxRed = (short) getMaxValue(red);
+ mMinBlue = (short) getMinValue(blue);
+ mMaxBlue = (short) getMaxValue(blue);
+ mMinGreen = (short) getMinValue(green);
+ mMaxGreen = (short) getMaxValue(green);
+ }
+
+ public PixelColor() {
+ this(BLACK);
+ }
+
+ private int getMinValue(short color) {
+ return Math.max(color - 4, 0);
+ }
+
+ private int getMaxValue(short color) {
+ return Math.min(color + 4, 0xFF);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 0925e12..7224a0e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -316,14 +316,14 @@
mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
- assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
+ assertFalse(mActivity.mDisplayContent.shouldImeAttachedToApp());
// Recompute the natural configuration without resolving size compat configuration.
mActivity.clearSizeCompatMode();
mActivity.onConfigurationChanged(mTask.getConfiguration());
// It should keep non-attachable because the resolved bounds will be computed according to
// the aspect ratio that won't match its parent bounds.
- assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
+ assertFalse(mActivity.mDisplayContent.shouldImeAttachedToApp());
// Activity max bounds should be sandboxed since it is letterboxed.
assertActivityMaxBoundsSandboxed();
}
@@ -358,7 +358,7 @@
// Because the aspect ratio of display doesn't exceed the max aspect ratio of activity.
// The activity should still fill its parent container and IME can attach to the activity.
assertTrue(mActivity.matchParentBounds());
- assertTrue(mActivity.mDisplayContent.isImeAttachedToApp());
+ assertTrue(mActivity.mDisplayContent.shouldImeAttachedToApp());
final Rect letterboxInnerBounds = new Rect();
mActivity.getLetterboxInnerBounds(letterboxInnerBounds);
@@ -521,6 +521,7 @@
mActivity.setState(STOPPED, "testSizeCompatMode");
mActivity.mVisibleRequested = false;
+ mActivity.visibleIgnoringKeyguard = false;
mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
// Simulate the display changes orientation.
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 619aee6..b89539c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -231,7 +231,7 @@
@Override
public SurfaceControl.Transaction setFrameRate(SurfaceControl sc, float frameRate,
- int compatibility) {
+ int compatibility, int changeFrameRateStrategy) {
return this;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 61b7002..42ef086 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -488,6 +488,7 @@
mLargeScreenSmallestScreenWidthDp = 600;
mSupportsNonResizableMultiWindow = 0;
mRespectsActivityMinWidthHeightMultiWindow = 0;
+ mForceResizableActivities = false;
doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
// allow background activity starts by default
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 81712c6..e9e2013 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -193,7 +193,7 @@
Task task = mAppWindow.mActivityRecord.getTask();
spyOn(task);
spyOn(mDisplayContent);
- when(task.getDisplayContent().isImeAttachedToApp()).thenReturn(false);
+ when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(false);
// Intentionally set the SurfaceControl of input method window as null.
mDisplayContent.mInputMethodWindow.setSurfaceControl(null);
// Verify no NPE happens when calling createTaskSnapshot.
@@ -213,7 +213,7 @@
spyOn(task);
spyOn(mDisplayContent);
spyOn(mDisplayContent.mInputMethodWindow);
- when(task.getDisplayContent().isImeAttachedToApp()).thenReturn(true);
+ when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(true);
// Intentionally set the IME window is in drawn state.
doReturn(true).when(mDisplayContent.mInputMethodWindow).isDrawn();
// Verify no NPE happens when calling createTaskSnapshot.
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 8c87bef..3f1248a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -1193,13 +1193,13 @@
splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */);
// Can't reparent non-resizable to split screen
- mAtm.mDevEnableNonResizableMultiWindow = false;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mWindowOrganizerController.applyTransaction(wct);
assertEquals(rootTask, activity.getRootTask());
// Allow reparent non-resizable to split screen
- mAtm.mDevEnableNonResizableMultiWindow = true;
+ mAtm.mSupportsNonResizableMultiWindow = 1;
mAtm.mWindowOrganizerController.applyTransaction(wct);
assertEquals(splitPrimaryRootTask, activity.getRootTask());
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index 4198d3b..2b01cdf 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -175,10 +175,9 @@
return;
}
try {
- // TODO: Pipe uiTranslationSpec through to the UiTranslationController.
taskTopActivityTokens.getApplicationThread().updateUiTranslationState(
taskTopActivityTokens.getActivityToken(), state, sourceSpec, targetSpec,
- viewIds);
+ viewIds, uiTranslationSpec);
} catch (RemoteException e) {
Slog.w(TAG, "Update UiTranslationState fail: " + e);
}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsManagerInternal.java b/services/usage/java/com/android/server/usage/StorageStatsManagerInternal.java
deleted file mode 100644
index 47760ef..0000000
--- a/services/usage/java/com/android/server/usage/StorageStatsManagerInternal.java
+++ /dev/null
@@ -1,50 +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.usage;
-
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.content.pm.PackageStats;
-
-/**
- * StorageStatsManager local system service interface.
- *
- * Only for use within the system server.
- */
-public abstract class StorageStatsManagerInternal {
- /**
- * Class used to augment {@link PackageStats} with the data stored by the system on
- * behalf of apps in system specific directories
- * ({@link android.os.Environment#getDataSystemDirectory},
- * {@link android.os.Environment#getDataSystemCeDirectory}, etc).
- */
- public interface StorageStatsAugmenter {
- void augmentStatsForPackage(@NonNull PackageStats stats,
- @NonNull String packageName, @UserIdInt int userId,
- boolean callerHasStatsPermission);
- void augmentStatsForUid(@NonNull PackageStats stats, int uid,
- boolean callerHasStatsPermission);
- }
-
- /**
- * Register a {@link StorageStatsAugmenter}.
- *
- * @param augmenter the {@link StorageStatsAugmenter} object to be registered.
- * @param tag the identifier to be used for debugging in logs/trace.
- */
- public abstract void registerStorageStatsAugmenter(@NonNull StorageStatsAugmenter augmenter,
- @NonNull String tag);
-}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsManagerLocal.java b/services/usage/java/com/android/server/usage/StorageStatsManagerLocal.java
new file mode 100644
index 0000000..7cac820
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/StorageStatsManagerLocal.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usage;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.pm.PackageStats;
+import android.os.UserHandle;
+
+/**
+ * StorageStatsManager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface StorageStatsManagerLocal {
+ /**
+ * Class used to augment {@link PackageStats} with the data stored by the system on
+ * behalf of apps in system specific directories
+ * ({@link android.os.Environment#getDataSystemDirectory},
+ * {@link android.os.Environment#getDataSystemCeDirectory}, etc).
+ */
+ interface StorageStatsAugmenter {
+ /**
+ * Augments {@link PackageStats} with data stored by the system for the given package.
+ *
+ * @param stats Structure to modify with usage data
+ * @param packageName Package name of the app whose data is stored by the
+ * system and needs to be added to {@code stats}.
+ * @param userHandle Device user for which usage stats are being requested.
+ * @param canCallerAccessAllStats Whether the caller who is requesting the storage stats
+ * can query stats for packages other than itself. For
+ * example, holding the PACKAGE_USAGE_STATS permission is one
+ * way to accomplish this.
+ */
+ void augmentStatsForPackageForUser(
+ @NonNull PackageStats stats,
+ @NonNull String packageName,
+ @NonNull UserHandle userHandle,
+ boolean canCallerAccessAllStats);
+
+ /**
+ * Augments {@link PackageStats} with data stored by the system for the given uid.
+ *
+ * @param stats Structure to modify with usage data
+ * @param uid Unique app ID for the app instance whose stats are being
+ * requested.
+ * @param canCallerAccessAllStats Whether the caller who is requesting the storage stats
+ * can query stats for packages other than itself. For
+ * example, holding the PACKAGE_USAGE_STATS permission is one
+ * way to accomplish this.
+ */
+ void augmentStatsForUid(
+ @NonNull PackageStats stats, int uid, boolean canCallerAccessAllStats);
+
+ /**
+ * Augments {@link PackageStats} with data stored by the system for the given device user.
+ *
+ * @param stats Structure to modify with usage data
+ * @param userHandle Device user whose data is stored by the system and needs to be added to
+ * {@code stats}.
+ */
+ void augmentStatsForUser(@NonNull PackageStats stats, @NonNull UserHandle userHandle);
+ }
+
+ /**
+ * Register a {@link StorageStatsAugmenter}.
+ *
+ * @param augmenter the {@link StorageStatsAugmenter} object to be registered.
+ * @param tag the identifier to be used for debugging in logs/trace.
+ */
+ void registerStorageStatsAugmenter(
+ @NonNull StorageStatsAugmenter augmenter, @NonNull String tag);
+}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index a0a3909..b056de0 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -20,7 +20,7 @@
import static com.android.internal.util.ArrayUtils.defeatNullable;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter;
+import static com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter;
import android.Manifest;
import android.annotation.NonNull;
@@ -71,6 +71,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.server.IoThread;
+import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.Installer;
@@ -152,7 +153,7 @@
}
});
- LocalServices.addService(StorageStatsManagerInternal.class, new LocalService());
+ LocalManagerRegistry.addManager(StorageStatsManagerLocal.class, new LocalService());
}
private void invalidateMounts() {
@@ -335,9 +336,10 @@
throw new ParcelableException(new IOException(e.getMessage()));
}
if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
+ UserHandle userHandle = UserHandle.of(userId);
forEachStorageStatsAugmenter((storageStatsAugmenter) -> {
- storageStatsAugmenter.augmentStatsForPackage(stats,
- packageName, userId, callerHasStatsPermission);
+ storageStatsAugmenter.augmentStatsForPackageForUser(stats,
+ packageName, userHandle, callerHasStatsPermission);
}, "queryStatsForPackage");
}
return translate(stats);
@@ -430,6 +432,12 @@
} catch (InstallerException e) {
throw new ParcelableException(new IOException(e.getMessage()));
}
+ if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
+ UserHandle userHandle = UserHandle.of(userId);
+ forEachStorageStatsAugmenter((storageStatsAugmenter) -> {
+ storageStatsAugmenter.augmentStatsForUser(stats, userHandle);
+ }, "queryStatsForUser");
+ }
return translate(stats);
}
@@ -784,7 +792,7 @@
}
}
- private class LocalService extends StorageStatsManagerInternal {
+ private class LocalService implements StorageStatsManagerLocal {
@Override
public void registerStorageStatsAugmenter(
@NonNull StorageStatsAugmenter storageStatsAugmenter,
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl b/services/usage/java/com/android/server/usage/package-info.java
similarity index 72%
copy from apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
copy to services/usage/java/com/android/server/usage/package-info.java
index 4686de8..0719df41 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
+++ b/services/usage/java/com/android/server/usage/package-info.java
@@ -1,11 +1,11 @@
-/**
- * Copyright 2020, The Android Open Source Project
+/*
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
-/** {@hide} */
-parcelable AppSearchBatchResult;
\ No newline at end of file
+/** @hide */
+package com.android.server.usage;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index d1bd159..aec06431 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -164,6 +164,11 @@
KEY_INITIALIZATION_STATUS,
INITIALIZATION_STATUS_UNKNOWN)
: INITIALIZATION_STATUS_UNKNOWN;
+ // Add the protection to avoid unexpected status
+ if (status > HotwordDetectionService.getMaxCustomInitializationStatus()
+ && status != INITIALIZATION_STATUS_UNKNOWN) {
+ status = INITIALIZATION_STATUS_UNKNOWN;
+ }
callback.onStatusReported(status);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to report initialization status: " + e);
@@ -612,7 +617,7 @@
options,
new IDspHotwordDetectionCallback.Stub() {
@Override
- public void onRejected(@Nullable HotwordRejectedResult result)
+ public void onRejected(HotwordRejectedResult result)
throws RemoteException {
bestEffortClose(serviceAudioSink);
bestEffortClose(serviceAudioSource);
@@ -622,7 +627,7 @@
}
@Override
- public void onDetected(@Nullable HotwordDetectedResult triggerResult)
+ public void onDetected(HotwordDetectedResult triggerResult)
throws RemoteException {
bestEffortClose(serviceAudioSink);
bestEffortClose(serviceAudioSource);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 92cfe49..f3d80b1 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -234,6 +234,23 @@
VoiceInteractionManagerService.this.mServiceStub.stopLocalVoiceInteraction(
callingActivity);
}
+
+ @Override
+ public boolean hasActiveSession(String packageName) {
+ VoiceInteractionManagerServiceImpl impl =
+ VoiceInteractionManagerService.this.mServiceStub.mImpl;
+ if (impl == null) {
+ return false;
+ }
+
+ VoiceInteractionSessionConnection session =
+ impl.mActiveSession;
+ if (session == null) {
+ return false;
+ }
+
+ return TextUtils.equals(packageName, session.mSessionComponentName.getPackageName());
+ }
}
// implementation entry point and binder service
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 73f1783..30403f4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -806,6 +806,15 @@
*/
public static final String EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ =
"android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ";
+
+ /**
+ * Boolean connection extra key used to indicate whether device to device communication is
+ * available for the current call.
+ * @hide
+ */
+ public static final String EXTRA_IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE =
+ "android.telecom.extra.IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE";
+
/**
* Connection event used to inform Telecom that it should play the on hold tone. This is used
* to play a tone when the peer puts the current call on hold. Sent to Telecom via
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 579b33e..e332d3f 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -27,6 +27,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -283,10 +284,13 @@
* number relies on presence. Should only be set if the {@code PhoneAccount} also has
* {@link #CAPABILITY_VIDEO_CALLING}.
* <p>
- * When set, the {@link ConnectionService} is responsible for toggling the
+ * Note: As of Android 12, using the
* {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit on the
* {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} column to indicate whether
- * a contact's phone number supports video calling.
+ * a contact's phone number supports video calling has been deprecated and should only be used
+ * on devices where {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set. On newer
+ * devices, applications must use {@link android.telephony.ims.RcsUceAdapter} instead to
+ * determine whether or not a contact's phone number supports carrier video calling.
* <p>
* See {@link #getCapabilities}
*/
diff --git a/telephony/common/com/google/android/mms/pdu/PduParser.java b/telephony/common/com/google/android/mms/pdu/PduParser.java
index 5340245..677fe2f 100755
--- a/telephony/common/com/google/android/mms/pdu/PduParser.java
+++ b/telephony/common/com/google/android/mms/pdu/PduParser.java
@@ -1550,6 +1550,11 @@
if (cur < TEXT_MIN) {
int length = parseValueLength(pduDataStream);
int startPos = pduDataStream.available();
+ if (length > startPos) {
+ Log.e(LOG_TAG, "parseContentType: Invalid length " + length
+ + " when available bytes are " + startPos);
+ return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
+ }
pduDataStream.mark(1);
temp = pduDataStream.read();
assert(-1 != temp);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 33a9a96..c527e66 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -38,6 +38,7 @@
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.ImsSsData;
+import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;
@@ -4259,6 +4260,9 @@
* If this flag is disabled, the capabilities cache will not be refreshed internally at all
* and will only be updated if the cached capabilities are stale when an application
* requests them.
+ *
+ * @see RcsUceAdapter#isUceSettingEnabled() more information about this feature and how
+ * it is enabled by the user.
*/
public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL =
KEY_PREFIX + "rcs_bulk_capability_exchange_bool";
@@ -5549,7 +5553,7 @@
sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_ENABLE_2G, false);
sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY,
- new String[]{"ia", "default", "ims", "mms", "dun", "emergency"});
+ new String[]{"ia", "default", "mms", "dun"});
sDefaults.putBoolean(KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL, false);
sDefaults.putBoolean(KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL, false);
sDefaults.putBoolean(KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL, true);
diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java
index ac775b3..14de2f2 100644
--- a/telephony/java/android/telephony/CellBroadcastService.java
+++ b/telephony/java/android/telephony/CellBroadcastService.java
@@ -28,6 +28,11 @@
import android.os.RemoteCallback;
import android.telephony.cdma.CdmaSmsCbProgramData;
+import com.android.internal.util.FastPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
import java.util.List;
import java.util.function.Consumer;
@@ -186,5 +191,16 @@
public @NonNull CharSequence getCellBroadcastAreaInfo(int slotIndex) {
return CellBroadcastService.this.getCellBroadcastAreaInfo(slotIndex);
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ CellBroadcastService.this.dump(fd, fout, args);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, String[] args) {
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
+ CellBroadcastService.this.dump(fd, pw, args);
+ }
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 78da86c..a262954 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15025,6 +15025,15 @@
"CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED";
/**
+ * Indicates whether modem supports handling parsed SIM phonebook records through the RIL,
+ * both batched reads and individual writes.
+ *
+ * @hide
+ */
+ public static final String CAPABILITY_SIM_PHONEBOOK_IN_MODEM =
+ "CAPABILITY_SIM_PHONEBOOK_IN_MODEM";
+
+ /**
* A list of the radio interface capability values with public valid constants.
*
* Here is a related list for the systemapi-only valid constants:
@@ -15039,6 +15048,7 @@
@Retention(RetentionPolicy.SOURCE)
@StringDef(prefix = "CAPABILITY_", value = {
CAPABILITY_SLICING_CONFIG_SUPPORTED,
+ CAPABILITY_SIM_PHONEBOOK_IN_MODEM,
})
public @interface RadioInterfaceCapability {}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 814ce18..3700026 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -56,7 +56,9 @@
/**
* Activity Action: Show the opt-in dialog for enabling or disabling RCS contact discovery
- * using User Capability Exchange (UCE).
+ * using User Capability Exchange (UCE), which enables a service that periodically shares the
+ * phone numbers of all of the contacts in the user's address book with the carrier to refresh
+ * the RCS capabilities associated with those contacts as the local cache becomes stale.
* <p>
* An application that depends on RCS contact discovery being enabled must send this intent
* using {@link Context#startActivity(Intent)} to ask the user to opt-in for contacts upload for
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index dd91026..7a1c092 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -28,10 +28,13 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -417,7 +420,7 @@
* <p>
* After {@link CapabilitiesCallback#onComplete} or {@link CapabilitiesCallback#onError} has
* been called, the reference to this callback will be discarded on the service side.
- * @see #requestCapabilities(Executor, List, CapabilitiesCallback)
+ * @see #requestCapabilities(Collection, Executor, CapabilitiesCallback)
* @hide
*/
@SystemApi
@@ -464,10 +467,16 @@
}
/**
- * Request the User Capability Exchange capabilities for one or more contacts.
+ * Request the RCS capabilities for one or more contacts using RCS User Capability Exchange.
* <p>
- * This will return the cached capabilities of the contact and will not perform a capability
- * poll on the network unless there are contacts being queried with stale information.
+ * This API will first check a local cache for the requested numbers and return the cached
+ * RCS capabilities of each number if the cache exists and is not stale. If the cache for a
+ * number is stale or there is no cached information about the requested number, the device will
+ * then perform a query to the carrier's network to request the RCS capabilities of the
+ * requested numbers.
+ * <p>
+ * Depending on the number of requests being sent, this API may throttled internally as the
+ * operations are queued to be executed by the carrier's network.
* <p>
* Be sure to check the availability of this feature using
* {@link ImsRcsManager#isAvailable(int, int)} and ensuring
@@ -552,13 +561,15 @@
}
/**
- * Ignore the device cache and perform a capability discovery for one contact, also called
- * "availability fetch."
+ * Request the RCS capabilities for a phone number using User Capability Exchange.
* <p>
- * This will always perform a query to the network as long as requests are over the carrier
- * availability fetch throttling threshold. If too many network requests are sent too quickly,
- * #ERROR_TOO_MANY_REQUESTS will be returned.
- *
+ * Unlike {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)}, which caches
+ * the result received from the network for a certain amount of time and uses that cached result
+ * for subsequent requests for RCS capabilities of the same phone number, this API will always
+ * request the RCS capabilities of a contact from the carrier's network.
+ * <p>
+ * Depending on the number of requests, this API may throttled internally as the operations are
+ * queued to be executed by the carrier's network.
* <p>
* Be sure to check the availability of this feature using
* {@link ImsRcsManager#isAvailable(int, int)} and ensuring
@@ -680,7 +691,8 @@
* state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
* <p>
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription
- * changed events and call {@link #unregisterPublishStateCallback} to clean up.
+ * changed events and call
+ * {@link #removeOnPublishStateChangedListener(OnPublishStateChangedListener)} to clean up.
* <p>
* The registered {@link OnPublishStateChangedListener} will also receive a callback when it is
* registered with the current publish state.
@@ -770,13 +782,23 @@
}
/**
- * The user’s setting for whether or not User Capability Exchange (UCE) is enabled for the
- * associated subscription.
+ * The setting for whether or not the user has opted in to the automatic refresh of the RCS
+ * capabilities associated with the contacts in the user's contact address book. By default,
+ * this setting is disabled and must be enabled after the user has seen the opt-in dialog shown
+ * by {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}.
+ * <p>
+ * If this feature is enabled, the device will periodically share the phone numbers of all of
+ * the contacts in the user's address book with the carrier to refresh the RCS capabilities
+ * cache associated with those contacts as the local cache becomes stale.
+ * <p>
+ * This setting will only enable this feature if
+ * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is also enabled.
* <p>
* Note: This setting does not affect whether or not the device publishes its service
* capabilities if the subscription supports presence publication.
*
- * @return true if the user’s setting for UCE is enabled, false otherwise.
+ * @return true if the user has opted in for automatic refresh of the RCS capabilities of their
+ * contacts, false otherwise.
* @throws ImsException if the subscription associated with this instance of
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
@@ -802,18 +824,33 @@
}
/**
- * Change the user’s setting for whether or not UCE is enabled for the associated subscription.
+ * Change the user’s setting for whether or not the user has opted in to the automatic
+ * refresh of the RCS capabilities associated with the contacts in the user's contact address
+ * book. By default, this setting is disabled and must be enabled using this method after the
+ * user has seen the opt-in dialog shown by
+ * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}.
* <p>
- * If an application Requires UCE, they will launch an Activity using the Intent
- * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}, which will ask the user if
- * they wish to enable this feature. This setting should only be enabled after the user has
- * opted-in to capability exchange.
+ * If an application wishes to request that the user enable this feature, they must launch an
+ * Activity using the Intent {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN},
+ * which will ask the user if they wish to enable this feature. This setting must only be
+ * enabled after the user has opted-in to this feature.
+ * <p>
+ * This must not affect the
+ * {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)} or
+ * {@link #requestAvailability(Uri, Executor, CapabilitiesCallback)} API,
+ * as those APIs are still required for per-contact RCS capability queries of phone numbers
+ * required for operations such as placing a Video Telephony call or starting an RCS chat
+ * session.
+ * <p>
+ * This setting will only enable this feature if
+ * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is also enabled.
* <p>
* Note: This setting does not affect whether or not the device publishes its service
* capabilities if the subscription supports presence publication.
*
- * @param isEnabled the user's setting for whether or not they wish for User
- * Capability Exchange to be enabled.
+ * @param isEnabled true if the user has opted in for automatic refresh of the RCS capabilities
+ * of their contacts, or false if they have chosen to opt-out. By default this
+ * setting is disabled.
* @throws ImsException if the subscription associated with this instance of
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 4cd59a2..91ecbf0 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2352,6 +2352,12 @@
void setActiveDeviceToDeviceTransport(String transport);
/**
+ * Forces Device to Device communication to be enabled, even if the device config has it
+ * disabled.
+ */
+ void setDeviceToDeviceForceEnabled(boolean isForceEnabled);
+
+ /**
* Gets the config of RCS VoLTE single registration enabled for the carrier/subscription.
*/
boolean getCarrierSingleRegistrationEnabled(int subId);
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 1d1eddf..fe8e671 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -497,6 +497,9 @@
int RIL_REQUEST_ENABLE_MODEM = 146;
int RIL_REQUEST_GET_MODEM_STATUS = 147;
int RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE = 148;
+ int RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY = 149;
+ int RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS = 150;
+ int RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD = 151;
/* The following requests are not defined in RIL.h */
int RIL_REQUEST_HAL_NON_RIL_BASE = 200;
@@ -583,6 +586,8 @@
int RIL_UNSOL_NETWORK_SCAN_RESULT = 1049;
int RIL_UNSOL_KEEPALIVE_STATUS = 1050;
int RIL_UNSOL_UNTHROTTLE_APN = 1052;
+ int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED = 1053;
+ int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054;
/* The following unsols are not defined in RIL.h */
int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
index fc1d839..014efc2 100644
--- a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
@@ -145,4 +145,21 @@
val received = mSender.getTimeline()
assertEquals(sent, received)
}
+
+ // If an invalid timeline is sent, the channel should get closed. This helps surface any
+ // app-originating bugs early, and forces the work-around to happen in the early stages of the
+ // event processing.
+ @Test
+ fun testSendAndReceiveInvalidTimeline() {
+ val sent = TestInputEventSender.Timeline(
+ inputEventId = 1, gpuCompletedTime = 3, presentTime = 2)
+ mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime)
+ val received = mSender.getTimeline()
+ assertEquals(null, received)
+ // Sender will no longer receive callbacks for this fd, even if receiver sends a valid
+ // timeline later
+ mReceiver.reportTimeline(2 /*inputEventId*/, 3 /*gpuCompletedTime*/, 4 /*presentTime*/)
+ val receivedSecondTimeline = mSender.getTimeline()
+ assertEquals(null, receivedSecondTimeline)
+ }
}
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 898b8d4..80b0dfe 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -20,6 +20,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeTrue;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -56,6 +57,11 @@
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
/**
* Tests if fonts can be updated by {@link FontManager} API.
@@ -95,6 +101,12 @@
EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity";
private static final long ACTIVITY_TIMEOUT_MILLIS = SECONDS.toMillis(10);
+ private static final Pattern PATTERN_FONT_FILES = Pattern.compile("\\.(ttf|otf|ttc|otc)$");
+ private static final Pattern PATTERN_TMP_FILES = Pattern.compile("^/data/local/tmp/");
+ private static final Pattern PATTERN_DATA_FONT_FILES = Pattern.compile("^/data/fonts/files/");
+ private static final Pattern PATTERN_SYSTEM_FONT_FILES =
+ Pattern.compile("^/(system|product)/fonts/");
+
private String mKeyId;
private FontManager mFontManager;
@@ -236,6 +248,45 @@
assertThat(fontPathAfterReboot).isEqualTo(fontPath);
}
+ @Test
+ public void fdLeakTest() throws Exception {
+ long originalOpenFontCount =
+ countMatch(getOpenFiles("system_server"), PATTERN_FONT_FILES);
+ Pattern patternEmojiVPlus1 =
+ Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
+ for (int i = 0; i < 10; i++) {
+ assertThat(updateFontFile(
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ .isEqualTo(FontManager.RESULT_SUCCESS);
+ List<String> openFiles = getOpenFiles("system_server");
+ for (Pattern p : Arrays.asList(PATTERN_FONT_FILES, PATTERN_SYSTEM_FONT_FILES,
+ PATTERN_DATA_FONT_FILES, PATTERN_TMP_FILES)) {
+ Log.i(TAG, String.format("num of %s: %d", p, countMatch(openFiles, p)));
+ }
+ // system_server should not keep /data/fonts files open.
+ assertThat(countMatch(openFiles, PATTERN_DATA_FONT_FILES)).isEqualTo(0);
+ // system_server should not keep passed FD open.
+ assertThat(countMatch(openFiles, patternEmojiVPlus1)).isEqualTo(0);
+ // The number of open font FD should not increase.
+ assertThat(countMatch(openFiles, PATTERN_FONT_FILES))
+ .isAtMost(originalOpenFontCount);
+ }
+ }
+
+ @Test
+ public void fdLeakTest_withoutPermission() throws Exception {
+ Pattern patternEmojiVPlus1 =
+ Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
+ byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+ try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(
+ new File(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF), MODE_READ_ONLY)) {
+ assertThrows(SecurityException.class,
+ () -> updateFontFileWithoutPermission(fd, signature, 0));
+ }
+ List<String> openFiles = getOpenFiles("system_server");
+ assertThat(countMatch(openFiles, patternEmojiVPlus1)).isEqualTo(0);
+ }
+
private static String insertCert(String certPath) throws Exception {
Pair<String, String> result;
try (InputStream is = new FileInputStream(certPath)) {
@@ -253,16 +304,21 @@
try (ParcelFileDescriptor fd =
ParcelFileDescriptor.open(new File(fontPath), MODE_READ_ONLY)) {
return SystemUtil.runWithShellPermissionIdentity(() -> {
- FontConfig fontConfig = mFontManager.getFontConfig();
- return mFontManager.updateFontFamily(
- new FontFamilyUpdateRequest.Builder()
- .addFontFileUpdateRequest(new FontFileUpdateRequest(fd, signature))
- .build(),
- fontConfig.getConfigVersion());
+ int configVersion = mFontManager.getFontConfig().getConfigVersion();
+ return updateFontFileWithoutPermission(fd, signature, configVersion);
});
}
}
+ private int updateFontFileWithoutPermission(ParcelFileDescriptor fd, byte[] signature,
+ int configVersion) {
+ return mFontManager.updateFontFamily(
+ new FontFamilyUpdateRequest.Builder()
+ .addFontFileUpdateRequest(new FontFileUpdateRequest(fd, signature))
+ .build(),
+ configVersion);
+ }
+
private String getFontPath(String psName) {
return SystemUtil.runWithShellPermissionIdentity(() -> {
FontConfig fontConfig = mFontManager.getFontConfig();
@@ -338,7 +394,37 @@
return !expectCommandToSucceed(cmd).trim().isEmpty();
}
+ private static List<String> getOpenFiles(String appId) throws Exception {
+ String pid = pidOf(appId);
+ if (pid.isEmpty()) {
+ return Collections.emptyList();
+ }
+ String cmd = String.format("lsof -p %s", pid);
+ String out = expectCommandToSucceed(cmd);
+ List<String> paths = new ArrayList<>();
+ boolean first = true;
+ for (String line : out.split("\n")) {
+ // Skip the header.
+ if (first) {
+ first = false;
+ continue;
+ }
+ String[] records = line.split(" ");
+ if (records.length > 0) {
+ paths.add(records[records.length - 1]);
+ }
+ }
+ return paths;
+ }
+
private static String pidOf(String appId) throws Exception {
return expectCommandToSucceed("pidof " + appId).trim();
}
+
+ private static long countMatch(List<String> paths, Pattern pattern) {
+ // Note: asPredicate() returns true for partial matching.
+ return paths.stream()
+ .filter(pattern.asPredicate())
+ .count();
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
deleted file mode 100644
index 551b94c..0000000
--- a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
+++ /dev/null
@@ -1,90 +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.connectivity
-
-import android.net.NetworkCapabilities
-import android.net.NetworkRequest
-import android.net.NetworkScore.KEEP_CONNECTED_NONE
-import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
-import kotlin.test.assertEquals
-import kotlin.test.assertNull
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class NetworkRankerTest {
- private val ranker = NetworkRanker()
-
- private fun makeNai(satisfy: Boolean, legacyScore: Int) =
- mock(NetworkAgentInfo::class.java).also {
- doReturn(satisfy).`when`(it).satisfies(any())
- val fs = FullScore(legacyScore, 0 /* policies */, KEEP_CONNECTED_NONE)
- doReturn(fs).`when`(it).getScore()
- val nc = NetworkCapabilities.Builder().build()
- doReturn(nc).`when`(it).getCapsNoCopy()
- }
-
- @Test
- fun testGetBestNetwork() {
- val scores = listOf(20, 50, 90, 60, 23, 68)
- val nais = scores.map { makeNai(true, it) }
- val bestNetwork = nais[2] // The one with the top score
- val someRequest = mock(NetworkRequest::class.java)
- assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais, bestNetwork))
- }
-
- @Test
- fun testIgnoreNonSatisfying() {
- val nais = listOf(makeNai(true, 20), makeNai(true, 50), makeNai(false, 90),
- makeNai(false, 60), makeNai(true, 23), makeNai(false, 68))
- val bestNetwork = nais[1] // Top score that's satisfying
- val someRequest = mock(NetworkRequest::class.java)
- assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais, nais[1]))
- }
-
- @Test
- fun testNoMatch() {
- val nais = listOf(makeNai(false, 20), makeNai(false, 50), makeNai(false, 90))
- val someRequest = mock(NetworkRequest::class.java)
- assertNull(ranker.getBestNetwork(someRequest, nais, null))
- }
-
- @Test
- fun testEmpty() {
- val someRequest = mock(NetworkRequest::class.java)
- assertNull(ranker.getBestNetwork(someRequest, emptyList(), null))
- }
-
- // Make sure the ranker is "stable" (as in stable sort), that is, it always returns the FIRST
- // network satisfying the request if multiple of them have the same score.
- @Test
- fun testStable() {
- val nais1 = listOf(makeNai(true, 30), makeNai(true, 30), makeNai(true, 30),
- makeNai(true, 30), makeNai(true, 30), makeNai(true, 30))
- val someRequest = mock(NetworkRequest::class.java)
- assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1, nais1[0]))
-
- val nais2 = listOf(makeNai(true, 30), makeNai(true, 50), makeNai(true, 20),
- makeNai(true, 50), makeNai(true, 50), makeNai(true, 40))
- assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2, nais2[1]))
- }
-}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 9410886..c59dcf8 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -16,13 +16,17 @@
package android.net.vcn;
+import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest;
import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest;
import androidx.test.filters.SmallTest;
@@ -120,6 +124,21 @@
}
@Test
+ public void testBuilderRequiresMobikeEnabled() {
+ try {
+ final IkeSessionParams ikeParams =
+ IkeSessionParamsUtilsTest.createBuilderMinimum()
+ .removeIkeOption(IKE_OPTION_MOBIKE)
+ .build();
+ final IkeTunnelConnectionParams tunnelParams =
+ TunnelConnectionParamsUtilsTest.buildTestParams(ikeParams);
+ new VcnGatewayConnectionConfig.Builder(GATEWAY_CONNECTION_NAME_PREFIX, tunnelParams);
+ fail("Expected exception due to MOBIKE not enabled");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
public void testBuilderRequiresNonEmptyExposedCaps() {
try {
newBuilder()
diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
index 582275d..19df3c7 100644
--- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
@@ -16,19 +16,24 @@
package android.net.vcn;
-import static android.net.NetworkCapabilities.REDACT_ALL;
+import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
+import android.net.NetworkCapabilities;
+import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.os.Parcel;
import org.junit.Test;
+import java.util.Arrays;
+
public class VcnTransportInfoTest {
private static final int SUB_ID = 1;
private static final int NETWORK_ID = 5;
@@ -39,12 +44,6 @@
private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO);
@Test
- public void testRedactionDefaults() {
- assertEquals(REDACT_ALL, CELL_UNDERLYING_INFO.getRedaction());
- assertEquals(REDACT_ALL, WIFI_UNDERLYING_INFO.getRedaction());
- }
-
- @Test
public void testGetWifiInfo() {
assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo());
@@ -59,15 +58,28 @@
}
@Test
- public void testMakeCopySetsRedactions() {
+ public void testMakeCopyRedactForNetworkSettings() {
+ for (VcnTransportInfo info : Arrays.asList(CELL_UNDERLYING_INFO, WIFI_UNDERLYING_INFO)) {
+ assertEquals(
+ INVALID_SUBSCRIPTION_ID,
+ ((VcnTransportInfo) info.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
+ .getSubId());
+ assertNull(
+ ((VcnTransportInfo) info.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
+ .getWifiInfo());
+ }
+ }
+
+ @Test
+ public void testMakeCopyRedactForAccessFineLocation() {
assertEquals(
- REDACT_FOR_NETWORK_SETTINGS,
- ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
- .getRedaction());
+ SUB_ID,
+ ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
+ .getSubId());
assertEquals(
- REDACT_FOR_NETWORK_SETTINGS,
- ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
- .getRedaction());
+ WifiConfiguration.INVALID_NETWORK_ID,
+ ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
+ .getWifiInfo().getNetworkId());
}
@Test
@@ -84,29 +96,34 @@
}
private void verifyParcelingIsNull(VcnTransportInfo vcnTransportInfo) {
- // Verify redacted by default
+ VcnTransportInfo redacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(
+ NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS);
+
Parcel parcel = Parcel.obtain();
- vcnTransportInfo.writeToParcel(parcel, 0 /* flags */);
+ redacted.writeToParcel(parcel, 0 /* flags */);
parcel.setDataPosition(0);
assertNull(VcnTransportInfo.CREATOR.createFromParcel(parcel));
}
@Test
- public void testParcelUnparcelNotRedactedForSysUi() {
- verifyParcelingForSysUi(CELL_UNDERLYING_INFO);
- verifyParcelingForSysUi(WIFI_UNDERLYING_INFO);
+ public void testParcelNotRedactedForSysUi() {
+ VcnTransportInfo cellRedacted = parcelForSysUi(CELL_UNDERLYING_INFO);
+ assertEquals(SUB_ID, cellRedacted.getSubId());
+ VcnTransportInfo wifiRedacted = parcelForSysUi(WIFI_UNDERLYING_INFO);
+ assertEquals(NETWORK_ID, wifiRedacted.getWifiInfo().getNetworkId());
}
- private void verifyParcelingForSysUi(VcnTransportInfo vcnTransportInfo) {
+ private VcnTransportInfo parcelForSysUi(VcnTransportInfo vcnTransportInfo) {
// Allow fully unredacted; SysUI will have all the relevant permissions.
- final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(0);
+ final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(
+ REDACT_NONE);
final Parcel parcel = Parcel.obtain();
unRedacted.writeToParcel(parcel, 0 /* flags */);
parcel.setDataPosition(0);
final VcnTransportInfo unparceled = VcnTransportInfo.CREATOR.createFromParcel(parcel);
assertEquals(vcnTransportInfo, unparceled);
- assertEquals(REDACT_ALL, unparceled.getRedaction());
+ return unparceled;
}
}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
index 393787f..f385113 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -52,8 +52,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IkeSessionParamsUtilsTest {
- // Package private for use in EncryptedTunnelParamsUtilsTest
- static IkeSessionParams.Builder createBuilderMinimum() {
+ // Public for use in VcnGatewayConnectionConfigTest, EncryptedTunnelParamsUtilsTest
+ public static IkeSessionParams.Builder createBuilderMinimum() {
final InetAddress serverAddress = InetAddresses.parseNumericAddress("192.0.2.100");
// TODO: b/185941731 Make sure all valid IKE_OPTIONS are added and validated.
@@ -63,6 +63,7 @@
.setLocalIdentification(new IkeFqdnIdentification("client.test.android.net"))
.setRemoteIdentification(new IkeFqdnIdentification("server.test.android.net"))
.addIkeOption(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500)
+ .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
.setAuthPsk("psk".getBytes());
}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
index 0c8ad32..f9dc9eb 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import androidx.test.filters.SmallTest;
@@ -31,9 +32,13 @@
public class TunnelConnectionParamsUtilsTest {
// Public for use in VcnGatewayConnectionConfigTest
public static IkeTunnelConnectionParams buildTestParams() {
+ return buildTestParams(IkeSessionParamsUtilsTest.createBuilderMinimum().build());
+ }
+
+ // Public for use in VcnGatewayConnectionConfigTest
+ public static IkeTunnelConnectionParams buildTestParams(IkeSessionParams params) {
return new IkeTunnelConnectionParams(
- IkeSessionParamsUtilsTest.createBuilderMinimum().build(),
- TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build());
+ params, TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build());
}
@Test
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 9ecd82f..3360d40 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -37,6 +37,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -66,6 +67,7 @@
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.os.IBinder;
@@ -197,7 +199,8 @@
.newVcnContext(
eq(mMockContext),
eq(mTestLooper.getLooper()),
- any(VcnNetworkProvider.class));
+ any(VcnNetworkProvider.class),
+ anyBoolean());
doReturn(mSubscriptionTracker)
.when(mMockDeps)
.newTelephonySubscriptionTracker(
@@ -371,6 +374,12 @@
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps)
+ .newVcnContext(
+ eq(mMockContext),
+ eq(mTestLooper.getLooper()),
+ any(VcnNetworkProvider.class),
+ anyBoolean());
+ verify(mMockDeps)
.newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
}
@@ -528,6 +537,28 @@
}
@Test
+ public void testSetVcnConfigTestModeRequiresPermission() throws Exception {
+ doThrow(new SecurityException("Requires MANAGE_TEST_NETWORKS"))
+ .when(mMockContext)
+ .enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TEST_NETWORKS), any());
+
+ final VcnConfig vcnConfig =
+ new VcnConfig.Builder(mMockContext)
+ .addGatewayConnectionConfig(
+ VcnGatewayConnectionConfigTest.buildTestConfig())
+ .setIsTestModeProfile()
+ .build();
+
+ try {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME);
+ fail("Expected exception due to using test-mode without permission");
+ } catch (SecurityException e) {
+ verify(mMockPolicyListener, never()).onPolicyChanged();
+ }
+ }
+
+ @Test
public void testSetVcnConfigNotifiesStatusCallback() throws Exception {
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 528f240..ca74638 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -88,13 +88,13 @@
private static final SubscriptionInfo TEST_SUBINFO_2 = mock(SubscriptionInfo.class);
private static final Map<ParcelUuid, Set<String>> TEST_PRIVILEGED_PACKAGES =
Collections.singletonMap(TEST_PARCEL_UUID, Collections.singleton(PACKAGE_NAME));
- private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP;
+ private static final Map<Integer, SubscriptionInfo> TEST_SUBID_TO_INFO_MAP;
static {
- final Map<Integer, ParcelUuid> subIdToGroupMap = new HashMap<>();
- subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_PARCEL_UUID);
- subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_PARCEL_UUID);
- TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap);
+ final Map<Integer, SubscriptionInfo> subIdToGroupMap = new HashMap<>();
+ subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_SUBINFO_1);
+ subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_SUBINFO_2);
+ TEST_SUBID_TO_INFO_MAP = Collections.unmodifiableMap(subIdToGroupMap);
}
@NonNull private final Context mContext;
@@ -190,13 +190,13 @@
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
Map<ParcelUuid, Set<String>> privilegedPackages) {
- return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, privilegedPackages);
+ return buildExpectedSnapshot(TEST_SUBID_TO_INFO_MAP, privilegedPackages);
}
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
- Map<Integer, ParcelUuid> subIdToGroupMap,
+ Map<Integer, SubscriptionInfo> subIdToInfoMap,
Map<ParcelUuid, Set<String>> privilegedPackages) {
- return new TelephonySubscriptionSnapshot(subIdToGroupMap, privilegedPackages);
+ return new TelephonySubscriptionSnapshot(subIdToInfoMap, privilegedPackages);
}
private void verifyNoActiveSubscriptions() {
@@ -371,7 +371,7 @@
@Test
public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
final TelephonySubscriptionSnapshot snapshot =
- new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap());
+ new TelephonySubscriptionSnapshot(TEST_SUBID_TO_INFO_MAP, emptyMap());
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
@@ -380,7 +380,7 @@
@Test
public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
final TelephonySubscriptionSnapshot snapshot =
- new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap());
+ new TelephonySubscriptionSnapshot(TEST_SUBID_TO_INFO_MAP, emptyMap());
assertEquals(
new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index 8289e85..a36fd79 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -41,12 +42,14 @@
import android.net.TelephonyNetworkSpecifier;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
import android.util.ArraySet;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
-import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkListener;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
@@ -97,11 +100,13 @@
@Mock private Context mContext;
@Mock private VcnNetworkProvider mVcnNetworkProvider;
@Mock private ConnectivityManager mConnectivityManager;
+ @Mock private TelephonyManager mTelephonyManager;
+ @Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
@Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
@Mock private Network mNetwork;
- @Captor private ArgumentCaptor<RouteSelectionCallback> mRouteSelectionCallbackCaptor;
+ @Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor;
private TestLooper mTestLooper;
private VcnContext mVcnContext;
@@ -112,14 +117,27 @@
MockitoAnnotations.initMocks(this);
mTestLooper = new TestLooper();
- mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider));
- doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+ mVcnContext =
+ spy(
+ new VcnContext(
+ mContext,
+ mTestLooper.getLooper(),
+ mVcnNetworkProvider,
+ false /* isInTestMode */));
+ resetVcnContext();
setupSystemService(
mContext,
mConnectivityManager,
Context.CONNECTIVITY_SERVICE,
ConnectivityManager.class);
+ setupSystemService(
+ mContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
+ setupSystemService(
+ mContext,
+ mCarrierConfigManager,
+ Context.CARRIER_CONFIG_SERVICE,
+ CarrierConfigManager.class);
when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
@@ -132,6 +150,11 @@
mNetworkTrackerCb);
}
+ private void resetVcnContext() {
+ reset(mVcnContext);
+ doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+ }
+
private static LinkProperties getLinkPropertiesWithName(String iface) {
LinkProperties linkProperties = new LinkProperties();
linkProperties.setInterfaceName(iface);
@@ -149,6 +172,31 @@
verifyNetworkRequestsRegistered(INITIAL_SUB_IDS);
}
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartupForTestMode() {
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
+ setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ final VcnContext vcnContext =
+ new VcnContext(
+ mContext,
+ mTestLooper.getLooper(),
+ mVcnNetworkProvider,
+ true /* isInTestMode */);
+
+ new UnderlyingNetworkTracker(
+ vcnContext,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
+ mNetworkTrackerCb);
+
+ verify(cm)
+ .registerNetworkCallback(
+ eq(getTestNetworkRequest(INITIAL_SUB_IDS)),
+ any(UnderlyingNetworkListener.class),
+ any());
+ }
+
private void verifyNetworkRequestsRegistered(Set<Integer> expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
@@ -163,9 +211,20 @@
}
verify(mConnectivityManager)
- .requestBackgroundNetwork(
+ .registerNetworkCallback(
eq(getRouteSelectionRequest(expectedSubIds)),
- any(RouteSelectionCallback.class), any());
+ any(UnderlyingNetworkListener.class),
+ any());
+ verify(mConnectivityManager)
+ .registerNetworkCallback(
+ eq(getWifiEntryRssiThresholdRequest(expectedSubIds)),
+ any(NetworkBringupCallback.class),
+ any());
+ verify(mConnectivityManager)
+ .registerNetworkCallback(
+ eq(getWifiExitRssiThresholdRequest(expectedSubIds)),
+ any(NetworkBringupCallback.class),
+ any());
}
@Test
@@ -180,9 +239,10 @@
mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate);
// verify that initially-filed bringup requests are unregistered (cell + wifi)
- verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 1))
+ verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 3))
.unregisterNetworkCallback(any(NetworkBringupCallback.class));
- verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class));
+ verify(mConnectivityManager)
+ .unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
verifyNetworkRequestsRegistered(UPDATED_SUB_IDS);
}
@@ -193,6 +253,24 @@
.build();
}
+ private NetworkRequest getWifiEntryRssiThresholdRequest(Set<Integer> netCapsSubIds) {
+ // TODO (b/187991063): Add tests for carrier-config based thresholds
+ return getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setSubscriptionIds(netCapsSubIds)
+ .setSignalStrength(UnderlyingNetworkTracker.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
+ .build();
+ }
+
+ private NetworkRequest getWifiExitRssiThresholdRequest(Set<Integer> netCapsSubIds) {
+ // TODO (b/187991063): Add tests for carrier-config based thresholds
+ return getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setSubscriptionIds(netCapsSubIds)
+ .setSignalStrength(UnderlyingNetworkTracker.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
+ .build();
+ }
+
private NetworkRequest getCellRequestForSubId(int subId) {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
@@ -201,7 +279,19 @@
}
private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) {
- return getExpectedRequestBase().setSubscriptionIds(netCapsSubIds).build();
+ return getExpectedRequestBase()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ .setSubscriptionIds(netCapsSubIds)
+ .build();
+ }
+
+ private NetworkRequest getTestNetworkRequest(Set<Integer> netCapsSubIds) {
+ return new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .setSubscriptionIds(netCapsSubIds)
+ .build();
}
private NetworkRequest.Builder getExpectedRequestBase() {
@@ -219,11 +309,12 @@
public void testTeardown() {
mUnderlyingNetworkTracker.teardown();
- // Expect 3 NetworkBringupCallbacks to be unregistered: 1 for WiFi and 2 for Cellular (1x
- // for each subId)
- verify(mConnectivityManager, times(3))
+ // Expect 5 NetworkBringupCallbacks to be unregistered: 1 for WiFi, 2 for Cellular (1x for
+ // each subId), and 1 for each of the Wifi signal strength thresholds
+ verify(mConnectivityManager, times(5))
.unregisterNetworkCallback(any(NetworkBringupCallback.class));
- verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class));
+ verify(mConnectivityManager)
+ .unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
}
@Test
@@ -256,19 +347,19 @@
verifyRegistrationOnAvailableAndGetCallback();
}
- private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback() {
+ private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback() {
return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
}
- private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback(
+ private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback(
NetworkCapabilities networkCapabilities) {
verify(mConnectivityManager)
- .requestBackgroundNetwork(
+ .registerNetworkCallback(
eq(getRouteSelectionRequest(INITIAL_SUB_IDS)),
- mRouteSelectionCallbackCaptor.capture(),
+ mUnderlyingNetworkListenerCaptor.capture(),
any());
- RouteSelectionCallback cb = mRouteSelectionCallbackCaptor.getValue();
+ UnderlyingNetworkListener cb = mUnderlyingNetworkListenerCaptor.getValue();
cb.onAvailable(mNetwork);
cb.onCapabilitiesChanged(mNetwork, networkCapabilities);
cb.onLinkPropertiesChanged(mNetwork, INITIAL_LINK_PROPERTIES);
@@ -286,7 +377,7 @@
@Test
public void testRecordTrackerCallbackNotifiedForNetworkCapabilitiesChange() {
- RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+ UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES);
@@ -301,7 +392,7 @@
@Test
public void testRecordTrackerCallbackNotifiedForLinkPropertiesChange() {
- RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+ UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES);
@@ -316,7 +407,7 @@
@Test
public void testRecordTrackerCallbackNotifiedForNetworkSuspended() {
- RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+ UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onCapabilitiesChanged(mNetwork, SUSPENDED_NETWORK_CAPABILITIES);
@@ -335,7 +426,7 @@
@Test
public void testRecordTrackerCallbackNotifiedForNetworkResumed() {
- RouteSelectionCallback cb =
+ UnderlyingNetworkListener cb =
verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES);
cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
@@ -355,7 +446,7 @@
@Test
public void testRecordTrackerCallbackNotifiedForBlocked() {
- RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+ UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
@@ -370,7 +461,7 @@
@Test
public void testRecordTrackerCallbackNotifiedForNetworkLoss() {
- RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+ UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onLost(mNetwork);
@@ -379,7 +470,7 @@
@Test
public void testRecordTrackerCallbackIgnoresDuplicateRecord() {
- RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+ UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
@@ -387,4 +478,6 @@
// UnderlyingNetworkRecord does not actually change
verifyNoMoreInteractions(mNetworkTrackerCb);
}
+
+ // TODO (b/187991063): Add tests for network prioritization
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 1ecb4c9..c747bc0 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -54,6 +54,7 @@
import android.os.ParcelUuid;
import android.os.PowerManager;
import android.os.test.TestLooper;
+import android.telephony.SubscriptionInfo;
import com.android.internal.util.State;
import com.android.internal.util.WakeupMessage;
@@ -73,6 +74,12 @@
public class VcnGatewayConnectionTestBase {
protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
+ protected static final SubscriptionInfo TEST_SUB_INFO = mock(SubscriptionInfo.class);
+
+ static {
+ doReturn(TEST_SUB_GRP).when(TEST_SUB_INFO).getGroupUuid();
+ }
+
protected static final InetAddress TEST_DNS_ADDR =
InetAddresses.parseNumericAddress("2001:DB8:0:1::");
protected static final InetAddress TEST_DNS_ADDR_2 =
@@ -116,7 +123,7 @@
protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
new TelephonySubscriptionSnapshot(
- Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
+ Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO), Collections.EMPTY_MAP);
@NonNull protected final Context mContext;
@NonNull protected final TestLooper mTestLooper;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index f2c3b86..812e208 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1121,8 +1121,8 @@
// Skip all "uses-sdk" tags besides the very last tag. The android runtime only uses
// the attribute values from the last defined tag.
- for (size_t i = 0; i < usesSdkTagPositions.size() - 1; i++) {
- tagsToSkip.emplace_back(usesSdkTagPositions[i]);
+ for (size_t i = 1; i < usesSdkTagPositions.size(); i++) {
+ tagsToSkip.emplace_back(usesSdkTagPositions[i - 1]);
}
// Reset the position before parsing.
diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp
index 5c47e0f..4e8dcb1 100644
--- a/tools/aapt/pseudolocalize.cpp
+++ b/tools/aapt/pseudolocalize.cpp
@@ -194,7 +194,8 @@
break;
}
}
- result.remove(length + ext, 0);
+ // Just keep the first length + ext characters
+ result = String16(result, length + ext);
}
return result;
}
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 77c0872..ef3a62f 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -278,17 +278,19 @@
printer->Println(StringPrintf(" entryCount=%zd", type.entries.size()));
printer->Indent();
- for (const ResourceEntry* entry : type.entries) {
+ for (const ResourceTableEntryView& entry : type.entries) {
printer->Print("resource ");
- printer->Print(entry->id.value_or_default(0).to_string());
+ printer->Print(ResourceId(package.id.value_or_default(0), type.id.value_or_default(0),
+ entry.id.value_or_default(0))
+ .to_string());
printer->Print(" ");
// Write the name without the package (this is obvious and too verbose).
printer->Print(to_string(type.type));
printer->Print("/");
- printer->Print(entry->name);
+ printer->Print(entry.name);
- switch (entry->visibility.level) {
+ switch (entry.visibility.level) {
case Visibility::Level::kPublic:
printer->Print(" PUBLIC");
break;
@@ -300,19 +302,24 @@
break;
}
- if (entry->visibility.staged_api) {
+ if (entry.visibility.staged_api) {
printer->Print(" STAGED");
}
- if (entry->overlayable_item) {
+ if (entry.overlayable_item) {
printer->Print(" OVERLAYABLE");
}
+ if (entry.staged_id) {
+ printer->Print(" STAGED_ID=");
+ printer->Print(entry.staged_id.value().id.to_string());
+ }
+
printer->Println();
if (options.show_values) {
printer->Indent();
- for (const auto& value : entry->values) {
+ for (const auto& value : entry.values) {
printer->Print("(");
printer->Print(value->config.to_string());
printer->Print(") ");
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1efabbb..f1e2da9 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -45,6 +45,7 @@
namespace {
constexpr const char* kPublicGroupTag = "public-group";
constexpr const char* kStagingPublicGroupTag = "staging-public-group";
+constexpr const char* kStagingPublicGroupFinalTag = "staging-public-group-final";
} // namespace
constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
@@ -109,6 +110,7 @@
bool staged_api = false;
bool allow_new = false;
Maybe<OverlayableItem> overlayable_item;
+ Maybe<StagedId> staged_alias;
std::string comment;
std::unique_ptr<Value> value;
@@ -155,6 +157,10 @@
res_builder.SetValue(std::move(res->value), res->config, res->product);
}
+ if (res->staged_alias) {
+ res_builder.SetStagedId(res->staged_alias.value());
+ }
+
bool error = false;
if (!res->name.entry.empty()) {
if (!table->AddResource(res_builder.Build(), diag)) {
@@ -532,6 +538,7 @@
{"public", std::mem_fn(&ResourceParser::ParsePublic)},
{"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
{"staging-public-group", std::mem_fn(&ResourceParser::ParseStagingPublicGroup)},
+ {"staging-public-group-final", std::mem_fn(&ResourceParser::ParseStagingPublicGroupFinal)},
{"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
{"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle,
std::placeholders::_2, std::placeholders::_3)},
@@ -671,7 +678,7 @@
if (bag_iter != elToBagMap.end()) {
// Ensure we have a name (unless this is a <public-group> or <overlayable>).
if (resource_type != kPublicGroupTag && resource_type != kStagingPublicGroupTag &&
- resource_type != "overlayable") {
+ resource_type != kStagingPublicGroupFinalTag && resource_type != "overlayable") {
if (!maybe_name) {
diag_->Error(DiagMessage(out_resource->source)
<< "<" << parser->element_name() << "> missing 'name' attribute");
@@ -1034,7 +1041,6 @@
ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{
.name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()},
.source = item_source,
- .id = next_id,
.comment = std::move(comment),
});
@@ -1060,6 +1066,14 @@
});
}
+bool ResourceParser::ParseStagingPublicGroupFinal(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ return ParseGroupImpl(parser, out_resource, kStagingPublicGroupFinalTag, diag_,
+ [](ParsedResource& parsed_entry, ResourceId id) {
+ parsed_entry.staged_alias = StagedId{id, parsed_entry.source};
+ });
+}
+
bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (options_.visibility) {
diag_->Error(DiagMessage(out_resource->source)
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 5c92def..2614997 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -115,6 +115,7 @@
bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseStagingPublicGroupFinal(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 45ea654..8ab1493 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -83,6 +83,20 @@
return action(found, iter);
}
+struct ConfigKey {
+ const ConfigDescription* config;
+ const StringPiece& product;
+};
+
+template <typename T>
+bool lt_config_key_ref(const T& lhs, const ConfigKey& rhs) {
+ int cmp = lhs->config.compare(*rhs.config);
+ if (cmp == 0) {
+ cmp = StringPiece(lhs->product).compare(rhs.product);
+ }
+ return cmp < 0;
+}
+
} // namespace
ResourceTable::ResourceTable(ResourceTable::Validation validation) : validation_(validation) {
@@ -134,23 +148,10 @@
});
}
-struct ConfigKey {
- const ConfigDescription* config;
- const StringPiece& product;
-};
-
-bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
- int cmp = lhs->config.compare(*rhs.config);
- if (cmp == 0) {
- cmp = StringPiece(lhs->product).compare(rhs.product);
- }
- return cmp < 0;
-}
-
ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
android::StringPiece product) {
auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
- lt_config_key_ref);
+ lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>);
if (iter != values.end()) {
ResourceConfigValue* value = iter->get();
if (value->config == config && StringPiece(value->product) == product) {
@@ -163,7 +164,7 @@
const ResourceConfigValue* ResourceEntry::FindValue(const android::ConfigDescription& config,
android::StringPiece product) const {
auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
- lt_config_key_ref);
+ lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>);
if (iter != values.end()) {
ResourceConfigValue* value = iter->get();
if (value->config == config && StringPiece(value->product) == product) {
@@ -176,7 +177,7 @@
ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
const StringPiece& product) {
auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
- lt_config_key_ref);
+ lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>);
if (iter != values.end()) {
ResourceConfigValue* value = iter->get();
if (value->config == config && StringPiece(value->product) == product) {
@@ -296,6 +297,7 @@
return CollisionResult::kConflict;
}
+namespace {
template <typename T, typename Comparer>
struct SortedVectorInserter : public Comparer {
std::pair<bool, typename std::vector<T>::iterator> LowerBound(std::vector<T>& el,
@@ -313,7 +315,7 @@
if (found) {
return &*it;
}
- return &*el.insert(it, std::move(value));
+ return &*el.insert(it, std::forward<T>(value));
}
};
@@ -331,35 +333,77 @@
};
struct EntryViewComparer {
- bool operator()(const ResourceEntry* lhs, const ResourceEntry* rhs) {
- return less_than_struct_with_name_and_id<ResourceEntry, ResourceId>(
- *lhs, std::make_pair(rhs->name, rhs->id));
+ bool operator()(const ResourceTableEntryView& lhs, const ResourceTableEntryView& rhs) {
+ return less_than_struct_with_name_and_id<ResourceTableEntryView, uint16_t>(
+ lhs, std::make_pair(rhs.name, rhs.id));
}
};
-ResourceTableView ResourceTable::GetPartitionedView() const {
- ResourceTableView view;
+void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePackage* package,
+ const ResourceTableType* type, const std::string& entry_name,
+ const Maybe<ResourceId>& id, const Visibility& visibility,
+ const Maybe<AllowNew>& allow_new,
+ const Maybe<OverlayableItem>& overlayable_item,
+ const Maybe<StagedId>& staged_id,
+ const std::vector<std::unique_ptr<ResourceConfigValue>>& values) {
SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
- SortedVectorInserter<const ResourceEntry*, EntryViewComparer> entry_inserter;
+ SortedVectorInserter<ResourceTableEntryView, EntryViewComparer> entry_inserter;
+ ResourceTablePackageView new_package{package->name,
+ id ? id.value().package_id() : Maybe<uint8_t>{}};
+ auto view_package = package_inserter.Insert(table.packages, std::move(new_package));
+
+ ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : Maybe<uint8_t>{}};
+ auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
+
+ if (visibility.level == Visibility::Level::kPublic) {
+ // Only mark the type visibility level as public, it doesn't care about being private.
+ view_type->visibility_level = Visibility::Level::kPublic;
+ }
+
+ ResourceTableEntryView new_entry{.name = entry_name,
+ .id = id ? id.value().entry_id() : Maybe<uint16_t>{},
+ .visibility = visibility,
+ .allow_new = allow_new,
+ .overlayable_item = overlayable_item,
+ .staged_id = staged_id};
+ for (auto& value : values) {
+ new_entry.values.emplace_back(value.get());
+ }
+
+ entry_inserter.Insert(view_type->entries, std::move(new_entry));
+}
+} // namespace
+
+const ResourceConfigValue* ResourceTableEntryView::FindValue(const ConfigDescription& config,
+ android::StringPiece product) const {
+ auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
+ lt_config_key_ref<const ResourceConfigValue*>);
+ if (iter != values.end()) {
+ const ResourceConfigValue* value = *iter;
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
+ }
+ }
+ return nullptr;
+}
+
+ResourceTableView ResourceTable::GetPartitionedView(const ResourceTableViewOptions& options) const {
+ ResourceTableView view;
for (const auto& package : packages) {
for (const auto& type : package->types) {
for (const auto& entry : type->entries) {
- ResourceTablePackageView new_package{
- package->name, entry->id ? entry->id.value().package_id() : Maybe<uint8_t>{}};
- auto view_package = package_inserter.Insert(view.packages, std::move(new_package));
+ InsertEntryIntoTableView(view, package.get(), type.get(), entry->name, entry->id,
+ entry->visibility, entry->allow_new, entry->overlayable_item,
+ entry->staged_id, entry->values);
- ResourceTableTypeView new_type{type->type,
- entry->id ? entry->id.value().type_id() : Maybe<uint8_t>{}};
- auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
-
- if (entry->visibility.level == Visibility::Level::kPublic) {
- // Only mark the type visibility level as public, it doesn't care about being private.
- view_type->visibility_level = Visibility::Level::kPublic;
+ if (options.create_alias_entries && entry->staged_id) {
+ auto alias_id = entry->staged_id.value().id;
+ InsertEntryIntoTableView(view, package.get(), type.get(), entry->name, alias_id,
+ entry->visibility, entry->allow_new, entry->overlayable_item, {},
+ entry->values);
}
-
- entry_inserter.Insert(view_type->entries, entry.get());
}
}
}
@@ -368,6 +412,8 @@
// for the same resource type within the same package. For this reason, if there are types with
// multiple type ids, each type needs to exist in its own package in order to be queried by name.
std::vector<ResourceTablePackageView> new_packages;
+ SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
+ SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
for (auto& package : view.packages) {
// If a new package was already created for a different type within this package, then
// we can reuse those packages for other types that need to be extracted from this package.
@@ -498,6 +544,10 @@
entry->allow_new = res.allow_new.value();
}
+ if (res.staged_id.has_value()) {
+ entry->staged_id = res.staged_id.value();
+ }
+
if (res.value != nullptr) {
auto config_value = entry->FindOrCreateValue(res.config, res.product);
if (!config_value->value) {
@@ -575,6 +625,28 @@
return {};
}
+bool ResourceTable::RemoveResource(const ResourceNameRef& name, ResourceId id) const {
+ ResourceTablePackage* package = FindPackage(name.package);
+ if (package == nullptr) {
+ return {};
+ }
+
+ ResourceTableType* type = package->FindType(name.type);
+ if (type == nullptr) {
+ return {};
+ }
+
+ auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), name.entry,
+ NameEqualRange<ResourceEntry>{});
+ for (auto it = entry_it.first; it != entry_it.second; ++it) {
+ if ((*it)->id == id) {
+ type->entries.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
CloningValueTransformer cloner(&new_table->string_pool);
@@ -640,6 +712,11 @@
return *this;
}
+NewResourceBuilder& NewResourceBuilder::SetStagedId(StagedId staged_alias) {
+ res_.staged_id = std::move(staged_alias);
+ return *this;
+}
+
NewResourceBuilder& NewResourceBuilder::SetAllowMangled(bool allow_mangled) {
res_.allow_mangled = allow_mangled;
return *this;
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 080ecc2..bae1d82 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -64,6 +64,12 @@
std::string comment;
};
+// Represents the staged resource id of a finalized resource.
+struct StagedId {
+ ResourceId id;
+ Source source;
+};
+
struct Overlayable {
Overlayable() = default;
Overlayable(const android::StringPiece& name, const android::StringPiece& actor)
@@ -124,6 +130,9 @@
// The declarations of this resource as overlayable for RROs
Maybe<OverlayableItem> overlayable_item;
+ // The staged resource id for a finalized resource.
+ Maybe<StagedId> staged_id;
+
// The resource's values for each configuration.
std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -194,14 +203,27 @@
DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
};
+struct ResourceTableEntryView {
+ std::string name;
+ Maybe<uint16_t> id;
+ Visibility visibility;
+ Maybe<AllowNew> allow_new;
+ Maybe<OverlayableItem> overlayable_item;
+ Maybe<StagedId> staged_id;
+ std::vector<const ResourceConfigValue*> values;
+
+ const ResourceConfigValue* FindValue(const android::ConfigDescription& config,
+ android::StringPiece product = {}) const;
+};
+
struct ResourceTableTypeView {
ResourceType type;
Maybe<uint8_t> id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
// Entries sorted in ascending entry id order. If ids have not been assigned, the entries are
- // // sorted lexicographically.
- std::vector<const ResourceEntry*> entries;
+ // sorted lexicographically.
+ std::vector<ResourceTableEntryView> entries;
};
struct ResourceTablePackageView {
@@ -212,6 +234,10 @@
std::vector<ResourceTableTypeView> types;
};
+struct ResourceTableViewOptions {
+ bool create_alias_entries = false;
+};
+
struct ResourceTableView {
// Packages sorted in ascending package id order. If ids have not been assigned, the packages are
// sorted lexicographically.
@@ -237,6 +263,7 @@
std::optional<Visibility> visibility;
std::optional<OverlayableItem> overlayable;
std::optional<AllowNew> allow_new;
+ std::optional<StagedId> staged_id;
bool allow_mangled = false;
};
@@ -249,6 +276,7 @@
NewResourceBuilder& SetVisibility(Visibility id);
NewResourceBuilder& SetOverlayable(OverlayableItem overlayable);
NewResourceBuilder& SetAllowNew(AllowNew allow_new);
+ NewResourceBuilder& SetStagedId(StagedId id);
NewResourceBuilder& SetAllowMangled(bool allow_mangled);
NewResource Build();
@@ -273,7 +301,7 @@
// Retrieves a sorted a view of the packages, types, and entries sorted in ascending resource id
// order.
- ResourceTableView GetPartitionedView() const;
+ ResourceTableView GetPartitionedView(const ResourceTableViewOptions& options = {}) const;
struct SearchResult {
ResourceTablePackage* package;
@@ -283,6 +311,7 @@
Maybe<SearchResult> FindResource(const ResourceNameRef& name) const;
Maybe<SearchResult> FindResource(const ResourceNameRef& name, ResourceId id) const;
+ bool RemoveResource(const ResourceNameRef& name, ResourceId id) const;
// Returns the package struct with the given name, or nullptr if such a package does not
// exist. The empty string is a valid package and typically is used to represent the
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index b45c040..95b7949 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -190,6 +190,12 @@
uint32 overlayable_idx = 4;
}
+// The staged resource ID definition of a finalized resource.
+message StagedId {
+ Source source = 1;
+ uint32 staged_id = 2;
+}
+
// An entry ID in the range [0x0000, 0xffff].
message EntryId {
uint32 id = 1;
@@ -222,6 +228,9 @@
// The set of values defined for this entry, each corresponding to a different
// configuration/variant.
repeated ConfigValue config_value = 6;
+
+ // The staged resource ID of this finalized resource.
+ StagedId staged_id = 7;
}
// A Configuration/Value pair.
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index df31087..3950f33 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -97,15 +97,15 @@
static bool EmitResourceConfigValueDiff(
IAaptContext* context, LoadedApk* apk_a, const ResourceTablePackageView& pkg_a,
- const ResourceTableTypeView& type_a, const ResourceEntry* entry_a,
+ const ResourceTableTypeView& type_a, const ResourceTableEntryView& entry_a,
const ResourceConfigValue* config_value_a, LoadedApk* apk_b,
const ResourceTablePackageView& pkg_b, const ResourceTableTypeView& type_b,
- const ResourceEntry* entry_b, const ResourceConfigValue* config_value_b) {
+ const ResourceTableEntryView& entry_b, const ResourceConfigValue* config_value_b) {
Value* value_a = config_value_a->value.get();
Value* value_b = config_value_b->value.get();
if (!value_a->Equals(value_b)) {
std::stringstream str_stream;
- str_stream << "value " << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
+ str_stream << "value " << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
<< " config=" << config_value_a->config << " does not match:\n";
value_a->Print(&str_stream);
str_stream << "\n vs \n";
@@ -118,32 +118,32 @@
static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a,
const ResourceTablePackageView& pkg_a,
- const ResourceTableTypeView& type_a, const ResourceEntry* entry_a,
- LoadedApk* apk_b, const ResourceTablePackageView& pkg_b,
+ const ResourceTableTypeView& type_a,
+ const ResourceTableEntryView& entry_a, LoadedApk* apk_b,
+ const ResourceTablePackageView& pkg_b,
const ResourceTableTypeView& type_b,
- const ResourceEntry* entry_b) {
+ const ResourceTableEntryView& entry_b) {
bool diff = false;
- for (const std::unique_ptr<ResourceConfigValue>& config_value_a : entry_a->values) {
- auto config_value_b = entry_b->FindValue(config_value_a->config);
+ for (const ResourceConfigValue* config_value_a : entry_a.values) {
+ auto config_value_b = entry_b.FindValue(config_value_a->config);
if (!config_value_b) {
std::stringstream str_stream;
- str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
+ str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
<< " config=" << config_value_a->config;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else {
- diff |=
- EmitResourceConfigValueDiff(context, apk_a, pkg_a, type_a, entry_a, config_value_a.get(),
- apk_b, pkg_b, type_b, entry_b, config_value_b);
+ diff |= EmitResourceConfigValueDiff(context, apk_a, pkg_a, type_a, entry_a, config_value_a,
+ apk_b, pkg_b, type_b, entry_b, config_value_b);
}
}
// Check for any newly added config values.
- for (const std::unique_ptr<ResourceConfigValue>& config_value_b : entry_b->values) {
- auto config_value_a = entry_a->FindValue(config_value_b->config);
+ for (const ResourceConfigValue* config_value_b : entry_b.values) {
+ auto config_value_a = entry_a.FindValue(config_value_b->config);
if (!config_value_a) {
std::stringstream str_stream;
- str_stream << "new config " << pkg_b.name << ":" << type_b.type << "/" << entry_b->name
+ str_stream << "new config " << pkg_b.name << ":" << type_b.type << "/" << entry_b.name
<< " config=" << config_value_b->config;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
@@ -164,36 +164,35 @@
if (entry_b_iter == type_b.entries.end()) {
// Type A contains a type that type B does not have.
std::stringstream str_stream;
- str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << (*entry_a_iter)->name;
+ str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a_iter->name;
EmitDiffLine(apk_a->GetSource(), str_stream.str());
diff = true;
} else if (entry_a_iter == type_a.entries.end()) {
// Type B contains a type that type A does not have.
std::stringstream str_stream;
- str_stream << "new entry " << pkg_b.name << ":" << type_b.type << "/"
- << (*entry_b_iter)->name;
+ str_stream << "new entry " << pkg_b.name << ":" << type_b.type << "/" << entry_b_iter->name;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else {
const auto& entry_a = *entry_a_iter;
const auto& entry_b = *entry_b_iter;
- if (IsSymbolVisibilityDifferent(entry_a->visibility, entry_b->visibility)) {
+ if (IsSymbolVisibilityDifferent(entry_a.visibility, entry_b.visibility)) {
std::stringstream str_stream;
- str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
+ str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
<< " has different visibility (";
- if (entry_b->visibility.staged_api) {
+ if (entry_b.visibility.staged_api) {
str_stream << "STAGED ";
}
- if (entry_b->visibility.level == Visibility::Level::kPublic) {
+ if (entry_b.visibility.level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
} else {
str_stream << "PRIVATE";
}
str_stream << " vs ";
- if (entry_a->visibility.staged_api) {
+ if (entry_a.visibility.staged_api) {
str_stream << "STAGED ";
}
- if (entry_a->visibility.level == Visibility::Level::kPublic) {
+ if (entry_a.visibility.level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
} else {
str_stream << "PRIVATE";
@@ -201,19 +200,19 @@
str_stream << ")";
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
- } else if (IsIdDiff(entry_a->visibility.level, entry_a->id, entry_b->visibility.level,
- entry_b->id)) {
+ } else if (IsIdDiff(entry_a.visibility.level, entry_a.id, entry_b.visibility.level,
+ entry_b.id)) {
std::stringstream str_stream;
- str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
+ str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
<< " has different public ID (";
- if (entry_b->id) {
- str_stream << "0x" << std::hex << entry_b->id.value();
+ if (entry_b.id) {
+ str_stream << "0x" << std::hex << entry_b.id.value();
} else {
str_stream << "none";
}
str_stream << " vs ";
- if (entry_a->id) {
- str_stream << "0x " << std::hex << entry_a->id.value();
+ if (entry_a.id) {
+ str_stream << "0x " << std::hex << entry_a.id.value();
} else {
str_stream << "none";
}
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 3118eb8..430c184 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -402,8 +402,39 @@
EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
}
-TEST_F(LinkTest, StagedAndroidApi) {
- StdErrDiagnostics diag;
+struct SourceXML {
+ std::string res_file_path;
+ std::string file_contents;
+};
+
+static void BuildApk(const std::vector<SourceXML>& source_files, const std::string& apk_path,
+ LinkCommandBuilder&& link_args, CommandTestFixture* fixture,
+ IDiagnostics* diag) {
+ TemporaryDir res_dir;
+ TemporaryDir compiled_res_dir;
+ for (auto& source_file : source_files) {
+ ASSERT_TRUE(fixture->CompileFile(res_dir.path + source_file.res_file_path,
+ source_file.file_contents, compiled_res_dir.path, diag));
+ }
+ ASSERT_TRUE(fixture->Link(
+ link_args.AddCompiledResDir(compiled_res_dir.path, diag).Build(apk_path), diag));
+}
+
+static void BuildSDK(const std::vector<SourceXML>& source_files, const std::string& apk_path,
+ const std::string& java_root_path, CommandTestFixture* fixture,
+ IDiagnostics* diag) {
+ auto android_manifest = ManifestBuilder(fixture).SetPackageName("android").Build();
+
+ auto android_link_args = LinkCommandBuilder(fixture)
+ .SetManifestFile(android_manifest)
+ .AddParameter("--private-symbols", "com.android.internal")
+ .AddParameter("--java", java_root_path);
+
+ BuildApk(source_files, apk_path, std::move(android_link_args), fixture, diag);
+}
+
+static void BuildNonFinalizedSDK(const std::string& apk_path, const std::string& java_path,
+ CommandTestFixture* fixture, IDiagnostics* diag) {
const std::string android_values =
R"(<resources>
<public type="attr" name="finalized_res" id="0x01010001"/>
@@ -413,6 +444,10 @@
<public name="staged_s_res" />
</staging-public-group>
+ <staging-public-group type="string" first-id="0x01fd0080">
+ <public name="staged_s_string" />
+ </staging-public-group>
+
<!-- SV2 staged attributes (support staged resources in a separate type id) -->
<staging-public-group type="attr" first-id="0x01ff0049">
<public name="staged_s2_res" />
@@ -423,46 +458,90 @@
<public name="staged_t_res" />
</staging-public-group>
- <staging-public-group type="string" first-id="0x01fd0072">
- <public name="staged_t_string" />
+ <attr name="finalized_res" />
+ <attr name="staged_s_res" />
+ <attr name="staged_s2_res" />
+ <attr name="staged_t_res" />
+ <string name="staged_s_string">Hello</string>
+ </resources>)";
+
+ SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
+ BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
+}
+
+static void BuildFinalizedSDK(const std::string& apk_path, const std::string& java_path,
+ CommandTestFixture* fixture, IDiagnostics* diag) {
+ const std::string android_values =
+ R"(<resources>
+ <public type="attr" name="finalized_res" id="0x01010001"/>
+ <public type="attr" name="staged_s_res" id="0x01010002"/>
+ <public type="attr" name="staged_s2_res" id="0x01010003"/>
+ <public type="string" name="staged_s_string" id="0x01020000"/>
+
+ <!-- S staged attributes (support staged resources in the same type id) -->
+ <staging-public-group-final type="attr" first-id="0x01010050">
+ <public name="staged_s_res" />
+ </staging-public-group-final>
+
+ <staging-public-group-final type="string" first-id="0x01fd0080">
+ <public name="staged_s_string" />
+ </staging-public-group-final>
+
+ <!-- SV2 staged attributes (support staged resources in a separate type id) -->
+ <staging-public-group-final type="attr" first-id="0x01ff0049">
+ <public name="staged_s2_res" />
+ </staging-public-group-final>
+
+ <!-- T staged attributes (support staged resources in multiple separate type ids) -->
+ <staging-public-group type="attr" first-id="0x01fe0063">
+ <public name="staged_t_res" />
</staging-public-group>
<attr name="finalized_res" />
<attr name="staged_s_res" />
<attr name="staged_s2_res" />
<attr name="staged_t_res" />
- <string name="staged_t_string">Hello</string>
+ <string name="staged_s_string">Hello</string>
</resources>)";
+ SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
+ BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
+}
+
+static void BuildAppAgainstSDK(const std::string& apk_path, const std::string& java_path,
+ const std::string& sdk_path, CommandTestFixture* fixture,
+ IDiagnostics* diag) {
const std::string app_values =
R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
<attr name="bar" />
+ <style name="MyStyle">
+ <item name="android:staged_s_res">@android:string/staged_s_string</item>
+ </style>
<declare-styleable name="ClientStyleable">
<attr name="android:finalized_res" />
<attr name="android:staged_s_res" />
<attr name="bar" />
</declare-styleable>
+ <public name="MyStyle" type="style" id="0x7f020000" />
</resources>)";
- const std::string android_res = GetTestPath("android-res");
- ASSERT_TRUE(
- CompileFile(GetTestPath("res/values/values.xml"), android_values, android_res, &diag));
+ SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = app_values};
+ auto app_manifest = ManifestBuilder(fixture).SetPackageName("com.example.app").Build();
+
+ auto app_link_args = LinkCommandBuilder(fixture)
+ .SetManifestFile(app_manifest)
+ .AddParameter("--java", java_path)
+ .AddParameter("-I", sdk_path);
+
+ BuildApk({source_xml}, apk_path, std::move(app_link_args), fixture, diag);
+}
+
+TEST_F(LinkTest, StagedAndroidApi) {
+ StdErrDiagnostics diag;
const std::string android_apk = GetTestPath("android.apk");
- const std::string android_java = GetTestPath("android_java");
- // clang-format off
- auto android_manifest = ManifestBuilder(this)
- .SetPackageName("android")
- .Build();
-
- auto android_link_args = LinkCommandBuilder(this)
- .SetManifestFile(android_manifest)
- .AddParameter("--private-symbols", "com.android.internal")
- .AddParameter("--java", android_java)
- .AddCompiledResDir(android_res, &diag)
- .Build(android_apk);
- // clang-format on
- ASSERT_TRUE(Link(android_link_args, &diag));
+ const std::string android_java = GetTestPath("android-java");
+ BuildNonFinalizedSDK(android_apk, android_java, this, &diag);
const std::string android_r_java = android_java + "/android/R.java";
std::string android_r_contents;
@@ -473,33 +552,17 @@
HasSubstr("public static final int staged_s_res; static { staged_s_res=0x01010050; }"));
EXPECT_THAT(
android_r_contents,
+ HasSubstr("public static final int staged_s_string; static { staged_s_string=0x01fd0080; }"));
+ EXPECT_THAT(
+ android_r_contents,
HasSubstr("public static final int staged_s2_res; static { staged_s2_res=0x01ff0049; }"));
EXPECT_THAT(
android_r_contents,
HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
- EXPECT_THAT(
- android_r_contents,
- HasSubstr("public static final int staged_t_string; static { staged_t_string=0x01fd0072; }"));
-
- // Build an app that uses the framework attribute in a declare-styleable
- const std::string client_res = GetTestPath("app-res");
- ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), app_values, client_res, &diag));
const std::string app_apk = GetTestPath("app.apk");
- const std::string app_java = GetTestPath("app_java");
- // clang-format off
- auto app_manifest = ManifestBuilder(this)
- .SetPackageName("com.example.app")
- .Build();
-
- auto app_link_args = LinkCommandBuilder(this)
- .SetManifestFile(app_manifest)
- .AddParameter("--java", app_java)
- .AddParameter("-I", android_apk)
- .AddCompiledResDir(client_res, &diag)
- .Build(app_apk);
- // clang-format on
- ASSERT_TRUE(Link(app_link_args, &diag));
+ const std::string app_java = GetTestPath("app-java");
+ BuildAppAgainstSDK(app_apk, app_java, android_apk, this, &diag);
const std::string client_r_java = app_java + "/com/example/app/R.java";
std::string client_r_contents;
@@ -520,6 +583,10 @@
ASSERT_TRUE(result.has_value());
EXPECT_THAT(*result, Eq(0x01010050));
+ result = am.GetResourceId("android:string/staged_s_string");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01fd0080));
+
result = am.GetResourceId("android:attr/staged_s2_res");
ASSERT_TRUE(result.has_value());
EXPECT_THAT(*result, Eq(0x01ff0049));
@@ -527,10 +594,88 @@
result = am.GetResourceId("android:attr/staged_t_res");
ASSERT_TRUE(result.has_value());
EXPECT_THAT(*result, Eq(0x01fe0063));
+}
- result = am.GetResourceId("android:string/staged_t_string");
+TEST_F(LinkTest, FinalizedAndroidApi) {
+ StdErrDiagnostics diag;
+ const std::string android_apk = GetTestPath("android.apk");
+ const std::string android_java = GetTestPath("android-java");
+ BuildFinalizedSDK(android_apk, android_java, this, &diag);
+
+ const std::string android_r_java = android_java + "/android/R.java";
+ std::string android_r_contents;
+ ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
+ EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
+ EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_res=0x01010002;"));
+ EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_string=0x01020000;"));
+ EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s2_res=0x01010003;"));
+ EXPECT_THAT(
+ android_r_contents,
+ HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
+ ;
+
+ // Build an application against the non-finalized SDK and then load it into an AssetManager with
+ // the finalized SDK.
+ const std::string non_finalized_android_apk = GetTestPath("non-finalized-android.apk");
+ const std::string non_finalized_android_java = GetTestPath("non-finalized-android-java");
+ BuildNonFinalizedSDK(non_finalized_android_apk, non_finalized_android_java, this, &diag);
+
+ const std::string app_apk = GetTestPath("app.apk");
+ const std::string app_java = GetTestPath("app-java");
+ BuildAppAgainstSDK(app_apk, app_java, non_finalized_android_apk, this, &diag);
+
+ android::AssetManager2 am;
+ auto android_asset = android::ApkAssets::Load(android_apk);
+ auto app_against_non_final = android::ApkAssets::Load(app_apk);
+ ASSERT_THAT(android_asset, NotNull());
+ ASSERT_THAT(app_against_non_final, NotNull());
+ ASSERT_TRUE(am.SetApkAssets({android_asset.get(), app_against_non_final.get()}));
+
+ auto result = am.GetResourceId("android:attr/finalized_res");
ASSERT_TRUE(result.has_value());
- EXPECT_THAT(*result, Eq(0x01fd0072));
+ EXPECT_THAT(*result, Eq(0x01010001));
+
+ result = am.GetResourceId("android:attr/staged_s_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01010002));
+
+ result = am.GetResourceId("android:string/staged_s_string");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01020000));
+
+ result = am.GetResourceId("android:attr/staged_s2_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01010003));
+
+ {
+ auto style = am.GetBag(0x7f020000);
+ ASSERT_TRUE(style.has_value());
+
+ auto& entry = (*style)->entries[0];
+ EXPECT_THAT(entry.key, Eq(0x01010002));
+ EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
+ EXPECT_THAT(entry.value.data, Eq(0x01020000));
+ }
+
+ // Re-compile the application against the finalized SDK and then load it into an AssetManager with
+ // the finalized SDK.
+ const std::string app_apk_respin = GetTestPath("app-respin.apk");
+ const std::string app_java_respin = GetTestPath("app-respin-java");
+ BuildAppAgainstSDK(app_apk_respin, app_java_respin, android_apk, this, &diag);
+
+ auto app_against_final = android::ApkAssets::Load(app_apk_respin);
+ ASSERT_THAT(app_against_final, NotNull());
+ ASSERT_TRUE(am.SetApkAssets({android_asset.get(), app_against_final.get()}));
+
+ {
+ auto style = am.GetBag(0x7f020000);
+ ASSERT_TRUE(style.has_value());
+
+ auto& entry = (*style)->entries[0];
+ EXPECT_THAT(entry.key, Eq(0x01010002));
+ EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
+ EXPECT_THAT(entry.value.data, Eq(0x01020000));
+ }
}
TEST_F(LinkTest, MacroSubstitution) {
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 9a50b26..339b8af 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -129,11 +129,16 @@
for (auto& type : package->types) {
for (auto& entry : type->entries) {
const ResourceName name(package->name, type->type, entry->name);
- if (entry->id) {
- if (!assigned_ids.ReserveId(name, entry->id.value(), entry->visibility,
- context->GetDiagnostics())) {
- return false;
- }
+ if (entry->id && !assigned_ids.ReserveId(name, entry->id.value(), entry->visibility,
+ context->GetDiagnostics())) {
+ return false;
+ }
+
+ auto v = entry->visibility;
+ v.staged_api = true;
+ if (entry->staged_id && !assigned_ids.ReserveId(name, entry->staged_id.value().id, v,
+ context->GetDiagnostics())) {
+ return false;
}
if (assigned_id_map_) {
@@ -237,7 +242,7 @@
if (type_id_ != id.type_id()) {
// Currently there cannot be multiple type ids for a single type.
std::stringstream error;
- error << "type '" << name.type << "' already has ID " << std::hex << (int)id.type_id();
+ error << "type '" << name.type << "' already has ID " << std::hex << (int)type_id_;
return unexpected(error.str());
}
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 61ba09b..f2c6b15 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -135,7 +135,8 @@
template <typename Predicate>
void Filter(Predicate&& func) {
children_.erase(std::remove_if(children_.begin(), children_.end(),
- [&](const auto& e) { return func(e.get()); }));
+ [&](const auto& e) { return func(e.get()); }),
+ children_.end());
}
/** Retrieves the list of children of the element. */
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index f1b350f..72eaa35 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -254,6 +254,12 @@
}
break;
+ case android::RES_TABLE_STAGED_ALIAS_TYPE:
+ if (!ParseStagedAliases(parser.chunk())) {
+ return false;
+ }
+ break;
+
default:
diag_->Warn(DiagMessage(source_)
<< "unexpected chunk type "
@@ -489,6 +495,52 @@
return true;
}
+bool BinaryResourceParser::ParseStagedAliases(const ResChunk_header* chunk) {
+ auto header = ConvertTo<ResTable_staged_alias_header>(chunk);
+ if (!header) {
+ diag_->Error(DiagMessage(source_) << "corrupt ResTable_staged_alias_header chunk");
+ return false;
+ }
+
+ const auto ref_begin = reinterpret_cast<const ResTable_staged_alias_entry*>(
+ ((uint8_t*)header) + util::DeviceToHost32(header->header.headerSize));
+ const auto ref_end = ref_begin + util::DeviceToHost32(header->count);
+ for (auto ref_iter = ref_begin; ref_iter != ref_end; ++ref_iter) {
+ const auto staged_id = ResourceId(util::DeviceToHost32(ref_iter->stagedResId));
+ const auto finalized_id = ResourceId(util::DeviceToHost32(ref_iter->finalizedResId));
+
+ // If the staged alias chunk comes before the type chunks, the resource ids and resource name
+ // pairing will not exist at this point.
+ const auto iter = id_index_.find(finalized_id);
+ if (iter == id_index_.cend()) {
+ diag_->Error(DiagMessage(source_) << "failed to find resource name for finalized"
+ << " resource ID " << finalized_id);
+ return false;
+ }
+
+ // Set the staged id of the finalized resource.
+ const auto& resource_name = iter->second;
+ const StagedId staged_id_def{.id = staged_id};
+ if (!table_->AddResource(NewResourceBuilder(resource_name)
+ .SetId(finalized_id, OnIdConflict::CREATE_ENTRY)
+ .SetStagedId(staged_id_def)
+ .SetAllowMangled(true)
+ .Build(),
+ diag_)) {
+ return false;
+ }
+
+ // Since a the finalized resource entry is cloned and added to the resource table under the
+ // staged resource id, remove the cloned resource entry from the table.
+ if (!table_->RemoveResource(resource_name, staged_id)) {
+ diag_->Error(DiagMessage(source_) << "failed to find resource entry for staged "
+ << " resource ID " << staged_id);
+ return false;
+ }
+ }
+ return true;
+}
+
std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name,
const ConfigDescription& config,
const android::Res_value& value) {
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index 13dd982..cd71d16 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -57,6 +57,7 @@
uint8_t package_id);
bool ParseLibrary(const android::ResChunk_header* chunk);
bool ParseOverlayable(const android::ResChunk_header* chunk);
+ bool ParseStagedAliases(const android::ResChunk_header* chunk);
std::unique_ptr<Item> ParseValue(const ResourceNameRef& name,
const android::ConfigDescription& config,
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 74ecf47..a9192e8 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -72,7 +72,7 @@
}
struct FlatEntry {
- const ResourceEntry* entry;
+ const ResourceTableEntryView* entry;
const Value* value;
// The entry string pool index to the entry's name.
@@ -286,6 +286,10 @@
return false;
}
+ if (!FlattenAliases(buffer)) {
+ return false;
+ }
+
pkg_writer.Finish();
return true;
}
@@ -351,8 +355,8 @@
BigBuffer values_buffer(512);
for (FlatEntry& flat_entry : *entries) {
- CHECK(static_cast<size_t>(flat_entry.entry->id.value().entry_id()) < num_total_entries);
- offsets[flat_entry.entry->id.value().entry_id()] = values_buffer.size();
+ CHECK(static_cast<size_t>(flat_entry.entry->id.value()) < num_total_entries);
+ offsets[flat_entry.entry->id.value()] = values_buffer.size();
if (!FlattenValue(&flat_entry, &values_buffer)) {
diag_->Error(DiagMessage()
<< "failed to flatten resource '"
@@ -404,6 +408,26 @@
return true;
}
+ bool FlattenAliases(BigBuffer* buffer) {
+ if (aliases_.empty()) {
+ return true;
+ }
+
+ ChunkWriter alias_writer(buffer);
+ auto header =
+ alias_writer.StartChunk<ResTable_staged_alias_header>(RES_TABLE_STAGED_ALIAS_TYPE);
+ header->count = util::HostToDevice32(aliases_.size());
+
+ auto mapping = alias_writer.NextBlock<ResTable_staged_alias_entry>(aliases_.size());
+ for (auto& p : aliases_) {
+ mapping->stagedResId = util::HostToDevice32(p.first);
+ mapping->finalizedResId = util::HostToDevice32(p.second);
+ ++mapping;
+ }
+ alias_writer.Finish();
+ return true;
+ }
+
bool FlattenOverlayable(BigBuffer* buffer) {
std::set<ResourceId> seen_ids;
std::map<std::string, OverlayableChunk> overlayable_chunks;
@@ -413,18 +437,17 @@
CHECK(bool(type.id)) << "type must have an ID set when flattening <overlayable>";
for (auto& entry : type.entries) {
CHECK(bool(type.id)) << "entry must have an ID set when flattening <overlayable>";
- if (!entry->overlayable_item) {
+ if (!entry.overlayable_item) {
continue;
}
- const OverlayableItem& item = entry->overlayable_item.value();
+ const OverlayableItem& item = entry.overlayable_item.value();
// Resource ids should only appear once in the resource table
- ResourceId id =
- android::make_resid(package_.id.value(), type.id.value(), entry->id.value().entry_id());
+ ResourceId id = android::make_resid(package_.id.value(), type.id.value(), entry.id.value());
CHECK(seen_ids.find(id) == seen_ids.end())
<< "multiple overlayable definitions found for resource "
- << ResourceName(package_.name, type.type, entry->name).to_string();
+ << ResourceName(package_.name, type.type, entry.name).to_string();
seen_ids.insert(id);
// Find the overlayable chunk with the specified name
@@ -452,9 +475,8 @@
if (item.policies == 0) {
context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source)
- << "overlayable "
- << entry->name
- << " does not specify policy");
+ << "overlayable " << entry.name
+ << " does not specify policy");
return false;
}
@@ -520,7 +542,8 @@
}
bool FlattenTypeSpec(const ResourceTableTypeView& type,
- const std::vector<const ResourceEntry*>& sorted_entries, BigBuffer* buffer) {
+ const std::vector<ResourceTableEntryView>& sorted_entries,
+ BigBuffer* buffer) {
ChunkWriter type_spec_writer(buffer);
ResTable_typeSpec* spec_header =
type_spec_writer.StartChunk<ResTable_typeSpec>(RES_TABLE_TYPE_SPEC_TYPE);
@@ -534,7 +557,7 @@
// We can't just take the size of the vector. There may be holes in the
// entry ID space.
// Since the entries are sorted by ID, the last one will be the biggest.
- const size_t num_entries = sorted_entries.back()->id.value().entry_id() + 1;
+ const size_t num_entries = sorted_entries.back().id.value() + 1;
spec_header->entryCount = util::HostToDevice32(num_entries);
@@ -542,23 +565,23 @@
// show for which configuration axis the resource changes.
uint32_t* config_masks = type_spec_writer.NextBlock<uint32_t>(num_entries);
- for (const ResourceEntry* entry : sorted_entries) {
- const uint16_t entry_id = entry->id.value().entry_id();
+ for (const ResourceTableEntryView& entry : sorted_entries) {
+ const uint16_t entry_id = entry.id.value();
// Populate the config masks for this entry.
uint32_t& entry_config_masks = config_masks[entry_id];
- if (entry->visibility.level == Visibility::Level::kPublic) {
+ if (entry.visibility.level == Visibility::Level::kPublic) {
entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
}
- if (entry->visibility.staged_api) {
+ if (entry.visibility.staged_api) {
entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_STAGED_API);
}
- const size_t config_count = entry->values.size();
+ const size_t config_count = entry.values.size();
for (size_t i = 0; i < config_count; i++) {
- const ConfigDescription& config = entry->values[i]->config;
+ const ConfigDescription& config = entry.values[i]->config;
for (size_t j = i + 1; j < config_count; j++) {
- config_masks[entry_id] |= util::HostToDevice32(config.diff(entry->values[j]->config));
+ config_masks[entry_id] |= util::HostToDevice32(config.diff(entry.values[j]->config));
}
}
}
@@ -590,7 +613,7 @@
}
// Since the entries are sorted by ID, the last ID will be the largest.
- const size_t num_entries = type.entries.back()->id.value().entry_id() + 1;
+ const size_t num_entries = type.entries.back().id.value() + 1;
// The binary resource table lists resource entries for each
// configuration.
@@ -603,20 +626,26 @@
// hardcoded string uses characters which make it an invalid resource name
const std::string obfuscated_resource_name = "0_resource_name_obfuscated";
- for (const ResourceEntry* entry : type.entries) {
+ for (const ResourceTableEntryView& entry : type.entries) {
+ if (entry.staged_id) {
+ aliases_.insert(std::make_pair(
+ entry.staged_id.value().id.id,
+ ResourceId(package_.id.value(), type.id.value(), entry.id.value()).id));
+ }
+
uint32_t local_key_index;
- ResourceName resource_name({}, type.type, entry->name);
+ ResourceName resource_name({}, type.type, entry.name);
if (!collapse_key_stringpool_ ||
name_collapse_exemptions_.find(resource_name) != name_collapse_exemptions_.end()) {
- local_key_index = (uint32_t)key_pool_.MakeRef(entry->name).index();
+ local_key_index = (uint32_t)key_pool_.MakeRef(entry.name).index();
} else {
// resource isn't exempt from collapse, add it as obfuscated value
local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
}
// Group values by configuration.
- for (auto& config_value : entry->values) {
+ for (auto& config_value : entry.values) {
config_to_entry_list_map[config_value->config].push_back(
- FlatEntry{entry, config_value->value.get(), local_key_index});
+ FlatEntry{&entry, config_value->value.get(), local_key_index});
}
}
@@ -667,6 +696,7 @@
StringPool key_pool_;
bool collapse_key_stringpool_;
const std::set<ResourceName>& name_collapse_exemptions_;
+ std::map<uint32_t, uint32_t> aliases_;
};
} // namespace
@@ -684,7 +714,8 @@
});
// Write the ResTable header.
- const auto& table_view = table->GetPartitionedView();
+ const auto& table_view =
+ table->GetPartitionedView(ResourceTableViewOptions{.create_alias_entries = true});
ChunkWriter table_writer(buffer_);
ResTable_header* table_header = table_writer.StartChunk<ResTable_header>(RES_TABLE_TYPE);
table_header->packageCount = util::HostToDevice32(table_view.packages.size());
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index ec331df..236c381 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -498,10 +498,20 @@
out_error)) {
return false;
}
-
entry->overlayable_item = std::move(overlayable_item);
}
+ if (pb_entry.has_staged_id()) {
+ const pb::StagedId& pb_staged_id = pb_entry.staged_id();
+
+ StagedId staged_id;
+ if (pb_staged_id.has_source()) {
+ DeserializeSourceFromPb(pb_staged_id.source(), src_pool, &staged_id.source);
+ }
+ staged_id.id = pb_staged_id.staged_id();
+ entry->staged_id = std::move(staged_id);
+ }
+
ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
pb_entry.entry_id().id());
if (resid.is_valid()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index d2f0336..6042ba8 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -364,43 +364,52 @@
static const char* obfuscated_resource_name = "0_resource_name_obfuscated";
for (const auto& entry : type.entries) {
pb::Entry* pb_entry = pb_type->add_entry();
- if (entry->id) {
- pb_entry->mutable_entry_id()->set_id(entry->id.value().entry_id());
+ if (entry.id) {
+ pb_entry->mutable_entry_id()->set_id(entry.id.value());
}
- ResourceName resource_name({}, type.type, entry->name);
+ ResourceName resource_name({}, type.type, entry.name);
if (options.collapse_key_stringpool &&
options.name_collapse_exemptions.find(resource_name) ==
options.name_collapse_exemptions.end()) {
pb_entry->set_name(obfuscated_resource_name);
} else {
- pb_entry->set_name(entry->name);
+ pb_entry->set_name(entry.name);
}
// Write the Visibility struct.
pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
- pb_visibility->set_staged_api(entry->visibility.staged_api);
- pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
+ pb_visibility->set_staged_api(entry.visibility.staged_api);
+ pb_visibility->set_level(SerializeVisibilityToPb(entry.visibility.level));
if (source_pool != nullptr) {
- SerializeSourceToPb(entry->visibility.source, source_pool.get(),
+ SerializeSourceToPb(entry.visibility.source, source_pool.get(),
pb_visibility->mutable_source());
}
- pb_visibility->set_comment(entry->visibility.comment);
+ pb_visibility->set_comment(entry.visibility.comment);
- if (entry->allow_new) {
+ if (entry.allow_new) {
pb::AllowNew* pb_allow_new = pb_entry->mutable_allow_new();
if (source_pool != nullptr) {
- SerializeSourceToPb(entry->allow_new.value().source, source_pool.get(),
+ SerializeSourceToPb(entry.allow_new.value().source, source_pool.get(),
pb_allow_new->mutable_source());
}
- pb_allow_new->set_comment(entry->allow_new.value().comment);
+ pb_allow_new->set_comment(entry.allow_new.value().comment);
}
- if (entry->overlayable_item) {
- SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables,
+ if (entry.overlayable_item) {
+ SerializeOverlayableItemToPb(entry.overlayable_item.value(), overlayables,
source_pool.get(), pb_entry, out_table);
}
- for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
+ if (entry.staged_id) {
+ pb::StagedId* pb_staged_id = pb_entry->mutable_staged_id();
+ if (source_pool != nullptr) {
+ SerializeSourceToPb(entry.staged_id.value().source, source_pool.get(),
+ pb_staged_id->mutable_source());
+ }
+ pb_staged_id->set_staged_id(entry.staged_id.value().id.id);
+ }
+
+ for (const ResourceConfigValue* config_value : entry.values) {
pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
SerializeConfig(config_value->config, pb_config_value->mutable_config());
pb_config_value->mutable_config()->set_product(config_value->product);
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index e563eda..38c811f 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -928,4 +928,27 @@
EXPECT_THAT(deserialized->alias_namespaces, Eq(original->alias_namespaces));
}
+TEST(ProtoSerializeTest, StagedId) {
+ CloningValueTransformer cloner(nullptr);
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .Add(NewResourceBuilder("com.app.a:string/foo")
+ .SetStagedId(StagedId{.id = 0x01ff0001})
+ .Build())
+ .Build();
+
+ ResourceTable new_table;
+ pb::ResourceTable pb_table;
+ MockFileCollection files;
+ std::string error;
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ auto result = new_table.FindResource(test::ParseNameOrDie("com.app.a:string/foo"));
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.value().entry->staged_id);
+ EXPECT_THAT(result.value().entry->staged_id.value().id, Eq(ResourceId(0x01ff0001)));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index bc93ec6..22f4d18 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -151,6 +151,18 @@
dst_entry->overlayable_item = std::move(src_entry->overlayable_item);
}
+ if (src_entry->staged_id) {
+ if (dst_entry->staged_id &&
+ dst_entry->staged_id.value().id != src_entry->staged_id.value().id) {
+ context->GetDiagnostics()->Error(DiagMessage(src_entry->staged_id.value().source)
+ << "conflicting staged id declaration for resource '"
+ << src_entry->name << "'");
+ context->GetDiagnostics()->Error(DiagMessage(dst_entry->staged_id.value().source)
+ << "previous declaration here");
+ }
+ dst_entry->staged_id = std::move(src_entry->staged_id);
+ }
+
return true;
}
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index f94f0fe..285e5a1 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -80,9 +80,6 @@
}
void TestDirectoryFixture::WriteFile(const std::string& path, const std::string& contents) {
- CHECK(util::StartsWith(path, temp_dir_))
- << "Attempting to create a file outside of test temporary directory.";
-
// Create any intermediate directories specified in the path
auto pos = std::find(path.rbegin(), path.rend(), file::sDirSep);
if (pos != path.rend()) {