Merge "Add MediaParceledListSlice as @SystemApi"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 7a8d1a1..f66d12a 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -11,6 +11,7 @@
libs/input/
services/core/jni/
services/incremental/
+ tests/
tools/
[Hook Scripts]
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index 833ae63..e0c11cf 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -20,7 +20,17 @@
<action android:name="com.android.perftests.core.PERFTEST" />
</intent-filter>
</activity>
- <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" />
+
+ <service
+ android:name="android.os.SomeService"
+ android:exported="false"
+ android:process=":some_service" />
+
+ <provider
+ android:name="android.os.SomeProvider"
+ android:authorities="android.os.SomeProvider"
+ android:exported="false"
+ android:process=":some_provider" />
<service
android:name="android.view.autofill.MyAutofillService"
diff --git a/apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java b/apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java
new file mode 100644
index 0000000..77654df
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.database;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class CrossProcessCursorPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ /**
+ * Measure transporting a small {@link Cursor}, roughly 1KB in size.
+ */
+ @Test
+ public void timeSmall() throws Exception {
+ time(1);
+ }
+
+ /**
+ * Measure transporting a small {@link Cursor}, roughly 54KB in size.
+ */
+ @Test
+ public void timeMedium() throws Exception {
+ time(100);
+ }
+
+ /**
+ * Measure transporting a small {@link Cursor}, roughly 5.4MB in size.
+ */
+ @Test
+ public void timeLarge() throws Exception {
+ time(10_000);
+ }
+
+ private static final Uri TEST_URI = Uri.parse("content://android.os.SomeProvider/");
+
+ private void time(int count) throws Exception {
+ try (ContentProviderClient client = InstrumentationRegistry.getTargetContext()
+ .getContentResolver().acquireContentProviderClient(TEST_URI)) {
+ // Configure remote side once with data size to return
+ final ContentValues values = new ContentValues();
+ values.put(Intent.EXTRA_INDEX, count);
+ client.update(TEST_URI, values, null);
+
+ // Repeatedly query that data until we reach convergence
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ try (Cursor c = client.query(TEST_URI, null, null, null)) {
+ // Actually walk the returned values to ensure we pull all
+ // data from the remote side
+ while (c.moveToNext()) {
+ assertEquals(c.getPosition(), c.getInt(0));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/os/SomeProvider.java b/apct-tests/perftests/core/src/android/os/SomeProvider.java
new file mode 100644
index 0000000..f5e247e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/SomeProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+
+import java.util.Arrays;
+
+public class SomeProvider extends ContentProvider {
+ private Cursor mCursor;
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return mCursor;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ final char[] valueRaw = new char[512];
+ Arrays.fill(valueRaw, '!');
+ final String value = new String(valueRaw);
+
+ final int count = values.getAsInteger(Intent.EXTRA_INDEX);
+ final MatrixCursor cursor = new MatrixCursor(new String[] { "_id", "value" });
+ for (int i = 0; i < count; i++) {
+ MatrixCursor.RowBuilder row = cursor.newRow();
+ row.add(0, i);
+ row.add(1, value);
+ }
+ mCursor = cursor;
+ return 1;
+ }
+}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index bb9f13f..5cebf8d 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -128,7 +128,7 @@
*/
public static final String KEY_USE_REVOCABLE_FD_FOR_READS =
"use_revocable_fd_for_reads";
- public static final boolean DEFAULT_USE_REVOCABLE_FD_FOR_READS = true;
+ public static final boolean DEFAULT_USE_REVOCABLE_FD_FOR_READS = false;
public static boolean USE_REVOCABLE_FD_FOR_READS =
DEFAULT_USE_REVOCABLE_FD_FOR_READS;
diff --git a/api/current.txt b/api/current.txt
index 4b0901a..b93ffe3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8783,6 +8783,7 @@
method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
+ method public void onServiceChanged(@NonNull android.bluetooth.BluetoothGatt);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
}
@@ -46096,6 +46097,7 @@
field public static final int MISSED = 5; // 0x5
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
+ field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 713a230..eb2f264 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -16,6 +16,14 @@
}
+package android.app.role {
+
+ public final class RoleManager {
+ method @Nullable public String getDefaultSmsPackage(int);
+ }
+
+}
+
package android.content.rollback {
public class RollbackManagerFrameworkInitializer {
diff --git a/api/system-current.txt b/api/system-current.txt
index 91be5bd..a058496 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10441,10 +10441,6 @@
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
- public final class DisconnectCause implements android.os.Parcelable {
- field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
- }
-
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
diff --git a/api/test-current.txt b/api/test-current.txt
index 9383152..3a4d101 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -766,6 +766,7 @@
method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @Nullable public String getDefaultSmsPackage(int);
method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index 2ae5763..12d3b94 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -501,6 +501,21 @@
const set<int64_t>& replacedStates,
vector<UpdateStatus>& metricsToUpdate) {
int metricIndex = 0;
+ for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
+ const CountMetric& metric = config.count_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_COUNT, {metric.what()},
+ conditionDependencies, metric.slice_by_state(), metric.links(),
+ oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
const EventMetric& metric = config.event_metric(i);
set<int64_t> conditionDependencies;
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index 65e5875..3b346c1 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -1094,6 +1094,162 @@
EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
+TEST_F(ConfigUpdateTest, TestCountMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->add_slice_by_state(sliceState.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricDefinitionChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ // Change bucket size, which should change the proto, causing replacement.
+ metric->set_bucket(TEN_MINUTES);
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricStateChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->add_slice_by_state(sliceState.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
StatsdConfig config;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 167b5a8..ef4f099 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -81,8 +81,6 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@@ -5021,8 +5019,7 @@
* @hide
*/
public static double round(double value) {
- final BigDecimal decimalScale = new BigDecimal(value);
- return decimalScale.setScale(0, RoundingMode.HALF_UP).doubleValue();
+ return Math.floor(value + 0.5);
}
@Override
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index 59dc999..55fff8b 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -22,12 +22,10 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.util.Slog;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -387,12 +385,13 @@
/**
* Removes all notifications from a conversation and regenerates the string pool
*/
- public boolean removeConversationFromWrite(String packageName, String conversationId) {
+ public boolean removeConversationsFromWrite(String packageName, Set<String> conversationIds) {
boolean removed = false;
for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) {
HistoricalNotification hn = mNotificationsToWrite.get(i);
if (packageName.equals(hn.getPackage())
- && conversationId.equals(hn.getConversationId())) {
+ && hn.getConversationId() != null
+ && conversationIds.contains(hn.getConversationId())) {
removed = true;
mNotificationsToWrite.remove(i);
}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 253c73796..26edba3 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -634,6 +634,8 @@
* @hide
*/
@Nullable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
public String getDefaultSmsPackage(@UserIdInt int userId) {
try {
return mService.getDefaultSmsPackage(userId);
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index 9f6b828..1c40cff 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.NonNull;
+
/**
* This abstract class is used to implement {@link BluetoothGatt} callbacks.
*/
@@ -203,8 +205,7 @@
* called to re-discover the services.
*
* @param gatt GATT client involved
- * @hide
*/
- public void onServiceChanged(BluetoothGatt gatt) {
+ public void onServiceChanged(@NonNull BluetoothGatt gatt) {
}
}
diff --git a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
index 27f8a61..7b6a457 100644
--- a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
+++ b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
@@ -37,12 +37,16 @@
/** the completed frame number for each type of capture results */
private long[] mCompletedFrameNumber = new long[CaptureRequest.REQUEST_TYPE_COUNT];
- /** the skipped frame numbers that don't belong to each type of capture results */
- private final LinkedList<Long>[] mSkippedOtherFrameNumbers =
+ /** the frame numbers that don't belong to each type of capture results and are yet to be seen
+ * through an updateTracker() call. Each list holds a list of frame numbers that should appear
+ * with request types other than that, to which the list corresponds.
+ */
+ private final LinkedList<Long>[] mPendingFrameNumbersWithOtherType =
new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT];
- /** the skipped frame numbers that belong to each type of capture results */
- private final LinkedList<Long>[] mSkippedFrameNumbers =
+ /** the frame numbers that belong to each type of capture results which should appear, but
+ * haven't yet.*/
+ private final LinkedList<Long>[] mPendingFrameNumbers =
new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT];
/** frame number -> request type */
@@ -53,8 +57,8 @@
public FrameNumberTracker() {
for (int i = 0; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) {
mCompletedFrameNumber[i] = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED;
- mSkippedOtherFrameNumbers[i] = new LinkedList<Long>();
- mSkippedFrameNumbers[i] = new LinkedList<Long>();
+ mPendingFrameNumbersWithOtherType[i] = new LinkedList<Long>();
+ mPendingFrameNumbers[i] = new LinkedList<Long>();
}
}
@@ -66,29 +70,29 @@
int requestType = (int) pair.getValue();
Boolean removeError = false;
if (errorFrameNumber == mCompletedFrameNumber[requestType] + 1) {
- mCompletedFrameNumber[requestType] = errorFrameNumber;
removeError = true;
+ }
+ // The error frame number could have also either been in the pending list or one of the
+ // 'other' pending lists.
+ if (!mPendingFrameNumbers[requestType].isEmpty()) {
+ if (errorFrameNumber == mPendingFrameNumbers[requestType].element()) {
+ mPendingFrameNumbers[requestType].remove();
+ removeError = true;
+ }
} else {
- if (!mSkippedFrameNumbers[requestType].isEmpty()) {
- if (errorFrameNumber == mSkippedFrameNumbers[requestType].element()) {
- mCompletedFrameNumber[requestType] = errorFrameNumber;
- mSkippedFrameNumbers[requestType].remove();
+ for (int i = 1; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) {
+ int otherType = (requestType + i) % CaptureRequest.REQUEST_TYPE_COUNT;
+ if (!mPendingFrameNumbersWithOtherType[otherType].isEmpty() && errorFrameNumber
+ == mPendingFrameNumbersWithOtherType[otherType].element()) {
+ mPendingFrameNumbersWithOtherType[otherType].remove();
removeError = true;
- }
- } else {
- for (int i = 1; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) {
- int otherType = (requestType + i) % CaptureRequest.REQUEST_TYPE_COUNT;
- if (!mSkippedOtherFrameNumbers[otherType].isEmpty() && errorFrameNumber
- == mSkippedOtherFrameNumbers[otherType].element()) {
- mCompletedFrameNumber[requestType] = errorFrameNumber;
- mSkippedOtherFrameNumbers[otherType].remove();
- removeError = true;
- break;
- }
+ break;
}
}
}
if (removeError) {
+ mCompletedFrameNumber[requestType] = errorFrameNumber;
+ mPartialResults.remove(errorFrameNumber);
iter.remove();
}
}
@@ -182,7 +186,7 @@
* It validates that all previous frames of the same category have arrived.
*
* If there is a gap since previous frame number of the same category, assume the frames in
- * the gap are other categories and store them in the skipped frame number queue to check
+ * the gap are other categories and store them in the pending frame number queue to check
* against when frames of those categories arrive.
*/
private void updateCompletedFrameNumber(long frameNumber,
@@ -199,25 +203,29 @@
if (frameNumber < maxOtherFrameNumberSeen) {
// if frame number is smaller than completed frame numbers of other categories,
// it must be:
- // - the head of mSkippedFrameNumbers for this category, or
- // - in one of other mSkippedOtherFrameNumbers
- if (!mSkippedFrameNumbers[requestType].isEmpty()) {
- // frame number must be head of current type of mSkippedFrameNumbers if
- // mSkippedFrameNumbers isn't empty.
- if (frameNumber < mSkippedFrameNumbers[requestType].element()) {
+ // - the head of mPendingFrameNumbers for this category, or
+ // - in one of other mPendingFrameNumbersWithOtherType
+ if (!mPendingFrameNumbers[requestType].isEmpty()) {
+ // frame number must be head of current type of mPendingFrameNumbers if
+ // mPendingFrameNumbers isn't empty.
+ Long pendingFrameNumberSameType = mPendingFrameNumbers[requestType].element();
+ if (frameNumber == pendingFrameNumberSameType) {
+ // frame number matches the head of the pending frame number queue.
+ // Do this before the inequality checks since this is likely to be the common
+ // case.
+ mPendingFrameNumbers[requestType].remove();
+ } else if (frameNumber < pendingFrameNumberSameType) {
throw new IllegalArgumentException("frame number " + frameNumber
+ " is a repeat");
- } else if (frameNumber > mSkippedFrameNumbers[requestType].element()) {
+ } else {
throw new IllegalArgumentException("frame number " + frameNumber
+ " comes out of order. Expecting "
- + mSkippedFrameNumbers[requestType].element());
+ + pendingFrameNumberSameType);
}
- // frame number matches the head of the skipped frame number queue.
- mSkippedFrameNumbers[requestType].remove();
} else {
- // frame number must be in one of the other mSkippedOtherFrameNumbers.
- int index1 = mSkippedOtherFrameNumbers[otherType1].indexOf(frameNumber);
- int index2 = mSkippedOtherFrameNumbers[otherType2].indexOf(frameNumber);
+ // frame number must be in one of the other mPendingFrameNumbersWithOtherType.
+ int index1 = mPendingFrameNumbersWithOtherType[otherType1].indexOf(frameNumber);
+ int index2 = mPendingFrameNumbersWithOtherType[otherType2].indexOf(frameNumber);
boolean inSkippedOther1 = index1 != -1;
boolean inSkippedOther2 = index2 != -1;
if (!(inSkippedOther1 ^ inSkippedOther2)) {
@@ -225,33 +233,39 @@
+ " is a repeat or invalid");
}
- // We know the category of frame numbers in skippedOtherFrameNumbers leading up
- // to the current frame number. Move them into the correct skippedFrameNumbers.
+ // We know the category of frame numbers in pendingFrameNumbersWithOtherType leading
+ // up to the current frame number. The destination is the type which isn't the
+ // requestType* and isn't the src. Move them into the correct pendingFrameNumbers.
+ // * : This is since frameNumber is the first frame of requestType that we've
+ // received in the 'others' list, since for each request type frames come in order.
+ // All the frames before frameNumber are of the same type. They're not of
+ // 'requestType', neither of the type of the 'others' list they were found in. The
+ // remaining option is the 3rd type.
LinkedList<Long> srcList, dstList;
int index;
if (inSkippedOther1) {
- srcList = mSkippedOtherFrameNumbers[otherType1];
- dstList = mSkippedFrameNumbers[otherType2];
+ srcList = mPendingFrameNumbersWithOtherType[otherType1];
+ dstList = mPendingFrameNumbers[otherType2];
index = index1;
} else {
- srcList = mSkippedOtherFrameNumbers[otherType2];
- dstList = mSkippedFrameNumbers[otherType1];
+ srcList = mPendingFrameNumbersWithOtherType[otherType2];
+ dstList = mPendingFrameNumbers[otherType1];
index = index2;
}
for (int i = 0; i < index; i++) {
dstList.add(srcList.removeFirst());
}
- // Remove current frame number from skippedOtherFrameNumbers
+ // Remove current frame number from pendingFrameNumbersWithOtherType
srcList.remove();
}
} else {
// there is a gap of unseen frame numbers which should belong to the other
- // 2 categories. Put all the skipped frame numbers in the queue.
+ // 2 categories. Put all the pending frame numbers in the queue.
for (long i =
Math.max(maxOtherFrameNumberSeen, mCompletedFrameNumber[requestType]) + 1;
i < frameNumber; i++) {
- mSkippedOtherFrameNumbers[requestType].add(i);
+ mPendingFrameNumbersWithOtherType[requestType].add(i);
}
}
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index df1f1b2..52ee04c 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -29,6 +29,7 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
@@ -43,11 +44,14 @@
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-/** @hide */
+/**
+ * GraphicsEnvironment sets up necessary properties for the graphics environment of the
+ * application process.
+ *
+ * @hide
+ */
public class GraphicsEnvironment {
private static final GraphicsEnvironment sInstance = new GraphicsEnvironment();
@@ -64,27 +68,35 @@
private static final String SYSTEM_DRIVER_NAME = "system";
private static final String SYSTEM_DRIVER_VERSION_NAME = "";
private static final long SYSTEM_DRIVER_VERSION_CODE = 0;
+
+ // System properties related to updatable graphics drivers.
private static final String PROPERTY_GFX_DRIVER_PRODUCTION = "ro.gfx.driver.0";
private static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1";
private static final String PROPERTY_GFX_DRIVER_BUILD_TIME = "ro.gfx.driver_build_time";
+
+ // Metadata flags within the <application> tag in the AndroidManifest.xml file.
private static final String METADATA_DRIVER_BUILD_TIME =
"com.android.graphics.driver.build_time";
private static final String METADATA_DEVELOPER_DRIVER_ENABLE =
"com.android.graphics.developerdriver.enable";
private static final String METADATA_INJECT_LAYERS_ENABLE =
"com.android.graphics.injectLayers.enable";
+
+ private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*";
+ private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
+
+ // ANGLE related properties.
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
private static final String ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE =
"android.app.action.ANGLE_FOR_ANDROID_TOAST_MESSAGE";
private static final String INTENT_KEY_A4A_TOAST_MESSAGE = "A4A Toast Message";
- private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*";
- private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
+
private static final int VULKAN_1_0 = 0x00400000;
private static final int VULKAN_1_1 = 0x00401000;
- // UPDATABLE_DRIVER_ALL_APPS
+ // Values for UPDATABLE_DRIVER_ALL_APPS
// 0: Default (Invalid values fallback to default as well)
// 1: All apps use updatable production driver
// 2: All apps use updatable prerelease driver
@@ -94,6 +106,15 @@
private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER = 2;
private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF = 3;
+ // Values for GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE
+ private static final int ANGLE_GL_DRIVER_ALL_ANGLE_ON = 1;
+ private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0;
+
+ // Values for GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES
+ private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
+ private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
+ private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
+
private ClassLoader mClassLoader;
private String mLibrarySearchPaths;
private String mLibraryPermittedPaths;
@@ -106,12 +127,15 @@
final String packageName = context.getPackageName();
final ApplicationInfo appInfoWithMetaData =
getAppInfoWithMetadata(context, pm, packageName);
+
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers");
setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData);
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle");
setupAngle(context, coreSettings, pm, packageName);
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver");
if (!chooseDriver(context, coreSettings, pm, packageName, appInfoWithMetaData)) {
setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE,
@@ -132,36 +156,30 @@
*/
public static boolean shouldUseAngle(Context context, Bundle coreSettings,
String packageName) {
- if (packageName.isEmpty()) {
- Log.v(TAG, "No package name available yet, ANGLE should not be used");
+ if (TextUtils.isEmpty(packageName)) {
+ Log.v(TAG, "No package name specified, ANGLE should not be used");
return false;
}
- final String devOptIn = getDriverForPkg(context, coreSettings, packageName);
- if (DEBUG) {
- Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
- + "set to: '" + devOptIn + "'");
- }
+ final String devOptIn = getDriverForPackage(context, coreSettings, packageName);
+ Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
+ + "set to: '" + devOptIn + "'");
- // We only want to use ANGLE if the app is allowlisted or the developer has
+ // We only want to use ANGLE if the app is in the allowlist, or the developer has
// explicitly chosen something other than default driver.
// The allowlist will be generated by the ANGLE APK at both boot time and
// ANGLE update time. It will only include apps mentioned in the rules file.
- final boolean allowlisted = checkAngleAllowlist(context, coreSettings, packageName);
- final boolean requested = devOptIn.equals(sDriverMap.get(OpenGlDriverChoice.ANGLE));
- final boolean useAngle = (allowlisted || requested);
- if (!useAngle) {
- return false;
- }
+ final boolean allowed = checkAngleAllowlist(context, coreSettings, packageName);
+ final boolean requested = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
- if (allowlisted) {
+ if (allowed) {
Log.v(TAG, "ANGLE allowlist includes " + packageName);
}
if (requested) {
Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
}
- return true;
+ return allowed || requested;
}
private static int getVulkanVersion(PackageManager pm) {
@@ -315,23 +333,6 @@
setLayerPaths(mClassLoader, layerPaths);
}
- enum OpenGlDriverChoice {
- DEFAULT,
- NATIVE,
- ANGLE
- }
-
- private static final Map<OpenGlDriverChoice, String> sDriverMap = buildMap();
- private static Map<OpenGlDriverChoice, String> buildMap() {
- final Map<OpenGlDriverChoice, String> map = new HashMap<>();
- map.put(OpenGlDriverChoice.DEFAULT, "default");
- map.put(OpenGlDriverChoice.ANGLE, "angle");
- map.put(OpenGlDriverChoice.NATIVE, "native");
-
- return map;
- }
-
-
private static List<String> getGlobalSettingsString(ContentResolver contentResolver,
Bundle bundle,
String globalSetting) {
@@ -378,18 +379,25 @@
return ai;
}
- private static String getDriverForPkg(Context context, Bundle bundle, String packageName) {
- final String allUseAngle;
+ private static String getDriverForPackage(Context context, Bundle bundle, String packageName) {
+ final int allUseAngle;
if (bundle != null) {
allUseAngle =
- bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+ bundle.getInt(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
} else {
ContentResolver contentResolver = context.getContentResolver();
- allUseAngle = Settings.Global.getString(contentResolver,
- Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+ allUseAngle = Settings.Global.getInt(contentResolver,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
+ ANGLE_GL_DRIVER_ALL_ANGLE_OFF);
}
- if ((allUseAngle != null) && allUseAngle.equals("1")) {
- return sDriverMap.get(OpenGlDriverChoice.ANGLE);
+ if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
+ Log.v(TAG, "Turn on ANGLE for all applications.");
+ return ANGLE_GL_DRIVER_CHOICE_ANGLE;
+ }
+
+ // Make sure we have a good package name
+ if (TextUtils.isEmpty(packageName)) {
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
final ContentResolver contentResolver = context.getContentResolver();
@@ -400,25 +408,21 @@
getGlobalSettingsString(contentResolver, bundle,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES);
- // Make sure we have a good package name
- if ((packageName == null) || (packageName.isEmpty())) {
- return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
- }
// Make sure we have good settings to use
if (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size()) {
Log.w(TAG,
"Global.Settings values are invalid: "
- + "globalSettingsDriverPkgs.size = "
+ + "number of packages: "
+ globalSettingsDriverPkgs.size() + ", "
- + "globalSettingsDriverValues.size = "
+ + "number of values: "
+ globalSettingsDriverValues.size());
- return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
final int pkgIndex = getGlobalSettingsPkgIndex(packageName, globalSettingsDriverPkgs);
if (pkgIndex < 0) {
- return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
return globalSettingsDriverValues.get(pkgIndex);
@@ -446,27 +450,28 @@
}
/**
- * Check for ANGLE debug package, but only for apps that can load them (dumpable)
+ * Check for ANGLE debug package, but only for apps that can load them.
+ * An application can load ANGLE debug package if it is a debuggable application, or
+ * the device is debuggable.
*/
private String getAngleDebugPackage(Context context, Bundle coreSettings) {
- if (isDebuggable()) {
- String debugPackage;
-
- if (coreSettings != null) {
- debugPackage =
- coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
- } else {
- ContentResolver contentResolver = context.getContentResolver();
- debugPackage = Settings.Global.getString(contentResolver,
- Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
- }
-
- if ((debugPackage != null) && (!debugPackage.isEmpty())) {
- return debugPackage;
- }
+ if (!isDebuggable()) {
+ return "";
}
+ final String debugPackage;
- return "";
+ if (coreSettings != null) {
+ debugPackage =
+ coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+ } else {
+ ContentResolver contentResolver = context.getContentResolver();
+ debugPackage = Settings.Global.getString(contentResolver,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+ }
+ if (TextUtils.isEmpty(debugPackage)) {
+ return "";
+ }
+ return debugPackage;
}
/**
@@ -491,7 +496,7 @@
final String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
- if ((angleTempRules == null) || angleTempRules.isEmpty()) {
+ if (TextUtils.isEmpty(angleTempRules)) {
Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
return false;
}
@@ -583,11 +588,12 @@
*
* @param context
* @param bundle
- * @param packageName
+ * @param pm
+ * @param packageName - package name of the application.
* @return true: ANGLE setup successfully
* false: ANGLE not setup (not on allowlist, ANGLE not present, etc.)
*/
- public boolean setupAngle(Context context, Bundle bundle, PackageManager pm,
+ private boolean setupAngle(Context context, Bundle bundle, PackageManager pm,
String packageName) {
if (!shouldUseAngle(context, bundle, packageName)) {
@@ -612,18 +618,18 @@
// Otherwise, check to see if ANGLE is properly installed
if (angleInfo == null) {
anglePkgName = getAnglePackageName(pm);
- if (!anglePkgName.isEmpty()) {
- Log.i(TAG, "ANGLE package enabled: " + anglePkgName);
- try {
- // Production ANGLE libraries must be pre-installed as a system app
- angleInfo = pm.getApplicationInfo(anglePkgName,
- PackageManager.MATCH_SYSTEM_ONLY);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed");
- return false;
- }
- } else {
- Log.e(TAG, "Failed to find ANGLE package.");
+ if (TextUtils.isEmpty(anglePkgName)) {
+ Log.w(TAG, "Failed to find ANGLE package.");
+ return false;
+ }
+
+ Log.i(TAG, "ANGLE package enabled: " + anglePkgName);
+ try {
+ // Production ANGLE libraries must be pre-installed as a system app
+ angleInfo = pm.getApplicationInfo(anglePkgName,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed");
return false;
}
}
@@ -645,7 +651,7 @@
// load a driver, GraphicsEnv::getShouldUseAngle() has seen the package name before
// and can confidently answer yes/no based on the previously set developer
// option value.
- final String devOptIn = getDriverForPkg(context, bundle, packageName);
+ final String devOptIn = getDriverForPackage(context, bundle, packageName);
if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
// We setup ANGLE with a temp rules file, so we're done here.
@@ -730,18 +736,18 @@
final boolean hasPrereleaseDriver = prereleaseDriver != null && !prereleaseDriver.isEmpty();
if (!hasProductionDriver && !hasPrereleaseDriver) {
- if (DEBUG) {
- Log.v(TAG,
- "Neither updatable production driver nor prerelease driver is supported.");
- }
+ Log.v(TAG, "Neither updatable production driver nor prerelease driver is supported.");
return null;
}
- // To minimize risk of driver updates crippling the device beyond user repair, never use an
- // updated driver for privileged or non-updated system apps. Presumably pre-installed apps
- // were tested thoroughly with the pre-installed driver.
+ // To minimize risk of driver updates crippling the device beyond user repair, never use the
+ // updatable drivers for privileged or non-updated system apps. Presumably pre-installed
+ // apps were tested thoroughly with the system driver.
if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
- if (DEBUG) Log.v(TAG, "Ignoring driver package for privileged/non-updated system app.");
+ if (DEBUG) {
+ Log.v(TAG,
+ "Ignore updatable driver package for privileged/non-updated system app.");
+ }
return null;
}
@@ -758,13 +764,13 @@
// 6. UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST
switch (coreSettings.getInt(Settings.Global.UPDATABLE_DRIVER_ALL_APPS, 0)) {
case UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF:
- if (DEBUG) Log.v(TAG, "updatable driver is turned off on this device.");
+ Log.v(TAG, "The updatable driver is turned off on this device.");
return null;
case UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRODUCTION_DRIVER:
- if (DEBUG) Log.v(TAG, "All apps opt in to use updatable production driver.");
+ Log.v(TAG, "All apps opt in to use updatable production driver.");
return hasProductionDriver ? productionDriver : null;
case UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER:
- if (DEBUG) Log.v(TAG, "All apps opt in to use updatable prerelease driver.");
+ Log.v(TAG, "All apps opt in to use updatable prerelease driver.");
return hasPrereleaseDriver && enablePrereleaseDriver ? prereleaseDriver : null;
case UPDATABLE_DRIVER_GLOBAL_OPT_IN_DEFAULT:
default:
@@ -775,20 +781,20 @@
if (getGlobalSettingsString(null, coreSettings,
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS)
.contains(appPackageName)) {
- if (DEBUG) Log.v(TAG, "App opts out for updatable production driver.");
+ Log.v(TAG, "App opts out for updatable production driver.");
return null;
}
if (getGlobalSettingsString(
null, coreSettings, Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS)
.contains(appPackageName)) {
- if (DEBUG) Log.v(TAG, "App opts in for updatable prerelease driver.");
+ Log.v(TAG, "App opts in for updatable prerelease driver.");
return hasPrereleaseDriver && enablePrereleaseDriver ? prereleaseDriver : null;
}
// Early return here since the rest logic is only for updatable production Driver.
if (!hasProductionDriver) {
- if (DEBUG) Log.v(TAG, "Updatable production driver is not supported on the device.");
+ Log.v(TAG, "Updatable production driver is not supported on the device.");
return null;
}
@@ -801,7 +807,7 @@
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST);
if (!isOptIn && allowlist.indexOf(UPDATABLE_DRIVER_ALLOWLIST_ALL) != 0
&& !allowlist.contains(appPackageName)) {
- if (DEBUG) Log.v(TAG, "App is not on the allowlist for updatable production driver.");
+ Log.v(TAG, "App is not on the allowlist for updatable production driver.");
return null;
}
@@ -811,7 +817,7 @@
&& getGlobalSettingsString(
null, coreSettings, Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST)
.contains(appPackageName)) {
- if (DEBUG) Log.v(TAG, "App is on the denylist for updatable production driver.");
+ Log.v(TAG, "App is on the denylist for updatable production driver.");
return null;
}
@@ -834,7 +840,7 @@
driverPackageInfo = pm.getPackageInfo(driverPackageName,
PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
+ Log.w(TAG, "updatable driver package '" + driverPackageName + "' not installed");
return false;
}
@@ -843,7 +849,7 @@
final ApplicationInfo driverAppInfo = driverPackageInfo.applicationInfo;
if (driverAppInfo.targetSdkVersion < Build.VERSION_CODES.O) {
if (DEBUG) {
- Log.w(TAG, "updated driver package is not known to be compatible with O");
+ Log.w(TAG, "updatable driver package is not compatible with O");
}
return false;
}
@@ -853,7 +859,7 @@
if (DEBUG) {
// This is the normal case for the pre-installed empty driver package, don't spam
if (driverAppInfo.isUpdatedSystemApp()) {
- Log.w(TAG, "updated driver package has no compatible native libraries");
+ Log.w(TAG, "Updatable driver package has no compatible native libraries");
}
}
return false;
@@ -867,11 +873,8 @@
.append(abi);
final String paths = sb.toString();
final String sphalLibraries = getSphalLibraries(context, driverPackageName);
- if (DEBUG) {
- Log.v(TAG,
- "gfx driver package search path: " + paths
- + ", required sphal libraries: " + sphalLibraries);
- }
+ Log.v(TAG, "Updatable driver package search path: " + paths
+ + ", required sphal libraries: " + sphalLibraries);
setDriverPathAndSphalLibraries(paths, sphalLibraries);
if (driverAppInfo.metaData == null) {
@@ -880,7 +883,7 @@
String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME);
if (driverBuildTime == null || driverBuildTime.length() <= 1) {
- Log.v(TAG, "com.android.graphics.driver.build_time is not set");
+ Log.w(TAG, "com.android.graphics.driver.build_time is not set");
driverBuildTime = "L0";
}
// driver_build_time in the meta-data is in "L<Unix epoch timestamp>" format. e.g. L123456.
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index b654707..35e7bad 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -235,6 +235,21 @@
}
/**
+ * Returns the list of declared instances for an interface.
+ *
+ * @return true if the service is declared somewhere (eg. VINTF manifest) and
+ * waitForService should always be able to return the service.
+ */
+ public static String[] getDeclaredInstances(@NonNull String iface) {
+ try {
+ return getIServiceManager().getDeclaredInstances(iface);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in getDeclaredInstances", e);
+ return null;
+ }
+ }
+
+ /**
* Returns the specified service from the service manager.
*
* If the service is not running, servicemanager will attempt to start it, and this function
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 91b56fb..b70b6b5 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -90,6 +90,10 @@
return mServiceManager.isDeclared(name);
}
+ public String[] getDeclaredInstances(String iface) throws RemoteException {
+ return mServiceManager.getDeclaredInstances(iface);
+ }
+
public void registerClientCallback(String name, IBinder service, IClientCallback cb)
throws RemoteException {
throw new RemoteException();
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a3c95a91..8adb7e5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -610,7 +610,8 @@
@Override
public void startInputAsyncOnWindowFocusGain(View focusedView,
@SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) {
- final int startInputFlags = getStartInputFlags(focusedView, 0);
+ int startInputFlags = getStartInputFlags(focusedView, 0);
+ startInputFlags |= StartInputFlags.WINDOW_GAINED_FOCUS;
final ImeFocusController controller = getFocusController();
if (controller == null) {
diff --git a/core/java/android/widget/TEST_MAPPING b/core/java/android/widget/TEST_MAPPING
index b5beac9..49c4093 100644
--- a/core/java/android/widget/TEST_MAPPING
+++ b/core/java/android/widget/TEST_MAPPING
@@ -22,7 +22,7 @@
"name": "CtsAutoFillServiceTestCases",
"options": [
{
- "include-filter": "android.autofillservice.cts.LoginActivityTest"
+ "include-filter": "android.autofillservice.cts.dropdown.LoginActivityTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
@@ -36,7 +36,7 @@
"name": "CtsAutoFillServiceTestCases",
"options": [
{
- "include-filter": "android.autofillservice.cts.CheckoutActivityTest"
+ "include-filter": "android.autofillservice.cts.dropdown.CheckoutActivityTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 37f6823..3bcba75 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -224,6 +224,8 @@
return "HIDE_DOCKED_STACK_ATTACHED";
case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION:
return "HIDE_RECENTS_ANIMATION";
+ case SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR:
+ return "HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR";
default:
return "Unknown=" + reason;
}
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index 4b968b4..f46626b 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -47,7 +47,8 @@
SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME,
SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED,
SoftInputShowHideReason.HIDE_RECENTS_ANIMATION,
- SoftInputShowHideReason.HIDE_BUBBLES})
+ SoftInputShowHideReason.HIDE_BUBBLES,
+ SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR})
public @interface SoftInputShowHideReason {
/** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
int SHOW_SOFT_INPUT = 0;
@@ -147,4 +148,17 @@
* switching, or collapsing Bubbles.
*/
int HIDE_BUBBLES = 19;
+
+ /**
+ * Hide soft input when focusing the same window (e.g. screen turned-off and turn-on) which no
+ * valid focused editor.
+ *
+ * Note: From Android R, the window focus change callback is processed by InputDispatcher,
+ * some focus behavior changes (e.g. There are an activity with a dialog window, after
+ * screen turned-off and turned-on, before Android R the window focus sequence would be
+ * the activity first and then the dialog focused, however, in R the focus sequence would be
+ * only the dialog focused as it's the latest window with input focus) makes we need to hide
+ * soft-input when the same window focused again to align with the same behavior prior to R.
+ */
+ int HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR = 20;
}
diff --git a/core/java/com/android/internal/inputmethod/StartInputFlags.java b/core/java/com/android/internal/inputmethod/StartInputFlags.java
index 5a8d2c2..ac83987 100644
--- a/core/java/com/android/internal/inputmethod/StartInputFlags.java
+++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java
@@ -47,4 +47,10 @@
* documented hence we probably need to revisit this though.
*/
int INITIAL_CONNECTION = 4;
+
+ /**
+ * The start input happens when the window gained focus to call
+ * {@code android.view.inputmethod.InputMethodManager#startInputAsyncOnWindowFocusGain}.
+ */
+ int WINDOW_GAINED_FOCUS = 8;
}
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index be68c4a..2435406 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -84,23 +84,31 @@
}
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) {
+ status_t status;
String8 name;
+ CursorWindow* window;
+
const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
name.setTo(nameStr);
env->ReleaseStringUTFChars(nameObj, nameStr);
- CursorWindow* window;
- status_t status = CursorWindow::create(name, cursorWindowSize, &window);
+ if (cursorWindowSize < 0) {
+ status = INVALID_OPERATION;
+ goto fail;
+ }
+ status = CursorWindow::create(name, cursorWindowSize, &window);
if (status || !window) {
- jniThrowExceptionFmt(env,
- "android/database/CursorWindowAllocationException",
- "Could not allocate CursorWindow '%s' of size %d due to error %d.",
- name.string(), cursorWindowSize, status);
- return 0;
+ goto fail;
}
LOG_WINDOW("nativeInitializeEmpty: window = %p", window);
return reinterpret_cast<jlong>(window);
+
+fail:
+ jniThrowExceptionFmt(env, "android/database/CursorWindowAllocationException",
+ "Could not allocate CursorWindow '%s' of size %d due to error %d.",
+ name.string(), cursorWindowSize, status);
+ return 0;
}
static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 4892faa..11f6a91 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -14,6 +14,7 @@
# Frameworks
ogunwale@google.com
jjaggi@google.com
+roosa@google.com
per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
# Launcher
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index b54dfc0..58d6a86 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Verhoog volume bo aanbevole vlak?\n\nOm lang tydperke teen hoë volume te luister, kan jou gehoor beskadig."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gebruik toeganklikheidkortpad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Wanneer die kortpad aan is, sal \'n toeganklikheidkenmerk begin word as albei volumeknoppies 3 sekondes lank gedruk word."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Skakel toeganklikheidkenmerke aan?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"As jy albei volumesleutels vir \'n paar sekondes hou, skakel dit toeganklikheidkenmerke aan. Dit kan verander hoe jou toestel werk.\n\nHuidige kenmerke:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nJy kan geselekteerde kenmerke in Instellings en Toeganklikheid verander."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Skakel <xliff:g id="SERVICE">%1$s</xliff:g> aan?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"As jy albei volumesleutels vir \'n paar sekondes hou, skakel dit <xliff:g id="SERVICE">%1$s</xliff:g>, \'n toeganklikheidkenmerk, aan. Dit kan verander hoe jou toestel werk.\n\nJy kan hierdie kortpad na \'n ander kenmerk in Instellings en Toeganklikheid verander."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Skakel aan"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Moenie aanskakel nie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 668ce42..b3878d8 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ድምጹ ከሚመከረው መጠን በላይ ከፍ ይበል?\n\nበከፍተኛ ድምጽ ለረጅም ጊዜ ማዳመጥ ጆሮዎን ሊጎዳው ይችላል።"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"የተደራሽነት አቋራጭ ጥቅም ላይ ይዋል?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"አቋራጩ ሲበራ ሁለቱንም የድምጽ አዝራሮች ለ3 ሰከንዶች ተጭኖ መቆየት የተደራሽነት ባህሪን ያስጀምረዋል።"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"የተደራሽነት ባሕሪያት ይብሩ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ሁለቱንም የድምፅ ቁልፎች ወደ ታች ለጥቂት ሰከንዶች መያዝ የተደራሽነት ባሕሪያትን ያበራል። ይህ የእርስዎ መሣሪያ እንዴት እንደሚሠራ ሊለውጥ ይችላል።\n\nየአሁን ባሕሪያት፦\n<xliff:g id="SERVICE">%1$s</xliff:g>\nበቅንብሮች > ተደራሽነት ውስጥ የተመረጡትን ባሕሪያት መለወጥ ይችላሉ።"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ይብራ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ሁለቱንም የድምፅ ቁልፎች ወደ ታች ለጥቂት ሰከንዶች መያዝ የተደራሽነት ባሕሪያትን <xliff:g id="SERVICE">%1$s</xliff:g> ያበራል። ይህ የእርስዎ መሣሪያ እንዴት እንደሚሠራ ሊለውጥ ይችላል።\n\nበቅንብሮች > ተደራሽነት ውስጥ ወደ ሌላ ባሕሪ ይህን አቋራጭ መለወጥ ይችላሉ።"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"አብራ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"አታብራ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index fdb351c..44055e1 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1710,10 +1710,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"هل تريد رفع مستوى الصوت فوق المستوى الموصى به؟\n\nقد يضر سماع صوت عالٍ لفترات طويلة بسمعك."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"هل تريد استخدام اختصار \"سهولة الاستخدام\"؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"عند تفعيل الاختصار، يؤدي الضغط على زرّي التحكّم في مستوى الصوت معًا لمدة 3 ثوانٍ إلى تفعيل إحدى ميزات إمكانية الوصول."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"هل تريد تفعيل ميزات إمكانية الوصول؟"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"يؤدي الضغط مع الاستمرار على كلا مفتاحَي التحكّم في مستوى الصوت لبضع ثوانٍ إلى تفعيل ميزات إمكانية الوصول. قد يؤدي هذا الإجراء إلى تغيير طريقة عمل جهازك.\n\nالميزات الحالية:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nيمكنك تغيير الميزات المحددة في الإعدادات > إمكانية الوصول."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"هل تريد تفعيل <xliff:g id="SERVICE">%1$s</xliff:g>؟"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"يؤدي الضغط مع الاستمرار لبضع ثوانٍ على كلا مفتاحَي التحكّم في مستوى الصوت إلى تفعيل <xliff:g id="SERVICE">%1$s</xliff:g> وهي إحدى ميزات إمكانية الوصول. يمكن أن يؤدي هذا الإجراء إلى تغيير كيفية عمل جهازك.\n\nيمكنك تغيير هذا الاختصار لاستخدامه مع ميزة أخرى في الإعدادات > أدوات تمكين الوصول."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"تفعيل"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"عدم التفعيل"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 93ce15f..e787f23 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"অনুমোদিত স্তৰতকৈ ওপৰলৈ ভলিউম বঢ়াব নেকি?\n\nদীৰ্ঘ সময়ৰ বাবে উচ্চ ভলিউমত শুনাৰ ফলত শ্ৰৱণ ক্ষমতাৰ ক্ষতি হ\'ব পাৰে।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাট ব্যৱহাৰ কৰেনে?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"শ্বৰ্টকাটটো অন হৈ থকাৰ সময়ত দুয়োটা ভলিউম বুটাম ৩ ছেকেণ্ডৰ বাবে হেঁচি ধৰি ৰাখিলে এটা সাধ্য সুবিধা আৰম্ভ হ’ব।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"সাধ্য-সুবিধাসমূহ অন কৰিবনে?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে সাধ্য-সুবিধাসমূহ অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nবর্তমানৰ সুবিধাসমূহ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nআপুনি ছেটিংসমূহ > সাধ্য-সুবিধাত কিছুমান নিৰ্দিষ্ট সুবিধা সলনি কৰিব পাৰে।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> অন কৰিবনে?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে এটা সাধ্য- সুবিধা <xliff:g id="SERVICE">%1$s</xliff:g> অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nআপুনি ছেটিংসমূহ > সাধ্য-সুবিধাসমূহত এই শ্বৰ্টকাটটো অন্য এটা সুবিধালৈ সলনি কৰিব পাৰে।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"অন কৰক"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"অন নকৰিব"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 01b815b..af57cf9 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Səsin həcmi tövsiyə olunan səviyyədən artıq olsun?\n\nYüksək səsi uzun zaman dinləmək eşitmə qabiliyyətinizə zərər vura bilər."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Əlçatımlılıq Qısayolu istifadə edilsin?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Qısayol aktiv olduqda, hər iki səs düyməsinə 3 saniyə basıb saxlamaqla əlçatımlılıq funksiyası başladılacaq."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Əlçatımlılıq funksiyaları aktiv edilsin?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyaları aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nCari funksiyalar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAyarlar və Əlçatımlılıq bölməsində seçilmiş funksiyaları dəyişə bilərsiniz."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> aktiv edilsin?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyası olan <xliff:g id="SERVICE">%1$s</xliff:g> aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nAyarlar və Əlçatımlılıq bölməsində bu qısayolu başqa bir funksiyata dəyişə bilərsiniz."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktiv edin"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktiv etməyin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index e2fc8f1..465e9d2 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite da pojačate zvuk iznad preporučenog nivoa?\n\nSlušanje glasne muzike duže vreme može da vam ošteti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li da koristite prečicu za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kada je prečica uključena, pritisnite oba dugmeta za jačinu zvuka da biste pokrenuli funkciju pristupačnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Želite li da uključite funkcije pristupačnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ako zadržite oba tastera za jačinu zvuka par sekundi, uključiće se funkcije pristupačnosti. To može da promeni način rada uređaja.\n\nPostojeće funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nMožete da promenite izabrane funkcije u odeljku Podešavanja > Pristupačnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Želite li da uključite uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako zadržite oba tastera za jačinu zvuka par sekundi, uključuje se <xliff:g id="SERVICE">%1$s</xliff:g>, funkcija pristupačnosti. To može da promeni način rada uređaja.\n\nMožete da promenite funkciju na koju se odnosi ova prečica u odeljku Podešavanja > Pristupačnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Uključi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne uključuj"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 45008f2..f0594a1 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Павялiчыць гук вышэй рэкамендаванага ўзроўню?\n\nДоўгае праслухоўванне музыкi на вялiкай гучнасцi можа пашкодзiць ваш слых."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Выкарыстоўваць камбінацыю хуткага доступу для спецыяльных магчымасцей?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Калі хуткі доступ уключаны, вы можаце націснуць абедзве кнопкі гучнасці і ўтрымліваць іх 3 секунды, каб запусціць функцыю спецыяльных магчымасцей."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Уключыць спецыяльныя магчымасці?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Утрымліванне націснутымі абедзвюх клавіш гучнасці на працягу некалькіх секунд уключае спецыяльныя магчымасці. У выніку ваша прылада можа пачаць працаваць па-іншаму.\n\nБягучыя функцыі:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nВыбраныя функцыі можна змяніць у меню \"Налады > Спецыяльныя магчымасці\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Уключыць службу \"<xliff:g id="SERVICE">%1$s</xliff:g>\"?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Утрымліванне націснутымі абедзвюх клавіш гучнасці на працягу некалькіх секунд уключае службу \"<xliff:g id="SERVICE">%1$s</xliff:g>\", якая з\'яўляецца спецыяльнай магчымасцю. У выніку ваша прылада можа пачаць працаваць па-іншаму.\n\nВы можаце задаць гэта спалучэнне клавіш для іншай функцыі ў меню \"Налады > Спецыяльныя магчымасці\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Уключыць"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не ўключаць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 0c2c022..99f6072 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Да се увеличи ли силата на звука над препоръчителното ниво?\n\nПродължителното слушане при висока сила на звука може да увреди слуха ви."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Искате ли да използвате пряк път към функцията за достъпност?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Когато прекият път е включен, можете да стартирате дадена функция за достъпност, като натиснете двата бутона за силата на звука и ги задържите за 3 секунди."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Включване на функциите за достъпност?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Натиснете двата бутона за силата на звука и ги задръжте за няколко секунди, за да включите функциите за достъпност. Това може да промени начина, по който работи устройството ви.\n\nТекущи функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nМожете да промените избраните функции от „Настройки“ > „Достъпност“."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Да се включи ли <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Натиснете двата бутона за силата на звука и ги задръжте за няколко секунди, за да включите функцията за достъпност <xliff:g id="SERVICE">%1$s</xliff:g>. Това може да промени начина, по който работи устройството ви.\n\nМожете да зададете друга функция за този пряк път от „Настройки“ > „Достъпност“."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Включване"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Без включване"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index b10d8dc..a07a1fe 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"প্রস্তাবিত স্তরের চেয়ে বেশি উঁচুতে ভলিউম বাড়াবেন?\n\nউঁচু ভলিউমে বেশি সময় ধরে কিছু শুনলে আপনার শ্রবনশক্তির ক্ষতি হতে পারে।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"অ্যাক্সেসযোগ্যতা শর্টকাট ব্যবহার করবেন?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"শর্টকাট চালু করা থাকাকালীন দুটি ভলিউম বোতাম একসাথে ৩ সেকেন্ড টিপে ধরে রাখলে একটি অ্যাকসেসিবিলিটি ফিচার চালু হবে।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"অ্যাক্সেসিবিলিটি ফিচার চালু করতে চান?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"উভয় ভলিউম কী কয়েক সেকেন্ড ধরে থাকলে অ্যাক্সেসিবিলিটি ফিচার চালু হয়ে যাবে। এর ফলে, আপনার ডিভাইস কীভাবে কাজ করবে সেটিতে পরিবর্তন হতে পারে।\n\nবর্তমান ফিচার:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nসেটিংস > অ্যাক্সেসিবিলিটি বিকল্প থেকে আপনি বাছাই করা ফিচার পরিবর্তন করতে পারবেন।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> চালু করতে চান?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"উভয় ভলিউম কী কয়েক সেকেন্ড ধরে থাকলে <xliff:g id="SERVICE">%1$s</xliff:g> চালু হয়ে যাবে। এটি একটি অ্যাক্সেসিবিলিটি ফিচার। এর ফলে, আপনার ডিভাইস কীভাবে কাজ করবে সেটিতে পরিবর্তন হতে পারে।\n\nসেটিংস > অ্যাক্সেসিবিলিটি থেকে আপনি এই শর্টকাট পরিবর্তন করতে পারবেন।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"চালু করুন"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"চালু করবেন না"</string>
@@ -1829,8 +1831,7 @@
<item quantity="one">%d ঘন্টার জন্য</item>
<item quantity="other">%d ঘন্টার জন্য</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত (পরবর্তী অ্যালার্ম)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 17c0d1d..1292fc1 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite li pojačati zvuk iznad preporučenog nivoa?\n\nDužim slušanjem glasnog zvuka možete oštetiti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li koristiti Prečicu za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kada je prečica uključena, pritiskom i držanjem oba dugmeta za jačinu zvuka u trajanju od 3 sekunde pokrenut će se funkcija pristupačnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Uključiti funkcije pristupačnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkcije pristupačnosti. Ovo može uticati na način rada uređaja.\n\nTrenutne funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nOdabrane funkcije možete promijeniti u odjeljku Postavke > Pristupačnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Uključiti <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkciju pristupačnosti <xliff:g id="SERVICE">%1$s</xliff:g>. Ovo može promijeniti način rada uređaja.\n\nOvu prečicu možete zamijeniti drugom funkcijom u odjeljku Postavke > Pristupačnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Uključi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nemoj uključiti"</string>
@@ -1860,7 +1862,7 @@
<item quantity="few">%d sata</item>
<item quantity="other">%d sati</item>
</plurals>
- <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sljedeći alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dok ne isključite"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e0c709d..28d961b 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -991,8 +991,8 @@
<string name="searchview_description_submit" msgid="6771060386117334686">"Envia la consulta"</string>
<string name="searchview_description_voice" msgid="42360159504884679">"Cerca per veu"</string>
<string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Vols activar l\'exploració tàctil?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interactuar amb la tauleta."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració per tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interactuar amb el telèfon."</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interaccionar amb la tauleta."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració per tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interaccionar amb el telèfon."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"Fa 1 mes"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Fa més d\'1 mes"</string>
<plurals name="last_num_days" formatted="false" msgid="687443109145393632">
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vols apujar el volum per sobre del nivell recomanat?\n\nSi escoltes música a un volum alt durant períodes llargs, pots danyar-te l\'oïda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vols fer servir la drecera d\'accessibilitat?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Si la drecera està activada, prem els dos botons de volum durant 3 segons per iniciar una funció d\'accessibilitat."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vols activar les funcions d\'accessibilitat?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si mantens premudes les dues tecles de volum durant uns segons, s\'activaran les funcions d\'accessibilitat. Això podria canviar el funcionament del teu dispositiu.\n\nFuncions actuals:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPots canviar les funcions seleccionades a Configuració > Accessibilitat."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vols activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si mantens premudes les dues tecles de volum durant uns segons, la funció d\'accessibilitat <xliff:g id="SERVICE">%1$s</xliff:g> s\'activarà. Això podria canviar el funcionament del teu dispositiu.\n\nPots canviar la funció d\'aquesta drecera a Configuració > Accessibilitat."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activa"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"No activis"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 095ec69..89f679e 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zvýšit hlasitost nad doporučenou úroveň?\n\nDlouhodobý poslech hlasitého zvuku může poškodit sluch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Použít zkratku přístupnosti?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Když je tato zkratka zapnutá, můžete funkci přístupnosti spustit tím, že na tři sekundy podržíte obě tlačítka hlasitosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Zapnout funkce pro usnadnění přístupu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Podržením obou tlačítek hlasitosti po dobu několika sekund zapnete funkce pro usnadnění přístupu. Tato funkce může změnit fungování zařízení.\n\nAktuální funkce:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nVybrané funkce můžete změnit v Nastavení > Přístupnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Zapnout <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Podržením obou tlačítek hlasitosti po dobu několika sekund zapnete funkci pro usnadnění přístupu <xliff:g id="SERVICE">%1$s</xliff:g>. Tato funkce může změnit fungování zařízení.\n\nZkratku můžete nastavit na jinou funkci v Nastavení > Přístupnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Zapnout"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nezapínat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 499b9de..e9d477d 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vil du skrue højere op end det anbefalede lydstyrkeniveau?\n\nDu kan skade hørelsen ved at lytte til meget høj musik over længere tid."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vil du bruge genvejen til Hjælpefunktioner?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Når genvejen er aktiveret, kan du starte en hjælpefunktion ved at trykke på begge lydstyrkeknapper i tre sekunder."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vil du aktivere hjælpefunktionerne?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hvis du holder begge lydstyrkeknapperne nede i et par sekunder, aktiveres hjælpefunktionerne. Det kan ændre på, hvordan din enhed fungerer.\n\nAktuelle funktioner:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kan ændre de valgte funktioner i Indstillinger > Hjælpefunktioner."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vil du aktivere <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hvis du holder begge lydstyrkeknapperne nede i et par sekunder, aktiveres hjælpefunktionen <xliff:g id="SERVICE">%1$s</xliff:g>. Det kan ændre på, hvordan din enhed fungerer.\n\nDu kan ændre denne genvej til en anden funktion i Indstillinger > Hjælpefunktioner."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivér"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktivér ikke"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 867efac..682491e 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lautstärke über den Schwellenwert anheben?\n\nWenn du über einen längeren Zeitraum Musik in hoher Lautstärke hörst, kann dies dein Gehör schädigen."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Verknüpfung für Bedienungshilfen verwenden?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Wenn die Verknüpfung aktiviert ist, kannst du die beiden Lautstärketasten drei Sekunden lang gedrückt halten, um eine Bedienungshilfe zu starten."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Bedienungshilfen aktivieren?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfen. Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nAktuelle Funktionen:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kannst ausgewählte Funktionen unter \"Einstellungen\" > \"Bedienungshilfen\" ändern."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> aktivieren?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfe \"<xliff:g id="SERVICE">%1$s</xliff:g>\". Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nUnter \"Einstellungen > \"Bedienungshilfen\" kannst du dieser Verknüpfung eine andere Funktion zuweisen."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivieren"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nicht aktivieren"</string>
@@ -1792,8 +1794,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
- <string name="battery_saver_description" msgid="6794188153647295212">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
<string name="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>
@@ -1829,8 +1831,7 @@
<item quantity="other">Für %d h</item>
<item quantity="one">Für 1 h</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nächste Weckzeit)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Bis zur Deaktivierung"</string>
@@ -2002,9 +2003,9 @@
<string name="notification_feedback_indicator" msgid="663476517711323016">"Feedback geben"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Infomitteilung zum Ablaufmodus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Dein Akku könnte vor der gewöhnlichen Ladezeit leer sein"</string>
- <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Energiesparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
- <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Energiesparmodus"</string>
- <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Energiesparmodus deaktiviert"</string>
+ <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Stromsparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
+ <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Stromsparmodus"</string>
+ <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Stromsparmodus deaktiviert"</string>
<string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Das Smartphone ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Das Tablet ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
<string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Das Gerät ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index d4d1c5a..9449ca0 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Αυξάνετε την ένταση ήχου πάνω από το επίπεδο ασφαλείας;\n\nΑν ακούτε μουσική σε υψηλή ένταση για μεγάλο χρονικό διάστημα ενδέχεται να προκληθεί βλάβη στην ακοή σας."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Να χρησιμοποιείται η συντόμευση προσβασιμότητας;"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Όταν η συντόμευση είναι ενεργοποιημένη, το πάτημα και των δύο κουμπιών έντασης ήχου για 3 δευτερόλεπτα θα ξεκινήσει μια λειτουργία προσβασιμότητας."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Θέλετε να ενεργοποιήσετε τις λειτουργίες προσβασιμότητας;"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Για να ενεργοποιήσετε τις λειτουργίες προσβασιμότητας, πατήστε παρατεταμένα τα δύο πλήκτρα έντασης για μερικά δευτερόλεπτα. Αυτό ενδέχεται να αλλάξει τον τρόπο λειτουργίας της συσκευής σας.\n\nΤρέχουσες λειτουργίες:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nΜπορείτε να αλλάξετε τις επιλεγμένες λειτουργίες στις Ρυθμίσεις > Προσβασιμότητα."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Θέλετε να ενεργοποιήσετε τη λειτουργία <xliff:g id="SERVICE">%1$s</xliff:g>;"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Μπορείτε να ενεργοποιήσετε τη λειτουργία <xliff:g id="SERVICE">%1$s</xliff:g>, η οποία είναι μία από τις λειτουργίες προσβασιμότητας, πατώντας παρατεταμένα ταυτόχρονα τα δύο πλήκτρα έντασης ήχου για μερικά δευτερόλεπτα. Αυτό ενδέχεται να αλλάξει τον τρόπο λειτουργίας της συσκευής σας.\n\nΜπορείτε να αλλάξετε αυτή τη συντόμευση σε μια άλλη λειτουργία στις Ρυθμίσεις > Προσβασιμότητα."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ενεργοποίηση"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Να μην ενεργοποιηθούν"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index f140ce3..0788a60 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index e814bdf..6e841ec 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 73f1562..731f184 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 122ea7f..8bd3597 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 53d8c66..f9793d2 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for 3 seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 953b6fc..0353000 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar a un alto volumen durante largos períodos puede dañar tu audición."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"¿Usar acceso directo de accesibilidad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Cuando la combinación de teclas está activada, puedes presionar los botones de volumen durante 3 segundos para iniciar una función de accesibilidad."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"¿Quieres activar las funciones de accesibilidad?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si mantienes presionadas las dos teclas de volumen durante unos segundos, se activarán las funciones de accesibilidad. Esto puede cambiar el funcionamiento de tu dispositivo.\n\nFunciones actuales:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuedes cambiar las funciones seleccionadas en Configuración > Accesibilidad."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"¿Quieres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si mantienes presionadas ambas teclas de volumen durante unos segundos, se activará la función de accesibilidad <xliff:g id="SERVICE">%1$s</xliff:g>. Esto podría cambiar la forma en que funciona tu dispositivo.\n\nPuedes cambiar este acceso directo a otra función en Configuración > Accesibilidad."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"No activar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 465d2ec..e64f568 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar sonidos fuertes durante mucho tiempo puede dañar los oídos."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"¿Utilizar acceso directo de accesibilidad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Si el acceso directo está activado, pulsa los dos botones de volumen durante 3 segundos para iniciar una función de accesibilidad."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"¿Activar funciones de accesibilidad?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Al mantener pulsadas las dos teclas de volumen durante unos segundos, se activan las funciones de accesibilidad, que pueden cambiar el funcionamiento del dispositivo.\n\nFunciones actuales:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuedes cambiar las funciones seleccionadas en Ajustes > Accesibilidad."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"¿Quieres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Al mantener pulsadas ambas teclas de volumen durante unos segundos se activa <xliff:g id="SERVICE">%1$s</xliff:g>, una función de accesibilidad. Esta función puede modificar el funcionamiento del dispositivo.\n\nPuedes asignar este acceso directo a otra función en Ajustes > Accesibilidad."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"No activar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 6d2a4c0f..a3622d64 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Kas suurendada helitugevuse taset üle soovitatud taseme?\n\nPikaajaline valju helitugevusega kuulamine võib kuulmist kahjustada."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Kas kasutada juurdepääsetavuse otseteed?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kui otsetee on sisse lülitatud, käivitab mõlema helitugevuse nupu kolm sekundit all hoidmine juurdepääsetavuse funktsiooni."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Kas lülitada juurdepääsufunktsioonid sisse?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hoidke juurdepääsufunktsioonide sisselülitamiseks mõlemat helitugevuse klahvi mõni sekund all. See võib teie seadme tööviisi muuta.\n\nPraegused funktsioonid:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nValitud funktsioone saab muuta jaotises Seaded > Juurdepääsetavus."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Kas lülitada <xliff:g id="SERVICE">%1$s</xliff:g> sisse?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Kui hoiate mõlemat helitugevuse klahvi mõni sekund all, lülitatakse juurdepääsufunktsioon <xliff:g id="SERVICE">%1$s</xliff:g> sisse. See võib teie seadme tööviisi muuta.\n\nSelle otsetee saab asendada muu otseteega jaotises Seaded > Juurdepääsetavus."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Lülita sisse"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ära lülita sisse"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 7241b97..47d41f5 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Bolumena gomendatutako mailatik gora igo nahi duzu?\n\nMusika bolumen handian eta denbora luzez entzuteak entzumena kalte diezazuke."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erabilerraztasun-lasterbidea erabili nahi duzu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Lasterbidea aktibatuta dagoenean, bi bolumen-botoiak hiru segundoz sakatuta abiaraziko da erabilerraztasun-eginbidea."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Erabilerraztasun-eginbideak aktibatu nahi dituzu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Eduki sakatuta bolumen-botoiak segundo batzuez erabilerraztasun-eginbideak aktibatzeko. Hori eginez gero, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nUneko eginbideak:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nHautatutako eginbideak aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> aktibatu nahi duzu?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Eduki sakatuta bolumen-botoiak segundo batzuez <xliff:g id="SERVICE">%1$s</xliff:g> izeneko erabilerraztasun-eginbidea aktibatzeko. Honen bidez, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nLasterbide hau beste eginbide batengatik aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktibatu"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ez aktibatu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 02f4728..807a7ae 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"میزان صدا را به بالاتر از حد توصیه شده افزایش میدهید؟\n\nگوش دادن به صداهای بلند برای مدت طولانی میتواند به شنواییتان آسیب وارد کند."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"از میانبر دسترسپذیری استفاده شود؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"وقتی میانبر روشن باشد، با فشار دادن هردو دکمه صدا بهمدت ۳ ثانیه ویژگی دسترسپذیری فعال میشود."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ویژگیهای دسترسپذیری روشن شود؟"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"با پایین نگه داشتن هردو کلید میزان صدا بهمدت چند ثانیه، ویژگیهای دسترسپذیری روشن میشود. با این کار نحوه عملکرد دستگاهتان تغییر میکند.\n\nویژگیهای فعلی:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nمیتوانید ویژگیهای انتخابی را در «تنظیمات > دسترسپذیری» تغییر دهید."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> روشن شود؟"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"با پایین نگه داشتن هردو کلید میزان صدا بهمدت چند ثانیه، <xliff:g id="SERVICE">%1$s</xliff:g> (یکی از ویژگیهای دسترسپذیری) روشن میشود. با این کار نحوه عملکرد دستگاهتان تغییر میکند.\n\nمیتوانید در «تنظیمات > دسترسپذیری»،این میانبر را به ویژگی دیگری تغییر دهید."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"روشن شود"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"روشن نشود"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0c94f35..da7ab3c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Nostetaanko äänenvoimakkuus suositellun tason yläpuolelle?\n\nPitkäkestoinen kova äänenvoimakkuus saattaa heikentää kuuloa."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Käytetäänkö esteettömyyden pikanäppäintä?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kun pikanäppäin on käytössä, voit käynnistää esteettömyystoiminnon pitämällä molempia äänenvoimakkuuspainikkeita painettuna kolmen sekunnin ajan."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Laitetaanko esteettömyysominaisuudet päälle?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Molempien äänenvoimakkuuspainikkeiden painaminen muutaman sekunnin ajan laittaa esteettömyysominaisuudet päälle. Tämä voi muuttaa laitteesi toimintaa.\n\nNykyiset ominaisuudet:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nVoit muuttaa valittuja ominaisuuksia kohdassa Asetukset > Esteettömyys."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Laitetaanko <xliff:g id="SERVICE">%1$s</xliff:g> päälle?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Molempien äänenvoimakkuuspainikkeiden pitkään painaminen laittaa päälle esteettömyysominaisuuden <xliff:g id="SERVICE">%1$s</xliff:g>. Tämä voi muuttaa laitteesi toimintaa.\n\nVoit muuttaa tätä pikanäppäintä kohdassa Asetukset > Esteettömyys."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Laita päälle"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Älä laita päälle"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index dcfe8ad..d44f036 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Augmenter le volume au-dessus du niveau recommandé?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utiliser le raccourci d\'accessibilité?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour lancer une fonctionnalité d\'accessibilité."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Activer les fonctionnalités d\'accessibilité?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si vous maintenez enfoncées les deux touches de volume pendant quelques secondes, vous activez les fonctionnalités d\'accessibilité. Cela peut modifier le fonctionnement de votre appareil.\n\nFonctionnalités actuellement utilisées :\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPour les modifier, sélectionnez Paramètres > Accessibilité."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Activer <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si vous maintenez enfoncées les deux touches de volume pendant quelques secondes, vous activez la fonctionnalité d\'accessibilité <xliff:g id="SERVICE">%1$s</xliff:g>. Cela peut modifier le fonctionnement de votre appareil.\n\nPour attribuer ce raccourci à une autre fonctionnalité, sélectionnez Paramètres > Accessibilité."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activer"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne pas activer"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 5fc2d69..57040d0 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Augmenter le volume au dessus du niveau recommandé ?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utiliser le raccourci d\'accessibilité ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour démarrer une fonctionnalité d\'accessibilité."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Activer les fonctionnalités d\'accessibilité ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si vous appuyez sur les deux touches de volume pendant quelques secondes, vous activez des fonctionnalités d\'accessibilité. Cela peut affecter le fonctionnement de votre appareil.\n\nFonctionnalités actuellement utilisées :\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPour les modifier, accédez à Paramètres > Accessibilité."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Activer <xliff:g id="SERVICE">%1$s</xliff:g> ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si vous appuyez sur les deux touches de volume pendant quelques secondes, vous activez la fonctionnalité d\'accessibilité <xliff:g id="SERVICE">%1$s</xliff:g>. Cela peut affecter le fonctionnement de votre appareil.\n\nPour attribuer ce raccourci à une autre fonctionnalité, accédez à Paramètres > Accessibilité."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activer"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne pas activer"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index a6eac55..581dda5 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Queres subir o volume máis do nivel recomendado?\n\nA reprodución de son a un volume elevado durante moito tempo pode provocar danos nos oídos."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Queres utilizar o atallo de accesibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Cando o atallo está activado, podes premer os dous botóns de volume durante 3 segundos para iniciar unha función de accesibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Queres activar as funcións de accesibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ao manter as dúas teclas de volume premidas durante uns segundos actívanse as funcións de accesibilidade. Esta acción pode cambiar o funcionamento do dispositivo.\n\nFuncións activadas actualmente:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPodes cambiar as funcións seleccionadas en Configuración > Accesibilidade."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Queres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ao manter as dúas teclas de volume premidas durante uns segundos actívase <xliff:g id="SERVICE">%1$s</xliff:g>, unha función de accesibilidade. Esta acción pode cambiar o funcionamento do dispositivo.\n\nPodes cambiar o uso deste atallo para outra función en Configuración > Accesibilidade."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Non activar"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 6b4bbf3..c95b8eb 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ભલામણ કરેલ સ્તરની ઉપર વૉલ્યૂમ વધાર્યો?\n\nલાંબા સમય સુધી ઊંચા અવાજે સાંભળવું તમારી શ્રવણક્ષમતાને નુકસાન પહોંચાડી શકે છે."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ઍક્સેસિબિલિટી શૉર્ટકટનો ઉપયોગ કરીએ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"જ્યારે શૉર્ટકટ ચાલુ હોય, ત્યારે બન્ને વૉલ્યૂમ બટનને 3 સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા શરૂ થઈ જશે."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ઍક્સેસિબિલિટી સુવિધાઓ ચાલુ કરીએ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"બન્ને વૉલ્યૂમ કીને થોડી સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધાઓ ચાલુ થઈ જાય છે. આનાથી તમારા ડિવાઇસની કામ કરવાની રીત બદલાઈ શકે છે.\n\nવર્તમાન સુવિધાઓ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nતમે સેટિંગ > ઍક્સેસિબિલિટીમાં જઈને પસંદ કરેલી સુવિધાઓને બદલી શકો છો."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g>ને ચાલુ કરીએ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"બન્ને વૉલ્યૂમ કીને થોડી સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા એવી <xliff:g id="SERVICE">%1$s</xliff:g> ચાલુ થઈ જાય છે. આનાથી તમારા ડિવાઇસની કામ કરવાની રીત બદલાઈ શકે છે.\n\nતમે સેટિંગ > ઍક્સેસિબિલિટીમાં જઈને આ શૉર્ટકટને બીજી સુવિધામાં બદલી શકો છો."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ચાલુ કરો"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ચાલુ કરશો નહીં"</string>
@@ -1829,8 +1831,7 @@
<item quantity="one">%d કલાક માટે</item>
<item quantity="other">%d કલાક માટે</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> સુધી"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> સુધી"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (આગલા એલાર્મ) સુધી"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"તમે બંધ ન કરો ત્યાં સુધી"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 3bf9dcd..b56fa2c 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"वॉल्यूम को सुझाए गए स्तर से ऊपर बढ़ाएं?\n\nअत्यधिक वॉल्यूम पर ज़्यादा समय तक सुनने से आपकी सुनने की क्षमता को नुकसान हो सकता है."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"सुलभता शॉर्टकट का इस्तेमाल करना चाहते हैं?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"शॉर्टकट के चालू होने पर, दाेनाें वॉल्यूम बटन (आवाज़ कम या ज़्यादा करने वाले बटन) को तीन सेकंड तक दबाने से, सुलभता सुविधा शुरू हाे जाएगी."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"सुलभता सुविधाएं चालू करना चाहते हैं?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"आवाज़ कम और ज़्यादा करने वाले दोनों बटन को कुछ सेकंड तक दबाकर रखने से सुलभता सुविधाएं चालू हो जाती हैं. ऐसा करने से आपके डिवाइस के काम करने के तरीके में बदलाव हो सकता है.\n\nमौजूदा सुविधाएं:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nआप सेटिंग और सुलभता में जाकर चुनी हुई सुविधाएं बदल सकते हैं."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> चालू करना चाहते हैं?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"आवाज़ कम और ज़्यादा करने वाले दोनों बटन को कुछ सेकंड तक दबाकर रखने से <xliff:g id="SERVICE">%1$s</xliff:g> चालू हो जाती है, जो एक सुलभता सुविधा है. ऐसा करने से आपके डिवाइस के काम करने के तरीके में बदलाव हो सकता है.\n\nआप सेटिंग और सुलभता में जाकर इस शॉर्टकट को किसी दूसरी सुविधा के लिए बदल सकते हैं."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"चालू करें"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"चालू न करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index d350d04..2678cb8 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite li pojačati zvuk iznad preporučene razine?\n\nDugotrajno slušanje glasne glazbe može vam oštetiti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li upotrebljavati prečac za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kad je taj prečac uključen, pritiskom na obje tipke za glasnoću na tri sekunde pokrenut će se značajka pristupačnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Želite li uključiti značajke pristupačnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Značajke pristupačnosti uključuju se ako na nekoliko sekundi pritisnete obje tipke za glasnoću. Time se može promijeniti način na koji vaš uređaj radi.\n\nTrenutačne značajke:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nOdabrane značajke možete promijeniti u odjeljku Postavke > Pristupačnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Želite li uključiti uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako na nekoliko sekundi pritisnete obje tipke za glasnoću, uključuje se značajka pristupačnosti <xliff:g id="SERVICE">%1$s</xliff:g>. Time se može promijeniti način na koji vaš uređaj radi.\n\nZnačajku na koju se taj prečac odnosi možete promijeniti u odjeljku Postavke > Pristupačnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Uključi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nemoj uključiti"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 70f87b6..235c8fd 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Az ajánlott szint fölé szeretné emelni a hangerőt?\n\nHa hosszú időn át teszi ki magát nagy hangerőnek, azzal károsíthatja a hallását."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Szeretné használni a Kisegítő lehetőségek billentyűparancsot?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Ha a gyorsparancs aktív, akkor a két hangerőgomb három másodpercig tartó együttes lenyomásával kisegítő funkciót indíthat el."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Bekapcsolja a kisegítő lehetőségeket?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"A kisegítő lehetőségek bekapcsolásához tartsa nyomva néhány másodpercig mindkét hangerőgombot. Ez hatással lehet az eszköz működésére.\n\nJelenlegi funkciók:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nA kiválasztott funkciókat a Beállítások > Kisegítő lehetőségek pontban módosíthatja."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Bekapcsolja a következőt: <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"A(z) <xliff:g id="SERVICE">%1$s</xliff:g> kisegítő lehetőség bekapcsolásához tartsa nyomva néhány másodpercig mindkét hangerőgombot. Ez hatással lehet az eszköz működésére.\n\nEzt a gyorsparancsot a Beállítások > Kisegítő lehetőségek pontban módosíthatja másik funkció használatára."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Bekapcsolom"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nem kapcsolom be"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 32ac6a8..3634908 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ձայնը բարձրացնե՞լ խորհուրդ տրվող մակարդակից ավել:\n\nԵրկարատև բարձրաձայն լսելը կարող է վնասել ձեր լսողությունը:"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Օգտագործե՞լ Մատչելիության դյուրանցումը։"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Հատուկ գործառույթն օգտագործելու համար սեղմեք և 3 վայրկյան սեղմած պահեք ձայնի ուժգնության երկու կոճակները, երբ գործառույթը միացված է:"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Միացնե՞լ հատուկ գործառույթները"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ձայնի կարգավորման երկու կոճակները մի քանի վայրկյան սեղմած պահելով կմիացնեք հատուկ գործառույթները։ Դրա արդյունքում սարքի աշխատաեղանակը կարող է փոխվել։\n\nԸնթացիկ գործառույթներ՝\n<xliff:g id="SERVICE">%1$s</xliff:g>\nԸնտրված գործառույթները փոխելու համար անցեք Կարգավորումներ > Հատուկ գործառույթներ։"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Միացնե՞լ <xliff:g id="SERVICE">%1$s</xliff:g> ծառայությունը"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ձայնի կարգավորման երկու կոճակները մի քանի վայրկյան սեղմած պահելով կմիացնեք <xliff:g id="SERVICE">%1$s</xliff:g> ծառայությունը, որը հատուկ գործառույթ է։ Դրա արդյունքում սարքի աշխատաեղանակը կարող է փոխվել։\n\nԱյս դյուրանցումը մեկ այլ գործառույթով փոխելու համար անցեք Կարգավորումներ > Հատուկ գործառույթներ։"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Միացնել"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Չմիացնել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f8ba016..5974594 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Mengeraskan volume di atas tingkat yang disarankan?\n\nMendengarkan dengan volume keras dalam waktu yang lama dapat merusak pendengaran Anda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gunakan Pintasan Aksesibilitas?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Saat pintasan aktif, menekan kedua tombol volume selama 3 detik akan memulai fitur aksesibilitas."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Aktifkan fitur aksesibilitas?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Menahan kedua tombol volume selama beberapa detik akan mengaktifkan fitur aksesibilitas. Tindakan ini dapat mengubah cara kerja perangkat Anda.\n\nFitur saat ini:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAnda dapat mengubah fitur yang dipilih di Setelan > Aksesibilitas."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Aktifkan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Menahan kedua tombol volume selama beberapa detik akan mengaktifkan <xliff:g id="SERVICE">%1$s</xliff:g>, yang merupakan fitur aksesibilitas. Tindakan ini dapat mengubah cara kerja perangkat Anda.\n\nAnda dapat mengubah pintasan ini ke fitur lain di Setelan > Aksesibilitas."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktifkan"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Jangan aktifkan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 9278bbb..64e855f 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Hækka hljóðstyrk umfram ráðlagðan styrk?\n\nEf hlustað er á háum hljóðstyrk í langan tíma kann það að skaða heyrnina."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Viltu nota aðgengisflýtileið?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Þegar flýtileiðin er virk er kveikt á aðgengiseiginleikanum með því að halda báðum hljóðstyrkshnöppunum inni í þrjár sekúndur."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Viltu kveikja á aðgengiseiginleikum?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Kveikt er á aðgengiseiginleikum þegar báðum hljóðstyrkstökkunum er haldið inni í nokkrar sekúndur. Þetta getur breytt því hvernig tækið virkar.\n\nNúverandi eiginleikar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nÞú getur breytt völdum eiginleikum í Stillingar > Aðgengi."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Viltu kveikja á <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ef báðum hljóðstyrkstökkunum er haldið inni í nokkrar sekúndur er kveikt á aðgengiseiginleikanum <xliff:g id="SERVICE">%1$s</xliff:g>. Þetta getur breytt því hvernig tækið virkar.\n\nÞú getur breytt þessari flýtileið í annan eiginleika í Stillingar > Aðgengi."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Kveikja"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ekki kveikja"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b962d8b..185bdcf 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vuoi aumentare il volume oltre il livello consigliato?\n\nL\'ascolto ad alto volume per lunghi periodi di tempo potrebbe danneggiare l\'udito."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usare la scorciatoia Accessibilità?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando la scorciatoia è attiva, puoi premere entrambi i pulsanti del volume per tre secondi per avviare una funzione di accessibilità."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Attivare le funzioni di accessibilità?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Se tieni premuti entrambi i tasti del volume per qualche secondo, vengono attivate le funzioni di accessibilità. Questa operazione potrebbe modificare il funzionamento del dispositivo.\n\nFunzioni correnti:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuoi modificare le funzioni selezionate in Impostazioni > Accessibilità."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Attivare <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Se tieni premuti entrambi i tasti del volume per qualche secondo verrà attivata la funzione di accessibilità <xliff:g id="SERVICE">%1$s</xliff:g>. Questa operazione potrebbe modificare il funzionamento del dispositivo.\n\nPuoi associare questa scorciatoia a un\'altra funzionalità in Impostazioni > Accessibilità."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Attiva"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Non attivare"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 821e4fd..963489e 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"האם להעלות את עוצמת הקול מעל לרמה המומלצת?\n\nהאזנה בעוצמת קול גבוהה למשכי זמן ממושכים עלולה לפגוע בשמיעה."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"להשתמש בקיצור הדרך לתכונת הנגישות?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"כשקיצור הדרך מופעל, לחיצה על שני לחצני עוצמת הקול למשך שלוש שניות מפעילה את תכונת הנגישות."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"להפעיל את תכונות הנגישות?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"לחיצה ארוכה על שני לחצני עוצמת הקול למשך מספר שניות מפעילה את תכונות הנגישות. בעקבות זאת, ייתכן שאופן הפעולה של המכשיר ישתנה.\n\nהתכונות הנוכחיות:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nניתן לשנות תכונות נבחרות ב\'הגדרות\' > \'נגישות\'."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"להפעיל את <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ניתן ללחוץ על שני מקשי עוצמת הקול למשך מספר שניות כדי להפעיל את <xliff:g id="SERVICE">%1$s</xliff:g>, תכונת נגישות. בעקבות זאת, ייתכן שאופן הפעולה של המכשיר ישתנה.\n\nאפשר לשנות את מקשי הקיצור האלה לתכונה נוספת ב\'הגדרות\' > \'נגישות\'."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"אני רוצה להפעיל"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"לא להפעיל"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4b48495..ee9f0eb 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"推奨レベルを超えるまで音量を上げますか?\n\n大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ユーザー補助機能のショートカットの使用"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ショートカットが ON の場合、両方の音量ボタンを 3 秒ほど長押しするとユーザー補助機能が起動します。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ユーザー補助機能を ON にしますか?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"音量大と音量小の両方のボタンを数秒ほど長押しすると、ユーザー補助機能が ON になります。この機能が ON になると、デバイスの動作が変わることがあります。\n\n現在の機能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n選択した機能は [設定] > [ユーザー補助] で変更できます。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> を ON にしますか?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"音量大と音量小の両方のボタンを数秒ほど長押しすると、ユーザー補助機能の <xliff:g id="SERVICE">%1$s</xliff:g> が ON になります。この機能が ON になると、デバイスの動作が変わることがあります。\n\nこのショートカットは [設定] > [ユーザー補助] で別の機能に変更できます。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ON にする"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ON にしない"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index cdac647..72d9374 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"გსურთ ხმის რეკომენდებულ დონეზე მაღლა აწევა?\n\nხანგრძლივად ხმამაღლა მოსმენით შესაძლოა სმენადობა დაიზიანოთ."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"გსურთ მარტივი წვდომის მალსახმობის გამოყენება?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"თუ მალსახმობი ჩართულია, ხმის ორივე ღილაკზე 3 წამის განმავლობაში დაჭერით მარტივი წვდომის ფუნქცია ჩაირთვება."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"გსურთ, ჩართოთ მარტივი წვდომის ფუნქციები?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ხმის ორივე ღილაკზე ხანგრძლივად დაჭერა რამდენიმე წამის განმავლობაში ჩართავს მარტივი წვდომის ფუნქციებს. ამ ქმედებამ შეიძლება შეცვალოს თქვენი მოწყობილობის მუშაობის პრინციპი.\n\nამჟამინდელი ფუნქციები:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nარჩეული ფუნქციების შეცვლა შესაძლებელია აქ: პარამეტრები > მარტივი წვდომა."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ჩაირთოს <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ხმის ორივე ღილაკზე რამდენიმე წამის განმავლობაში დაჭერით ჩაირთვება <xliff:g id="SERVICE">%1$s</xliff:g>, რომელიც მარტივი წვდომის ფუნქციაა. ამან შეიძლება შეცვალოს თქვენი მოწყობილობის მუშაობის პრინციპი.\n\nამ მალსახმობის შეცვლა სხვა ფუნქციით შეგიძლიათ აქ: პარამეტრები > აპები."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ჩართვა"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"არ ჩაირთოს"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 608ed1e..ca1500d 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дыбыс деңгейін ұсынылған деңгейден көтеру керек пе?\n\nЖоғары дыбыс деңгейінде ұзақ кезеңдер бойы тыңдау есту қабілетіңізге зиян тигізуі мүмкін."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Арнайы мүмкіндік төте жолын пайдалану керек пе?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Түймелер тіркесімі қосулы кезде, екі дыбыс түймесін 3 секунд басып тұрсаңыз, \"Арнайы мүмкіндіктер\" функциясы іске қосылады."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Арнайы мүмкіндіктер іске қосылсын ба?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, арнайы мүмкіндіктер іске қосылады. Бұл – құрылғының жұмысына әсер етуі мүмкін.\n\nҚазіргі функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТаңдалған функцияларды \"Параметрлер > Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> қосылсын ба?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, <xliff:g id="SERVICE">%1$s</xliff:g> арнайы қызметі іске қосылады. Бұл – құрылғының жүмысына әсер етуі мүмкін.\n\nБұл таңбашаны басқа функцияға \"Параметрлер > Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Қосылсын"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Қосылмасын"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 14b0189..4424426 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"បង្កើនកម្រិតសំឡេងលើសពីកម្រិតបានផ្ដល់យោបល់?\n\nការស្ដាប់នៅកម្រិតសំឡេងខ្លាំងយូរអាចធ្វើឲ្យខូចត្រចៀក។"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ប្រើប្រាស់ផ្លូវកាត់ភាពងាយស្រួល?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"នៅពេលបើកផ្លូវកាត់ ការចុចប៊ូតុងកម្រិតសំឡេងទាំងពីររយៈពេល 3 វិនាទីនឹងចាប់ផ្តើមមុខងារភាពងាយប្រើ។"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"បើកមុខងារភាពងាយប្រើឬ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ការសង្កត់គ្រាប់ចុចកម្រិតសំឡេងទាំងពីរឱ្យជាប់រយៈពេលពីរបីវិនាទីនឹងបើកមុខងារភាពងាយប្រើ។ ការធ្វើបែបនេះអាចផ្លាស់ប្ដូររបៀបដែលឧបករណ៍របស់អ្នកដំណើរការ។\n\nមុខងារបច្ចុប្បន្ន៖\n<xliff:g id="SERVICE">%1$s</xliff:g>\nអ្នកអាចប្ដូរមុខងារដែលបានជ្រើសរើសនៅក្នុងការកំណត់ > ភាពងាយស្រួល។"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"បើក <xliff:g id="SERVICE">%1$s</xliff:g> ឬ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ការសង្កត់គ្រាប់ចុចកម្រិតសំឡេងទាំងពីរឱ្យជាប់រយៈពេលពីរបីវិនាទីនឹងបើក <xliff:g id="SERVICE">%1$s</xliff:g> ដែលជាមុខងារភាពងាយប្រើ។ ការធ្វើបែបនេះអាចផ្លាស់ប្ដូររបៀបដែលឧបករណ៍របស់អ្នកដំណើរការ។\n\nអ្នកអាចប្ដូរផ្លូវកាត់នេះទៅមុខងារផ្សេងទៀតនៅក្នុងការកំណត់ > ភាពងាយស្រួល។"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"បើក"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"កុំបើក"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 0fe1c24..4e2e7e0 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ವಾಲ್ಯೂಮ್ ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾದ ಮಟ್ಟಕ್ಕಿಂತಲೂ ಹೆಚ್ಚು ಮಾಡುವುದೇ?\n\nದೀರ್ಘ ಅವಧಿಯವರೆಗೆ ಹೆಚ್ಚಿನ ವಾಲ್ಯೂಮ್ನಲ್ಲಿ ಆಲಿಸುವುದರಿಂದ ನಿಮ್ಮ ಆಲಿಸುವಿಕೆ ಸಾಮರ್ಥ್ಯಕ್ಕೆ ಹಾನಿಯುಂಟು ಮಾಡಬಹುದು."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್ಕಟ್ ಬಳಸುವುದೇ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ಶಾರ್ಟ್ಕಟ್ ಆನ್ ಆಗಿರುವಾಗ, ಎರಡೂ ವಾಲ್ಯೂಮ್ ಬಟನ್ಗಳನ್ನು 3 ಸೆಕೆಂಡುಗಳ ಕಾಲ ಒತ್ತಿದರೆ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯವೊಂದು ಪ್ರಾರಂಭವಾಗುತ್ತದೆ."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆನ್ ಮಾಡಬೇಕೇ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಕೆಲವು ಸೆಕೆಂಡುಗಳ ಕಾಲ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳು ಆನ್ ಆಗುತ್ತವೆ. ಇದು ನಿಮ್ಮ ಸಾಧನವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಬಹುದು.\n\n ಪ್ರಸ್ತುತ ವೈಶಿಷ್ಟ್ಯಗಳು:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿಯಲ್ಲಿ ಆಯ್ದ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ನೀವು ಬದಲಾಯಿಸಬಹುದು."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡುವುದೇ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಕೆಲವು ಸೆಕೆಂಡುಗಳ ಕಾಲ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯವಾದ <xliff:g id="SERVICE">%1$s</xliff:g> ಆನ್ ಆಗುತ್ತದೆ. ಇದು ನಿಮ್ಮ ಸಾಧನವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಬಹುದು.\n\nನೀವು ಈ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಅಕ್ಸೆಸಿಬಿಲಿಟಿಯಲ್ಲಿನ ಮತ್ತೊಂದು ವೈಶಿಷ್ಟ್ಯಕ್ಕೆ ಬದಲಾಯಿಸಬಹುದು."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ಆನ್ ಮಾಡಿ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ಆನ್ ಮಾಡಬೇಡಿ"</string>
@@ -1829,8 +1831,7 @@
<item quantity="one">%d ಗಂಟೆಗೆ</item>
<item quantity="other">%d ಗಂಟೆಗೆ</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ (ಮುಂದಿನ ಅಲಾರಮ್)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index e3406c1b..43c785a 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"권장 수준 이상으로 볼륨을 높이시겠습니까?\n\n높은 볼륨으로 장시간 청취하면 청력에 손상이 올 수 있습니다."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"접근성 단축키를 사용하시겠습니까?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"단축키가 사용 설정된 경우 볼륨 버튼 두 개를 동시에 3초간 누르면 접근성 기능이 시작됩니다."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"접근성 기능을 사용하시겠습니까?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"볼륨 키 2개를 몇 초 동안 길게 누르면 접근성 기능이 사용 설정됩니다. 이때 기기 작동 방식이 달라질 수 있습니다.\n\n현재 기능:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n설정 > 접근성에서 선택한 기능을 변경할 수 있습니다."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g>을(를) 사용하시겠습니까?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"볼륨 키 2개를 몇 초 동안 길게 누르면 <xliff:g id="SERVICE">%1$s</xliff:g> 접근성 기능이 사용 설정됩니다. 이렇게 되면 기기 작동 방식이 달라질 수 있습니다.\n\n설정 > 접근성에서 이 단축키를 다른 기능으로 변경할 수 있습니다."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"사용"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"사용 안 함"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 3268d16..182af1b 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Сунушталган деңгээлден да катуулатып уккуңуз келеби?\n\nМузыканы узакка чейин катуу уксаңыз, угууңуз начарлап кетиши мүмкүн."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ыкчам иштетесизби?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Атайын мүмкүнчүлүктөрдү иштетесизби?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Атайын мүмкүнчүлүктөр функциясын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nУчурдагы функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТандалган функцияларды өзгөртүү үчүн, Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> күйгүзүлсүнбү?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> кызматын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nБаскычтардын ушул айкалышын башка функцияга дайындоо үчүн, Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ооба"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Жок"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index d22ce0c..9f964eb 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ເພີ່ມລະດັບສຽງໃຫ້ເກີນກວ່າລະດັບທີ່ແນະນຳບໍ?\n\nການຮັບຟັງສຽງໃນລະດັບທີ່ສູງເປັນໄລຍະເວລາດົນອາດເຮັດໃຫ້ການຟັງຂອງທ່ານມີບັນຫາໄດ້."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ໃຊ້ປຸ່ມລັດການຊ່ວຍເຂົ້າເຖິງບໍ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ເມື່ອເປີດໃຊ້ທາງລັດແລ້ວ, ການກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ 3 ວິນາທີຈະເປັນການເລີ່ມຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ເປີດໃຊ້ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງບໍ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ສອງສາມວິນາທີເພື່ອເປີດໃຊ້ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ນີ້ອາດປ່ຽນວິທີການເຮັດວຽກຂອງອຸປະກອນທ່ານ.\n\nຄຸນສົມບັດປັດຈຸບັນ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nທ່ານສາມາດປ່ຽນຄຸນສົມບັດທີ່ເລືອກໄດ້ໃນການຕັ້ງຄ່າ > ການຊ່ວຍເຂົ້າເຖິງ."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ເປີດໃຊ້ <xliff:g id="SERVICE">%1$s</xliff:g> ບໍ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ສອງສາມວິນາທີເພື່ອເປີດໃຊ້ <xliff:g id="SERVICE">%1$s</xliff:g>, ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ນີ້ອາດປ່ຽນວິທີການເຮັດວຽກຂອງອຸປະກອນທ່ານ.\n\nທ່ານສາມາດປ່ຽນທາງລັດນີ້ເປັນຄຸນສົມບັດອື່ນໄດ້ໃນການຕັ້ງຄ່າ > ການຊ່ວຍເຂົ້າເຖິງ."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ເປີດໃຊ້"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ບໍ່ເປີດ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ff687c5..337b31c 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Padidinti garsą daugiau nei rekomenduojamas lygis?\n\nIlgai klausydami dideliu garsu galite pažeisti klausą."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Naudoti spartųjį pritaikymo neįgaliesiems klavišą?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kai spartusis klavišas įjungtas, paspaudus abu garsumo mygtukus ir palaikius 3 sekundes bus įjungta pritaikymo neįgaliesiems funkcija."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Įjungti pritaikymo neįgaliesiems funkcijas?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Paspaudus abu garsumo klavišus ir palaikius kelias sekundes įjungiamos pritaikymo neįgaliesiems funkcijos. Tai gali pakeisti įrenginio veikimą.\n\nDabartinės funkcijos:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPasirinktas funkcijas galite pakeisti skiltyje „Nustatymai“ > „Pritaikomumas“."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Įjungti „<xliff:g id="SERVICE">%1$s</xliff:g>“?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Paspaudus abu garsumo klavišus ir palaikius kelias sekundes įjungiama pritaikymo neįgaliesiems funkcija „<xliff:g id="SERVICE">%1$s</xliff:g>“. Tai gali pakeisti įrenginio veikimą.\n\nGalite pakeisti šį spartųjį klavišą į kitą funkciją skiltyje „Nustatymai“ > „Pritaikomumas“."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Įjungti"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Neįjungti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 2cb6058..2b92b44 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vai palielināt skaļumu virs ieteicamā līmeņa?\n\nIlgstoši klausoties skaņu lielā skaļumā, var tikt bojāta dzirde."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vai izmantot pieejamības saīsni?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kad īsinājumtaustiņš ir ieslēgts, nospiežot abas skaļuma pogas un 3 sekundes turot tās, tiks aktivizēta pieejamības funkcija."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vai ieslēgt pieejamības funkcijas?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Turot nospiestus abus skaļuma taustiņus dažas sekundes, tiek ieslēgtas pieejamības funkcijas. Tas var mainīt ierīces darbību.\n\nPašreizējās funkcijas:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAtlasītās funkcijas varat mainīt šeit: Iestatījumi > Pieejamība."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vai ieslēgt pakalpojumu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Turot nospiestus abus skaļuma taustiņus dažas sekundes, tiek ieslēgta pieejamības funkcija <xliff:g id="SERVICE">%1$s</xliff:g>. Tas var mainīt ierīces darbību.\n\nŠo saīsni uz citu funkciju varat mainīt šeit: Iestatījumi > Pieejamība."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ieslēgt"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Neieslēgt"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index bf7220c..ccd1ef7 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Да го зголемиме звукот над препорачаното ниво?\n\nСлушањето звуци со голема јачина подолги периоди може да ви го оштети сетилото за слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Да се користи кратенка за „Пристапност“?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Кога е вклучена кратенката, ако ги притиснете двете копчиња за јачина на звук во времетраење од 3 секунди, ќе се стартува функција за пристапност."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Да се вклучат функциите за пристапност?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ако ги задржите притиснати двете копчиња за јачина на звук неколку секунди, ќе се вклучат функциите за пристапност. Ова може да го промени начинот на функционирање на уредот.\n\nТековни функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nМоже да ги промените избраните функции во „Поставки > Пристапност“."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Да се вклучи <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ако ги задржите притиснати двете копчиња за јачина на звук неколку секунди, ќе се вклучи функцијата за пристапност <xliff:g id="SERVICE">%1$s</xliff:g>. Ова може да го промени начинот на функционирање на уредот.\n\nМоже да ја измените кратенкава да биде за друга функција во „Поставки > Пристапност“."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Вклучи"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не вклучувај"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 513ed1d..8ec1c68 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"മുകളിൽക്കൊടുത്തിരിക്കുന്ന ശുപാർശചെയ്ത ലെവലിലേക്ക് വോളിയം വർദ്ധിപ്പിക്കണോ?\n\nഉയർന്ന വോളിയത്തിൽ ദീർഘനേരം കേൾക്കുന്നത് നിങ്ങളുടെ ശ്രവണ ശേഷിയെ ദോഷകരമായി ബാധിക്കാം."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ഉപയോഗസഹായി കുറുക്കുവഴി ഉപയോഗിക്കണോ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"കുറുക്കുവഴി ഓണായിരിക്കുമ്പോൾ, രണ്ട് വോളിയം ബട്ടണുകളും 3 സെക്കൻഡ് നേരത്തേക്ക് അമർത്തുന്നത് ഉപയോഗസഹായി ഫീച്ചർ ആരംഭിക്കും."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ഉപയോഗസഹായി ഫീച്ചറുകൾ ഓണാക്കണോ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"രണ്ട് വോളിയം കീകളും അൽപ്പ നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുന്നത്, ഉപയോഗസഹായി ഫീച്ചറുകൾ ഓണാക്കുന്നു. നിങ്ങളുടെ ഉപകരണം പ്രവർത്തിക്കുന്ന രീതിയെ ഇത് മാറ്റിയേക്കാം.\n\nനിലവിലുള്ള ഫീച്ചറുകൾ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nതിരഞ്ഞെടുത്ത ഫീച്ചറുകൾ ക്രമീകരണം > ഉപയോഗസഹായി എന്നതിൽ മാറ്റാനാവും."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ഓണാക്കണോ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"രണ്ട് വോളിയം കീകളും അൽപ്പ നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുന്നത് ഉപയോഗസഹായി ഫീച്ചറായ <xliff:g id="SERVICE">%1$s</xliff:g> എന്നതിനെ ഓണാക്കുന്നു. നിങ്ങളുടെ ഉപകരണം പ്രവർത്തിക്കുന്ന വിധം ഇത് മാറ്റിയേക്കാം.\n\nക്രമീകരണം > ഉപയോഗസഹായി എന്നതിലെ മറ്റൊരു ഫീച്ചറിലേക്ക് നിങ്ങൾക്ക് ഈ കുറുക്കുവഴി മാറ്റാനാവും."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ഓണാക്കുക"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ഓണാക്കരുത്"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index b95b689..6b7a365 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дууг санал болгосноос чанга болгож өсгөх үү?\n\nУрт хугацаанд чанга хөгжим сонсох нь таны сонсголыг муутгаж болно."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Хүртээмжийн товчлолыг ашиглах уу?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Товчлол асаалттай үед дууны түвшний хоёр товчлуурыг хамтад нь 3 секунд дарснаар хандалтын онцлогийг эхлүүлнэ."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Хандалтын онцлогуудыг асаах уу?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Дууны түвшний түлхүүрийг хэдэн секундийн турш зэрэг дарснаар хандалтын онцлогууд асна. Энэ нь таны төхөөрөмжийн ажиллах зарчмыг өөрчилж болзошгүй.\n\nОдоогийн онцлогууд:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТа сонгосон онцлогуудыг Тохиргоо > Хандалт хэсэгт өөрчлөх боломжтой."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g>-г асаах уу?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Дууны түвшний түлхүүрийг хэдэн секундийн турш зэрэг дарах нь хандалтын онцлог болох <xliff:g id="SERVICE">%1$s</xliff:g>-г асаадаг. Энэ нь таны төхөөрөмжийн ажиллах зарчмыг өөрчилж болзошгүй.\n\nТа Тохиргоо > Хандалт хэсэгт энэ товчлолыг өөр онцлогт оноож өөрчлөх боломжтой."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Асаах"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Бүү асаа"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 40c7dbd..4f8925c 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"शिफारस केलेल्या पातळीच्या वर आवाज वाढवायचा?\n\nउच्च आवाजात दीर्घ काळ ऐकण्याने आपल्या श्रवणशक्तीची हानी होऊ शकते."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"प्रवेशयोग्यता शॉर्टकट वापरायचा?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"शॉर्टकट सुरू असताना, दोन्ही व्हॉल्यूम बटणे तीन सेकंदांसाठी दाबून ठेवल्याने अॅक्सेसिबिलिटी वैशिष्ट्य सुरू होईल."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"अॅक्सेसिबिलिटी वैशिष्ट्ये सुरू करायची आहेत का?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"दोन्ही व्हॉल्यूम की काही सेकंद धरून ठेवल्याने अॅक्सेसिबिलिटी वैशिष्ट्ये सुरू होतात. यामुळे तुमचे डिव्हाइस कसे काम करते हे पूर्णपणे बदलते.\n\nसध्याची वैशिष्ट्ये:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nतुम्ही हा शॉर्टकट सेटिंग्ज > अॅक्सेसिबिलिटी मध्ये बदलू शकता."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> सुरू करायची आहे का?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"दोन्ही व्हॉल्यूम की काही सेकंद धरून ठेवल्याने <xliff:g id="SERVICE">%1$s</xliff:g>, एक अॅक्सेसिबिलिटी वैशिष्ट्य सुरू होते. यामुळे तुमचे डिव्हाइस कसे काम करते हे पूर्णपणे बदलते.\n\nतुम्ही हा शॉर्टकट सेटिंग्ज > अॅक्सेसिबिलिटी मध्ये बदलू शकता."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"सुरू करा"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"सुरू करू नका"</string>
@@ -1829,8 +1831,7 @@
<item quantity="other">%d तासासाठी</item>
<item quantity="one">1 तासासाठी</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>पर्यंत"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत (पुढील अलार्म)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"तुम्ही बंद करेपर्यंत"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index fb24a06..161be37 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Naikkan kelantangan melebihi paras yang disyokorkan?\n\nMendengar pada kelantangan yang tinggi untuk tempoh yang lama boleh merosakkan pendengaran anda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gunakan Pintasan Kebolehaksesan?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Apabila pintasan dihidupkan, tindakan menekan kedua-dua butang kelantangan selama 3 saat akan memulakan ciri kebolehaksesan."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Hidupkan ciri kebolehaksesan?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Tindakan menahan kedua-dua kekunci kelantangan selama beberapa saat akan menghidupkan ciri kebolehaksesan. Hal ini mungkin mengubah cara peranti anda berfungsi.\n\nCiri semasa:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAnda boleh menukar ciri yang dipilih dalam Tetapan > Kebolehaksesan."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Hidupkan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Tindakan menahan kedua-dua kekunci kelantangan selama beberapa saat akan menghidupkan <xliff:g id="SERVICE">%1$s</xliff:g>, iaitu satu ciri kebolehaksesan. Ini mungkin mengubah cara peranti anda berfungsi.\n\nAnda boleh menukar pintasan ini kepada ciri lain dalam Tetapan > Kebolehaksesan."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Hidupkan"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Jangan hidupkan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 2c3fbcb..a21e4bc 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"အသံကို အကြံပြုထားသည့် ပမာဏထက် မြှင့်ပေးရမလား?\n\nအသံကို မြင့်သည့် အဆင့်မှာ ကြာရှည်စွာ နားထောင်ခြင်းက သင်၏ နားကို ထိခိုက်စေနိုင်သည်။"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်ကို အသုံးပြုလိုပါသလား။"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ဖြတ်လမ်းလင့်ခ်ကို ဖွင့်ထားစဉ် အသံထိန်းခလုတ် နှစ်ခုစလုံးကို ၃ စက္ကန့်ခန့် ဖိထားခြင်းဖြင့် အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုကို ဖွင့်နိုင်သည်။"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်မလား။"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"အသံခလုတ်နှစ်ခုလုံးကို စက္ကန့်အနည်းငယ် ဖိထားခြင်းက အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်ပေးသည်။ ဤလုပ်ဆောင်ချက်က သင့်စက်အလုပ်လုပ်ပုံကို ပြောင်းလဲနိုင်သည်။\n\nလက်ရှိ ဝန်ဆောင်မှုများ-\n<xliff:g id="SERVICE">%1$s</xliff:g>\n\'ဆက်တင်များ > အများသုံးစွဲနိုင်မှု\' တွင် ရွေးထားသည့် ဝန်ဆောင်မှုများကို ပြောင်းနိုင်သည်။"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ဖွင့်မလား။"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"အသံခလုတ်နှစ်ခုလုံးကို စက္ကန့်အနည်းငယ် ဖိထားခြင်းက အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုဖြစ်သော <xliff:g id="SERVICE">%1$s</xliff:g> ကို ဖွင့်ပေးသည်။ ဤလုပ်ဆောင်ချက်က သင့်စက်အလုပ်လုပ်ပုံကို ပြောင်းလဲနိုင်သည်။\n\nဤဖြတ်လမ်းလင့်ခ်ကို \'ဆက်တင်များ > အများသုံးစွဲနိုင်မှု\' တွင် နောက်ဝန်ဆောင်မှုတစ်ခုသို့ ပြောင်းနိုင်သည်။"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ဖွင့်ရန်"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"မဖွင့်ပါနှင့်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index fced207..d25c3ca 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vil du øke volumet til over anbefalt nivå?\n\nHvis du hører på et høyt volum over lengre perioder, kan det skade hørselen din."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vil du bruke tilgjengelighetssnarveien?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Når snarveien er på, starter en tilgjengelighetsfunksjon når du trykker inn begge volumknappene i tre sekunder."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vil du slå på tilgjengelighetsfunksjoner?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hvis du holder inne volumtastene i noen sekunder, slås tilgjengelighetsfunksjoner på. Dette kan endre hvordan enheten din fungerer.\n\nNåværende funksjoner:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kan endre valgte funksjoner i Innstillinger > Tilgjengelighet."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vil du slå på <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hvis du holder inne begge volumtastene i noen sekunder, slår du på <xliff:g id="SERVICE">%1$s</xliff:g>, en tilgjengelighetsfunksjon. Dette kan endre hvordan enheten din fungerer.\n\nDu kan endre denne snarveien til en annen funksjon i Innstillinger > Tilgjengelighet."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Slå på"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ikke slå på"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index fadabe8..2fc711f 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"सिफारिस तहभन्दा आवाज ठुलो गर्नुहुन्छ?\n\nलामो समय सम्म उच्च आवाजमा सुन्दा तपाईँको सुन्ने शक्तिलाई हानी गर्न सक्छ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"पहुँच सम्बन्धी सर्टकट प्रयोग गर्ने हो?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"यो सर्टकट सक्रिय हुँदा, ३ सेकेन्डसम्म दुवै भोल्युम बटन थिच्नुले पहुँचसम्बन्धी कुनै सुविधा सुरु गर्ने छ।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"पहुँचसम्बन्धी सुविधाहरू सक्रिय गर्ने हो?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुभयो भने पहुँचसम्बन्धी सुविधाहरू सक्रिय हुन्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nहालका सुविधाहरू:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nतपाईं सेटिङ > पहुँचमा गएर चयन गरिएका सुविधाहरू परिवर्तन गर्न सक्नुहुन्छ।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> सक्रिय गर्ने हो?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुले <xliff:g id="SERVICE">%1$s</xliff:g> नामक पहुँचसम्बन्धी सुविधा सक्रिय गर्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nतपाईं सेटिङ > पहुँचमा गई यो सर्टकटमार्फत अर्को सुविधा खुल्ने बनाउन सक्नुहुन्छ।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"सक्रिय गरियोस्"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"सक्रिय नगरियोस्"</string>
@@ -1829,8 +1831,7 @@
<item quantity="other">%d घन्टाका लागि</item>
<item quantity="one">१ घन्टाको लागि</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> सम्म"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> सम्म"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (अर्को अलार्म) सम्म"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"तपाईंले निष्क्रिय नपार्नुभएसम्म"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 337bf17..11a873a 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Snelkoppeling toegankelijkheid gebruiken?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Als de snelkoppeling is ingeschakeld, kun je drie seconden op beide volumeknoppen drukken om een toegankelijkheidsfunctie te starten."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Toegankelijkheidsfuncties inschakelen?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Als je beide volumetoetsen een paar seconden ingedrukt houdt, schakel je de toegankelijkheidsfuncties in. Hierdoor kan de manier veranderen waarop je apparaat werkt.\n\nHuidige functies:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nJe kunt de geselecteerde functies wijzigen via Instellingen > Toegankelijkheid."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> inschakelen?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Als je beide volumetoetsen een paar seconden ingedrukt houdt, wordt de toegankelijkheidsfunctie <xliff:g id="SERVICE">%1$s</xliff:g> ingeschakeld. Hierdoor kan de manier veranderen waarop je apparaat werkt.\n\nJe kunt deze sneltoets op een andere functie instellen via Instellingen > Toegankelijkheid."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Inschakelen"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Niet inschakelen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 85ca065..188a9bc 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ମାତ୍ରା ବଢ଼ାଇ ସୁପାରିଶ ସ୍ତର ବଢ଼ାଉଛନ୍ତି? \n\n ଲମ୍ବା ସମୟ ପର୍ଯ୍ୟନ୍ତ ଉଚ୍ଚ ଶବ୍ଦରେ ଶୁଣିଲେ ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତି ଖରାପ ହୋଇପାରେ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ଆକ୍ସେସବିଲିଟି ଶର୍ଟକଟ୍ ବ୍ୟବହାର କରିବେ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ସର୍ଟକଟ୍ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ଭଲ୍ୟୁମ୍ ବଟନ୍ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଦ୍ୱାରା ଏକ ଆକ୍ସେସବିଲିଟି ଫିଚର୍ ଆରମ୍ଭ ହେବ।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ଆକ୍ସେସିବିଲିଟୀ ଫିଚରଗୁଡ଼ିକୁ ଚାଲୁ କରିବେ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"କିଛି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍ କୀ’କୁ ଧରି ରଖିବା ଫଳରେ ଆକ୍ସେସିବିଲିଟୀ ଫିଚରଗୁଡ଼ିକ ଚାଲୁ ହୁଏ। ଏହା ଆପଣଙ୍କ ଡିଭାଇସ୍ କିପରି କାମ କରେ ତାହା ପରିବର୍ତ୍ତନ କରିପାରେ।\n\nବର୍ତ୍ତମାନର ଫିଚରଗୁଡ଼ିକ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n ଆପଣ ସେଟିଂସ୍ &gt ଆକ୍ସେସିବିଲିଟୀରେ ଚୟନିତ ଫିଚରଗୁଡ଼ିକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ଚାଲୁ କରିବେ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"କିଛି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍ କୀ’କୁ ଧରି ରଖିବା ଫଳରେ ଏକ ଆକ୍ସେସିବିଲିଟୀ ଫିଚର୍ <xliff:g id="SERVICE">%1$s</xliff:g> ଚାଲୁ ହୁଏ। ଏହା ଆପଣଙ୍କ ଡିଭାଇସ୍ କିପରି କାମ କରେ ତାହା ପରିବର୍ତ୍ତନ କରିପାରେ।\n\nଆପଣ ସେଟିଂସ୍ &gt ଆକ୍ସେସିବିଲିଟୀରେ ଏହି ସର୍ଚକଟକୁ ଅନ୍ୟ ଏକ ଫିଚରରେ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ଚାଲୁ କରନ୍ତୁ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ଚାଲୁ କରନ୍ତୁ ନାହିଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index ed663a8..22d5f441 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ਕੀ ਵੌਲਿਊਮ ਸਿਫ਼ਾਰਸ਼ ਕੀਤੇ ਪੱਧਰ ਤੋਂ ਵਧਾਉਣੀ ਹੈ?\n\nਲੰਮੇ ਸਮੇਂ ਤੱਕ ਉੱਚ ਵੌਲਿਊਮ ਤੇ ਸੁਣਨ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ਕੀ ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਵਰਤਣਾ ਹੈ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ਸ਼ਾਰਟਕੱਟ ਚਾਲੂ ਹੋਣ \'ਤੇ, ਕਿਸੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ ਬਟਨਾਂ ਨੂੰ 3 ਸਕਿੰਟ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ਕੀ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ਕੁਝ ਸਕਿੰਟਾਂ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਨੂੰ ਦਬਾਈ ਰੱਖਣਾ, ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕਰ ਦਿੰਦਾ ਹੈ। ਇਹ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਕੰਮ ਕਰਨ ਦੇ ਤਰੀਕੇ ਨੂੰ ਬਦਲ ਸਕਦਾ ਹੈ।\n\nਮੌਜੂਦਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nਸੈਟਿੰਗਾਂ ਅਤੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿੱਚ ਤੁਸੀਂ ਚੁਣੀਆਂ ਗਈਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਬਦਲ ਸਕਦੇ ਹੋ।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ਕੀ <xliff:g id="SERVICE">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ਕੁਝ ਸਕਿੰਟਾਂ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਨੂੰ ਦਬਾਈ ਰੱਖਣਾ <xliff:g id="SERVICE">%1$s</xliff:g>, ਇੱਕ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਚਾਲੂ ਕਰ ਦਿੰਦਾ ਹੈ। ਇਹ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਕੰਮ ਕਰਨ ਦੇ ਤਰੀਕੇ ਨੂੰ ਬਦਲ ਸਕਦਾ ਹੈ।\n\nਸੈਟਿੰਗਾਂ ਅਤੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿੱਚ ਤੁਸੀਂ ਇਸ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਕਿਸੇ ਹੋਰ ਵਿਸ਼ੇਸ਼ਤਾ ਵਿੱਚ ਬਦਲ ਸਕਦੇ ਹੋ।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ਚਾਲੂ ਕਰੋ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ਚਾਲੂ ਨਾ ਕਰੋ"</string>
@@ -1829,8 +1831,7 @@
<item quantity="one">%d ਘੰਟਿਆਂ ਲਈ</item>
<item quantity="other">%d ਘੰਟਿਆਂ ਲਈ</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ (ਅਗਲਾ ਅਲਾਰਮ)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ ਹੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 16b3faf..c9c3b46 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zwiększyć głośność ponad zalecany poziom?\n\nSłuchanie głośno przez długi czas może uszkodzić Twój słuch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Użyć skrótu do ułatwień dostępu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Gdy skrót jest włączony, jednoczesne naciskanie przez trzy sekundy obu przycisków głośności uruchamia funkcję ułatwień dostępu."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Włączyć ułatwienia dostępu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Przytrzymanie obu klawiszy głośności przez kilka sekund włącza ułatwienia dostępu. Może to zmienić sposób działania urządzenia.\n\nBieżące funkcje:\n<xliff:g id="SERVICE">%1$s</xliff:g>\naby zmienić wybrane funkcje, kliknij Ustawienia > Ułatwienia dostępu."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Włączyć usługę <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Przytrzymanie obu klawiszy głośności przez kilka sekund włącza usługę <xliff:g id="SERVICE">%1$s</xliff:g>, stanowiącą ułatwienie dostępu. Może to zmienić sposób działania urządzenia.\n\nAby zmienić ten skrót i wskazać inną funkcję, kliknij Ustawienia > Ułatwienia dostępu."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Włącz"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nie włączaj"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 2883d6f..221e29c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usar atalho de Acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho estiver ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Ativar os recursos de acessibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Manter as duas teclas de volume pressionadas por alguns segundos ativa os recursos de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nRecursos atuais:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nÉ possível mudar os recursos selecionados em \"Config. > Acessibilidade\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Ativar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Manter as duas teclas de volume pressionadas por alguns segundos ativa o serviço <xliff:g id="SERVICE">%1$s</xliff:g>, um recurso de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nÉ possível trocar o uso desse atalho para outro recurso em \"Config. > Acessibilidade\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ativar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Não ativar"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 3bc9530..e908c7f 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir com um volume elevado durante longos períodos poderá ser prejudicial para a sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Pretende utilizar o atalho de acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho está ativado, premir ambos os botões de volume durante 3 segundos inicia uma funcionalidade de acessibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Pretende ativar as funcionalidades de acessibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Manter premidas ambas as teclas de volume durante alguns segundos ativa as funcionalidades de acessibilidade. Estas podem alterar a forma como o seu dispositivo funciona.\n\nFuncionalidades atuais:\n<xliff:g id="SERVICE">%1$s</xliff:g>\npode alterar as funcionalidades selecionadas em Definições > Acessibilidade."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Pretende ativar o serviço <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Manter premidas ambas as teclas de volume durante alguns segundos ativa o serviço <xliff:g id="SERVICE">%1$s</xliff:g>, uma funcionalidade de acessibilidade. Esta pode alterar a forma como o seu dispositivo funciona.\n\nPode alterar este atalho para outra funcionalidade em Definições > Acessibilidade."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ativar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Não ativar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 2883d6f..221e29c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usar atalho de Acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho estiver ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Ativar os recursos de acessibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Manter as duas teclas de volume pressionadas por alguns segundos ativa os recursos de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nRecursos atuais:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nÉ possível mudar os recursos selecionados em \"Config. > Acessibilidade\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Ativar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Manter as duas teclas de volume pressionadas por alguns segundos ativa o serviço <xliff:g id="SERVICE">%1$s</xliff:g>, um recurso de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nÉ possível trocar o uso desse atalho para outro recurso em \"Config. > Acessibilidade\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ativar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Não ativar"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d5a9297..c2dea7e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ridicați volumul mai sus de nivelul recomandat?\n\nAscultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utilizați comanda rapidă pentru accesibilitate?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Atunci când comanda rapidă este activată, dacă apăsați ambele butoane de volum timp de trei secunde, veți lansa o funcție de accesibilitate."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Activați funcțiile de accesibilitate?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Dacă apăsați ambele taste de volum câteva secunde, activați funcțiile de accesibilitate. Acest lucru poate schimba funcționarea dispozitivului.\n\nFuncțiile actuale:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuteți schimba funcțiile selectate din Setări > Accesibilitate."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Activați <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Dacă apăsați ambele taste de volum câteva secunde, activați funcția de accesibilitate <xliff:g id="SERVICE">%1$s</xliff:g>. Acest lucru poate schimba funcționarea dispozitivului.\n\nPuteți alege altă funcție pentru această comandă în Setări > Accesibilitate."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activați"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nu activați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 3339fb5..f8d6dfb 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Установить громкость выше рекомендуемого уровня?\n\nВоздействие громкого звука в течение долгого времени может привести к повреждению слуха."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Использовать быстрое включение?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Чтобы использовать функцию специальных возможностей, когда она включена, нажмите и удерживайте обе кнопки регулировки громкости в течение трех секунд."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Включить специальные возможности?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Чтобы включить специальные возможности, нажмите обе кнопки регулировки громкости и удерживайте несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nТекущие функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nЧтобы изменить выбранные функции, перейдите в настройки и нажмите \"Специальные возможности\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Включить функцию \"<xliff:g id="SERVICE">%1$s</xliff:g>\"?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Чтобы включить функцию \"<xliff:g id="SERVICE">%1$s</xliff:g>\", нажмите обе кнопки регулировки громкости на несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nЧтобы назначить это сочетание клавиш другой функции, перейдите в настройки и выберите \"Специальные возможности\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Включить"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не включать"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index c26f23f..63615f7 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"නිර්දේශිතයි මට්ටමට වඩා ශබ්දය වැඩිද?\n\nදිගු කාලයක් සඳහා ඉහළ ශබ්දයක් ඇසීමෙන් ඇතැම් විට ඔබගේ ඇසීමට හානි විය හැක."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ප්රවේශ්යතා කෙටිමඟ භාවිතා කරන්නද?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"කෙටිමග ක්රියාත්මක විට, හඬ පරිමා බොත්තම් දෙකම තත්පර 3ක් තිස්සේ එබීමෙන් ප්රවේශ්යතා විශේෂාංගය ආරම්භ වනු ඇත."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ප්රවේශ්යතා විශේෂාංග ක්රියාත්මක කරන්නද?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"හඬ පරිමා යතුරු දෙකම තත්පර කීපයකට පහළට අල්ලාගෙන සිටීම ප්රවේශ්යතා විශේෂාංග ක්රියාත්මක කරයි. මෙය ඔබේ උපාංගය ක්රියා කරන ආකාරය වෙනස් කළ හැකිය.\n\nවත්මන් විශේෂාංග:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nඔබට තේරූ විශේෂාංග සැකසීම් > ප්රවේශ්යතාව හි වෙනස් කළ හැකිය."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ක්රියාත්මක කරන්නද?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"හඬ පරිමා යතුරු දෙකම තත්පර කීපයකට පහළට අල්ලාගෙන සිටීම ප්රවේශ්යතා විශේෂාංගයක් වන <xliff:g id="SERVICE">%1$s</xliff:g> ක්රියාත්මක කරයි. මෙය ඔබේ උපාංගය ක්රියා කරන ආකාරය වෙනස් කළ හැකිය.\n\nඔබට මෙම කෙටිමග සැකසීම් > ප්රවේශ්යතාව හි තවත් විශේෂාංගයකට වෙනස් කළ හැකිය."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ක්රියාත්මක කරන්න"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ක්රියාත්මක නොකරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 1a4f07e..4f0704b 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zvýšiť hlasitosť nad odporúčanú úroveň?\n\nDlhodobé počúvanie pri vysokej hlasitosti môže poškodiť váš sluch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Použiť skratku dostupnosti?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Keď je skratka zapnutá, stlačením obidvoch tlačidiel hlasitosti na tri sekundy spustíte funkciu dostupnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Chcete zapnúť funkcie dostupnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Pridržaním oboch tlačidiel hlasitosti na niekoľko sekúnd zapnete funkcie dostupnosti. Môže sa tým zmeniť spôsob fungovania vášho zariadenia.\n\nAktuálne funkcie:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nVybrané funkcie môžete zmeniť v časti Nastavenia > Dostupnosť."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Chcete zapnúť <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Pridržaním oboch klávesov hlasitosti na niekoľko sekúnd zapnete funkciu dostupnosti <xliff:g id="SERVICE">%1$s</xliff:g>. Môže sa tým zmeniť spôsob fungovania vášho zariadenia.\n\nTúto skratku môžete zmeniť na inú funkciu v časti Nastavenia > Dostupnosť."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Zapnúť"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nezapínať"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0079e38..85f1124 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ali želite povečati glasnost nad priporočeno raven?\n\nDolgotrajno poslušanje pri veliki glasnosti lahko poškoduje sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite uporabljati bližnjico funkcij za ljudi s posebnimi potrebami?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Ko je bližnjica vklopljena, pritisnite gumba za glasnost in ju pridržite tri sekunde, če želite zagnati funkcijo za ljudi s posebnimi potrebami."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Želite vklopiti funkcije za ljudi s posebnimi potrebami?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Če za nekaj sekund pridržite obe tipki za glasnost, boste vklopili funkcije za ljudi s posebnimi potrebami. To lahko spremeni način delovanja naprave.\n\nTrenutne funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nIzbrane funkcije lahko spremenite v meniju »Nastavitve« > »Funkcije za ljudi s posebnimi potrebami«."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Želite vklopiti storitev <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Če za nekaj sekund pridržite obe tipki za glasnost, boste vklopili storitev <xliff:g id="SERVICE">%1$s</xliff:g>, ki je funkcija za ljudi s posebnimi potrebami. To lahko spremeni način delovanja naprave.\n\nTo bližnjico lahko v meniju »Nastavitve« > »Funkcije za ljudi s posebnimi potrebami« spremenite, da bo uporabljena za drugo funkcijo."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Vklopi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne vklopi"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index b29582c..4fd25bd 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Të ngrihet volumi mbi nivelin e rekomanduar?\n\nDëgjimi me volum të lartë për periudha të gjata mund të dëmtojë dëgjimin."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Të përdoret shkurtorja e qasshmërisë?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kur shkurtorja është e aktivizuar, shtypja e të dy butonave për 3 sekonda do të nisë një funksion qasshmërie."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Të aktivizohen veçoritë e qasshmërisë?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Mbajtja shtypur e dy tasteve të volumit për pak sekonda aktivizon veçoritë e qasshmërisë. Kjo mund të ndryshojë mënyrën se si funksionon pajisja jote.\n\nVeçoritë aktuale:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nKe ndryshuar veçoritë e zgjedhura te Cilësimet > Qasshmëria."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Të aktivizohet <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Mbajtja shtypur e dy tasteve të volumit për pak sekonda aktivizon <xliff:g id="SERVICE">%1$s</xliff:g>, një veçori të qasshmërisë. Kjo mund të ndryshojë mënyrën se si funksionon pajisja jote.\n\nMund të ndryshosh këtë shkurtore te një veçori tjetër te Cilësimet > Qasshmëria."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivizo"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Mos e aktivizo"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f24e285..aa37ea0 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Желите да појачате звук изнад препорученог нивоа?\n\nСлушање гласне музике дуже време може да вам оштети слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Желите ли да користите пречицу за приступачност?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Када је пречица укључена, притисните оба дугмета за јачину звука да бисте покренули функцију приступачности."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Желите ли да укључите функције приступачности?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ако задржите оба тастера за јачину звука пар секунди, укључиће се функције приступачности. То може да промени начин рада уређаја.\n\nПостојеће функције:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nМожете да промените изабране функције у одељку Подешавања > Приступачност."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Желите ли да укључите услугу <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ако задржите оба тастера за јачину звука пар секунди, укључује се <xliff:g id="SERVICE">%1$s</xliff:g>, функција приступачности. То може да промени начин рада уређаја.\n\nМожете да промените функцију на коју се односи ова пречица у одељку Подешавања > Приступачност."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Укључи"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не укључуј"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 5c9ce2c..acea0d5 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vill du höja volymen över den rekommenderade nivån?\n\nAtt lyssna med stark volym långa stunder åt gången kan skada hörseln."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vill du använda Aktivera tillgänglighet snabbt?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"När kortkommandot har aktiverats startar du en tillgänglighetsfunktion genom att trycka ned båda volymknapparna i tre sekunder."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vill du aktivera tillgänglighetsfunktionerna?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Om du trycker ned båda volymknapparna i ett par sekunder aktiveras tillgänglighetsfunktionerna. Det kan få enheten ett fungera annorlunda.\n\nAktuella funktioner:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kan ändra vilka funktioner som aktiveras under Inställningar > Tillgänglighet."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vill du aktivera <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Om du trycker ned båda volymknapparna i ett par sekunder aktiveras <xliff:g id="SERVICE">%1$s</xliff:g>, en tillgänglighetsfunktion. Det kan leda till att enheten fungerar annorlunda.\n\nDu kan ändra vilken funktion som ska aktiveras med genvägen under Inställningar > Tillgänglighet."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivera"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktivera inte"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 55db3eb..aa0c520 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ungependa kupandisha sauti zaidi ya kiwango kinachopendekezwa?\n\nKusikiliza kwa sauti ya juu kwa muda mrefu kunaweza kuharibu uwezo wako wa kusikia."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ungependa kutumia njia ya mkato ya ufikivu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Unapowasha kipengele cha njia ya mkato, hatua ya kubonyeza vitufe vyote viwili vya sauti kwa sekunde tatu itafungua kipengele cha ufikivu."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Ungependa kuwasha vipengele vya ufikivu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hatua ya kushikilia chini vitufe vyote viwili vya sauti kwa sekunde chache huwasha vipengele vya ufikivu. Huenda hatua hii ikabadilisha jinsi kifaa chako kinavyofanya kazi.\n\nVipengele vya sasa:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nUnaweza kubadilisha vipengele ulivyochagua katika Mipangilio > Ufikivu."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Ungependa kuwasha <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hatua ya kushikilia chini vitufe vyote viwili vya sauti kwa sekunde chache huwasha <xliff:g id="SERVICE">%1$s</xliff:g>, kipengele cha ufikivu. Huenda hatua hii ikabadilisha jinsi kifaa chako kinavyofanya kazi.\n\nUnaweza kubadilisha njia hii ya mkato iwe kipengele kingine katika Mipangilio > Ufikivu."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Washa"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Usiwashe"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index be030d1..068c375 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"பரிந்துரைத்த அளவை விட ஒலியை அதிகரிக்கவா?\n\nநீண்ட நேரத்திற்கு அதிகளவில் ஒலி கேட்பது கேட்கும் திறனைப் பாதிக்கலாம்."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"அணுகல்தன்மை ஷார்ட்கட்டைப் பயன்படுத்தவா?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ஷார்ட்கட் இயக்கத்தில் இருக்கும்போது ஒலியளவு பட்டன்கள் இரண்டையும் 3 வினாடிகளுக்கு அழுத்தினால் அணுகல்தன்மை அம்சம் இயக்கப்படும்."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"அணுகல்தன்மை அம்சங்களை ஆன் செய்யவா?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"இரண்டு ஒலியளவு விசைகளையும் சில விநாடிகள் பிடித்திருந்தால் அணுகல்தன்மை அம்சங்கள் ஆன் செய்யப்படும். இதனால் உங்கள் சாதனம் வேலை செய்யும் முறை மாறக்கூடும்.\n\nதற்போதைய அம்சங்கள்:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nதேர்ந்தெடுத்த அம்சங்களை அமைப்புகள் > அணுகல்தன்மைக்குச் சென்று உங்களால் மாற்ற முடியும்."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ஐ ஆன் செய்யவா?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"இரண்டு ஒலியளவு விசைகளையும் சில விநாடிகள் பிடித்திருப்பதால் அணுகல்தன்மை அம்சமான <xliff:g id="SERVICE">%1$s</xliff:g> ஆன் ஆகும். இதனால் உங்கள் சாதனம் வேலை செய்யும் முறை மாறக்கூடும்.\n\nஅமைப்புகள் > அணுகல்தன்மைக்குச் சென்று இந்த ஷார்ட்கட்டை வேறு அம்சத்திற்கு மாற்ற முடியும்."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ஆன் செய்"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ஆன் செய்யாதே"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 0966d23..6016082 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"వాల్యూమ్ను సిఫార్సు చేయబడిన స్థాయి కంటే ఎక్కువగా పెంచాలా?\n\nసుదీర్ఘ వ్యవధుల పాటు అధిక వాల్యూమ్లో వినడం వలన మీ వినికిడి శక్తి దెబ్బ తినవచ్చు."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"యాక్సెస్ సామర్థ్యం షార్ట్కట్ను ఉపయోగించాలా?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"షార్ట్కట్ ఆన్ చేసి ఉన్నప్పుడు, రెండు వాల్యూమ్ బటన్లను 3 సెకన్ల పాటు నొక్కి ఉంచితే యాక్సెస్ సౌలభ్య ఫీచర్ ప్రారంభం అవుతుంది."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"యాక్సెసిబిలిటీ ఫీచర్లను ఆన్ చేయాలా?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"రెండు వాల్యూమ్ కీలను కొంత సేపు నొక్కి పట్టుకుంటే యాక్సెసిబిలిటీ ఫీచర్లు ఆన్ అవుతాయి. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nప్రస్తుత ఫీచర్లు:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nఎంపిక చేసిన ఫీచర్లను మీరు సెట్టింగ్లు>యాక్సెసిబిలిటీలో మార్చవచ్చు."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ఆన్ చేయాాలా?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"రెండు వాల్యూమ్ కీలను కొన్ని సెకన్ల పాటు నొక్కి పట్టుకోవడం ద్వారా యాక్సెసిబిలిటీ అయిన <xliff:g id="SERVICE">%1$s</xliff:g> ఆన్ అవుతుంది. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nసెట్టింగ్లు > యాక్సెసిబిలిటీలో, వేరొక ఫీచర్ను ప్రారంభించేలా ఈ షార్ట్ కట్ను మీరు మార్చవచ్చు."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ఆన్ చేయి"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ఆన్ చేయకండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 7ff76ec..2feea14 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"นี่เป็นการเพิ่มระดับเสียงเกินระดับที่แนะนำ\n\nการฟังเสียงดังเป็นเวลานานอาจทำให้การได้ยินของคุณบกพร่องได้"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ใช้ทางลัดการช่วยเหลือพิเศษไหม"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"เมื่อทางลัดเปิดอยู่ การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มนาน 3 วินาทีจะเริ่มฟีเจอร์การช่วยเหลือพิเศษ"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ต้องการเปิดฟีเจอร์การช่วยเหลือพิเศษใช่ไหม"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 2-3 วินาทีจะเปิดฟีเจอร์การช่วยเหลือพิเศษ การดำเนินการนี้อาจเปลี่ยนแปลงลักษณะการทำงานของอุปกรณ์\n\nฟีเจอร์ปัจจุบัน:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nคุณจะเปลี่ยนฟีเจอร์ที่เลือกไว้ได้ในการตั้งค่า > การช่วยเหลือพิเศษ"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ต้องการเปิด <xliff:g id="SERVICE">%1$s</xliff:g> ใช่ไหม"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 2-3 วินาทีจะเปิด <xliff:g id="SERVICE">%1$s</xliff:g> ซึ่งเป็นฟีเจอร์การช่วยเหลือพิเศษ การดำเนินการนี้อาจเปลี่ยนแปลงลักษณะการทำงานของอุปกรณ์\n\nคุณแก้ไขทางลัดนี้ให้เปิดฟีเจอร์อื่นได้ในการตั้งค่า > การช่วยเหลือพิเศษ"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"เปิด"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ไม่ต้องเปิด"</string>
@@ -2031,7 +2033,7 @@
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> และอีก <xliff:g id="COUNT_3">%d</xliff:g> ไฟล์</item>
<item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> และอีก <xliff:g id="COUNT_1">%d</xliff:g> ไฟล์</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ไม่มีบุคคลที่แนะนำให้แชร์ด้วย"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ไม่พบใครที่แนะนำให้แชร์ด้วย"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"รายชื่อแอป"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"แอปนี้ไม่ได้รับอนุญาตให้บันทึกเสียงแต่จะบันทึกเสียงผ่านอุปกรณ์ USB นี้ได้"</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"หน้าแรก"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 7d9c717..41ea057 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gagamitin ang Shortcut sa Accessibility?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"I-on ang mga feature ng accessibility?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Mao-on ang mga feature ng accessibility kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nMga kasalukuyang feature:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuwede mong baguhin ang mga napiling feature sa Mga Setting > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"I-on ang <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Mao-on ang feature ng accessibility na <xliff:g id="SERVICE">%1$s</xliff:g> kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nPuwede mong palitan ng ibang feature ang shortcut na ito sa Mga Setting > Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"I-on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Huwag i-on"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a658bf1..4513eb6 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ses seviyesi önerilen düzeyin üzerine yükseltilsin mi?\n\nUzun süre yüksek ses seviyesinde dinlemek işitme duyunuza zarar verebilir."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erişilebilirlik Kısayolu Kullanılsın mı?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kısayol açıkken ses düğmelerinin ikisini birden 3 saniyeliğine basılı tutmanız bir erişilebilirlik özelliğini başlatır."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Erişilebilirlik özellikleri etkinleştirilsin mi?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ses tuşlarının ikisini birden birkaç saniyeliğine basılı tutmak, erişilebilirlik özelliklerini açar. Bu, cihazınızın çalışma şeklini değiştirebilir.\n\nGeçerli özellikler:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nSeçilen özellikleri Ayarlar > Erişilebilirlik\'te değiştirebilirsiniz."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> etkinleştirilsin mi?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ses tuşlarının ikisini birden birkaç saniyeliğine basılı tutmak <xliff:g id="SERVICE">%1$s</xliff:g> erişilebilirlik özelliğini etkinleştirir. Bu, cihazınızın çalışma şeklini değiştirebilir.\n\nBu kısayolu, Ayarlar > Erişilebilirlik\'te başka bir özellikle değiştirebilirsiniz."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Etkinleştir"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Etkinleştirme"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 7e81784..0f5ce7e 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Збільшити гучність понад рекомендований рівень?\n\nЯкщо слухати надто гучну музику тривалий час, можна пошкодити слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Використовувати швидке ввімкнення?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Якщо цей засіб увімкнено, ви можете активувати спеціальні можливості, утримуючи обидві кнопки гучності протягом трьох секунд."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Увімкнути спеціальні можливості?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Якщо втримувати обидві клавіші гучності впродовж кількох секунд, вмикаються спеціальні можливості. Це впливає на роботу пристрою.\n\nПоточні функції:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nВибрані функції можна змінити в налаштуваннях у меню спеціальних можливостей."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Увімкнути <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Якщо втримувати обидві клавіші гучності впродовж кількох секунд, буде ввімкнено спеціальні можливості – <xliff:g id="SERVICE">%1$s</xliff:g>. Це може вплинути на роботу пристрою.\n\nДля цієї комбінації клавіш можна вибрати іншу функцію в меню \"Налаштування > Спеціальні можливості\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Увімкнути"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не вмикати"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index b9b9c58..7720480 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"والیوم کو تجویز کردہ سطح سے زیادہ کریں؟\n\nزیادہ وقت تک اونچی آواز میں سننے سے آپ کی سماعت کو نقصان پہنچ سکتا ہے۔"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ایکسیسبیلٹی شارٹ کٹ استعمال کریں؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"شارٹ کٹ آن ہونے پر، 3 سیکنڈ تک دونوں والیوم بٹنز کو دبانے سے ایک ایکسیسبیلٹی خصوصیت شروع ہو جائے گی۔"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ایکسیسبیلٹی خصوصیات آن کریں؟َ"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"دونوں والیوم کی کلیدوں کو کچھ سیکنڈز تک دبائیں رکھنے سے ایکسیسبیلٹی خصوصیات آن ہو جاتی ہیں۔ اس سے آپ کے آلے کے کام کرنے کا طریقہ تبدیل ہو سکتا ہے۔\n\nموجودہ خصوصیات:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nآپ ترتیبات اور ایکسیسبیلٹی میں منتخب کردہ خصوصیات کو تبدیل کر سکتے ہیں۔"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> آن کریں؟"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"والیوم کی دونوں کلیدوں کو کچھ سیکنڈز تک دبائے رکھنے سے <xliff:g id="SERVICE">%1$s</xliff:g> ایکسیسبیلٹی خصوصیت آن ہو جاتی ہے۔ اس سے آپ کے آلے کے کام کرنے کا طریقہ تبدیل ہو سکتا ہے۔\n\nآپ ترتیبات اور ایکسیسبیلٹی میں دیگر خصوصیت کے لیے اس شارٹ کٹ کو تبدیل کر سکتے ہیں۔"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"آن کریں"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"آن نہ کریں"</string>
@@ -1829,8 +1831,7 @@
<item quantity="other">%d گھنٹے کیلئے</item>
<item quantity="one">1 گھنٹہ کیلئے</item>
</plurals>
- <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
- <skip />
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک (اگلا الارم)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"یہاں تک کہ آپ آف کر دیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 5edd9dc..49caf1b 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Tovush balandligi tavsiya etilgan darajadan ham yuqori qilinsinmi?\n\nUzoq vaqt davomida baland ovozda tinglash eshitish qobiliyatingizga salbiy ta’sir ko‘rsatishi mumkin."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Tezkor ishga tushirishdan foydalanilsinmi?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Maxsus imkoniyatlar funksiyasidan foydalanish uchun u yoniqligida ikkala tovush tugmasini 3 soniya bosib turing."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Maxsus imkoniyatlar yoqilsinmi?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Maxsus imkoniyatlarni yoqish uchun ikkala tovush tugmalarini bir necha soniya bosib turing. Qurilmangiz ishlashida oʻzgarish yuz berishi mumkin.\n\nJoriy funksiyalar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nTanlangan funksiyalarni Sozlamalar ichidagi Maxsus imkoniyatlar ustiga bosib oʻzgartirishingiz mumkin."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> yoqilsinmi?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> funksiyasini yoqish uchun ikkala tovush tugmalarini bir necha soniya bosib turing. Qurilmangiz ishlashida oʻzgarish yuz berishi mumkin.\n\nBu tezkor tugmalarni boshqa funksiyaga Sozlamalar ichidagi Maxsus imkoniyatlar orqali tayinlash mumkin."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Yoqilsin"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Yoqilmasin"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e3c7658..3b65cc1 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Bạn tăng âm lượng lên quá mức khuyên dùng?\n\nViệc nghe ở mức âm lượng cao trong thời gian dài có thể gây tổn thương thính giác của bạn."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Sử dụng phím tắt Hỗ trợ tiếp cận?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Khi phím tắt này đang bật, thao tác nhấn cả hai nút âm lượng trong 3 giây sẽ mở tính năng hỗ trợ tiếp cận."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Bật các tính năng hỗ trợ tiếp cận?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Thao tác nhấn và giữ cả hai phím âm lượng trong vài giây sẽ bật các tính năng hỗ trợ tiếp cận. Việc bật các tính năng này có thể thay đổi cách thiết bị của bạn hoạt động.\n\nCác tính năng hiện tại:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nBạn có thể thay đổi những tính năng đã chọn trong phần Cài đặt > Hỗ trợ tiếp cận."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Bật <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Thao tác nhấn và giữ cả hai phím âm lượng trong vài giây sẽ bật <xliff:g id="SERVICE">%1$s</xliff:g>, một tính năng hỗ trợ tiếp cận. Việc bật tính năng này có thể thay đổi cách thiết bị của bạn hoạt động.\n\nBạn có thể chuyển phím tắt này thành một tính năng khác trong phần Cài đặt > Hỗ trợ tiếp cận."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Bật"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Không bật"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index dee9d95..10472cb 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要将音量调高到建议的音量以上吗?\n\n长时间保持高音量可能会损伤听力。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用无障碍快捷方式吗?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"启用这项快捷方式后,同时按下两个音量按钮 3 秒钟即可启动无障碍功能。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"要开启无障碍功能吗?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"同时按住两个音量键几秒钟,即可开启无障碍功能。这样做可能会改变您设备的工作方式。\n\n当前功能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n您可以在“设置”>“无障碍”中更改所选功能。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"要开启<xliff:g id="SERVICE">%1$s</xliff:g>吗?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"同时按住两个音量键几秒钟,即可开启<xliff:g id="SERVICE">%1$s</xliff:g>无障碍功能。这样做可能会改变您设备的工作方式。\n\n您可以在“设置”>“无障碍”中将此快捷方式更改为开启另一项功能。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"开启"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"不开启"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 44ad863..455ae54 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要調高音量 (比建議的音量更大聲) 嗎?\n\n長時間聆聽高分貝音量可能會導致您的聽力受損。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用無障礙功能快速鍵嗎?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"啟用快速鍵後,同時按住音量按鈕 3 秒便可啟用無障礙功能。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"要開啟無障礙功能嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"同時按下兩個音量鍵幾秒,以開啟無障礙功能。這可能會變更裝置的運作。\n\n目前功能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n您可在「設定」>「無障礙功能」中變更所選功能。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"要啟用 <xliff:g id="SERVICE">%1$s</xliff:g> 嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"同時按下兩個音量鍵幾秒,以開啟 <xliff:g id="SERVICE">%1$s</xliff:g> 無障礙功能。這可能會變更裝置的運作。\n\n您可在「設定」>「無障礙功能」中變更此快速鍵。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"開啟"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"不要開啟"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8831bf0..5971d97 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要調高音量,比建議的音量更大聲嗎?\n\n長時間聆聽高分貝音量可能會使你的聽力受損。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用無障礙捷徑嗎?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"啟用捷徑功能,只要同時按下兩個音量按鈕 3 秒,就能啟動無障礙功能。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"要開啟無障礙功能嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"同時按住音量調高鍵和調低鍵數秒,即可開啟無障礙功能。這麼做可能會改變裝置的運作方式。\n\n目前的功能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n你可以在 [設定] > [無障礙設定] 中變更選取的功能。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"要開啟「<xliff:g id="SERVICE">%1$s</xliff:g>」嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"同時按住音量調高鍵和調低鍵數秒,即可開啟「<xliff:g id="SERVICE">%1$s</xliff:g>」無障礙功能。這麼做可能會改變裝置的運作方式。\n\n你可以在 [設定] > [無障礙設定] 中變更這個快速鍵觸發的功能。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"開啟"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"不要開啟"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 5fb9fa6..4a1033c 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Khuphukisa ivolumu ngaphezu kweleveli enconyiwe?\n\nUkulalela ngevolumu ephezulu izikhathi ezide kungahle kulimaze ukuzwa kwakho."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Sebenzisa isinqamuleli sokufinyelela?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Uma isinqamuleli sivuliwe, ukucindezela zombili izinkinobho zevolumu amasekhondi angu-3 kuzoqalisa isici sokufinyelela."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Uvula izici zokufinyelela?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ukubambela phansi bobabili okhiye bevolumu amasekhondi ambalwa kuvula izici zokufinyelela. Lokhu kungashintsha indlela idivayisi yakho esebenza ngayo.\n\nIzici zamanje:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nUngashintsha izici ezikhethiwe Kuzilungiselelo > Ukufinyeleleka."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vula i-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ukubambela phansi bobabili okhiye bevolumu amasekhondi ambalwa kuvula i-<xliff:g id="SERVICE">%1$s</xliff:g>, eyisici sokufinyelela Lokhu kungashintsha indlela idivayisi yakho esebenza ngayo.\n\nUngashintshela lesi sinqamuleli kwesinye isici Kuzilungiselelo > Ukufinyeleleka."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Vula"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ungavuli"</string>
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index c951091..7fa1613 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -21,7 +21,6 @@
import android.app.NotificationHistory.HistoricalNotification;
import android.graphics.drawable.Icon;
import android.os.Parcel;
-import android.util.Slog;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -31,6 +30,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class NotificationHistoryTest {
@@ -297,7 +297,7 @@
for (int i = 1; i <= 10; i++) {
HistoricalNotification n = getHistoricalNotification("pkg", i);
- if (i != 2) {
+ if (i != 2 && i != 4) {
postRemoveExpectedStrings.add(n.getPackage());
postRemoveExpectedStrings.add(n.getChannelName());
postRemoveExpectedStrings.add(n.getChannelId());
@@ -318,10 +318,10 @@
// 1 package name and 20 unique channel names and ids and 5 conversation ids
assertThat(history.getPooledStringsToWrite().length).isEqualTo(26);
- history.removeConversationFromWrite("pkg", "convo2");
+ history.removeConversationsFromWrite("pkg", Set.of("convo2", "convo4"));
- // 1 package names and 9 * 2 unique channel names and ids and 4 conversation ids
- assertThat(history.getPooledStringsToWrite().length).isEqualTo(23);
+ // 1 package names and 8 * 2 unique channel names and ids and 3 conversation ids
+ assertThat(history.getPooledStringsToWrite().length).isEqualTo(20);
assertThat(history.getNotificationsToWrite())
.containsExactlyElementsIn(postRemoveExpectedEntries);
}
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 0504cdc..bda84dd 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -283,10 +283,30 @@
<font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
</family>
<family lang="und-Ethi">
- <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
- <font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.otf</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-Bold.otf</font>
+ <font weight="400" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="700" />
+ </font>
</family>
<family lang="und-Hebr">
<font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java
new file mode 100644
index 0000000..d524316
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.allOf;
+import static com.google.errorprone.matchers.Matchers.enclosingClass;
+import static com.google.errorprone.matchers.Matchers.enclosingMethod;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.methodIsNamed;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * Parcelable data can be transported in many ways (some of which can be very
+ * inefficient) so this checker guides developers towards using high-performance
+ * best-practices.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkParcelablePerformance",
+ summary = "Verifies Parcelable performance best-practices",
+ severity = WARNING)
+public final class ParcelablePerformanceChecker extends BugChecker
+ implements MethodInvocationTreeMatcher {
+ private static final Matcher<Tree> INSIDE_WRITE_TO_PARCEL = allOf(
+ enclosingClass(isSubtypeOf("android.os.Parcelable")),
+ enclosingMethod(methodIsNamed("writeToParcel")));
+
+ private static final Matcher<ExpressionTree> WRITE_STRING = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeString"));
+ private static final Matcher<ExpressionTree> WRITE_STRING_ARRAY = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeStringArray"));
+
+ private static final Matcher<ExpressionTree> WRITE_VALUE = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeValue"));
+ private static final Matcher<ExpressionTree> WRITE_PARCELABLE = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeParcelable"));
+
+ private static final Matcher<ExpressionTree> WRITE_LIST = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeList"));
+ private static final Matcher<ExpressionTree> WRITE_PARCELABLE_LIST = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeParcelableList"));
+ private static final Matcher<ExpressionTree> WRITE_PARCELABLE_ARRAY = methodInvocation(
+ instanceMethod().onExactClass("android.os.Parcel").named("writeParcelableArray"));
+
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ if (INSIDE_WRITE_TO_PARCEL.matches(tree, state)) {
+ if (WRITE_STRING.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'writeString8()' to improve "
+ + "efficiency; sending as UTF-8 can double throughput")
+ .build();
+ }
+ if (WRITE_STRING_ARRAY.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'writeString8Array()' to improve "
+ + "efficiency; sending as UTF-8 can double throughput")
+ .build();
+ }
+
+ if (WRITE_VALUE.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use strongly-typed methods to improve "
+ + "efficiency; saves 4 bytes for type and overhead of "
+ + "Parcelable class name")
+ .build();
+ }
+ if (WRITE_PARCELABLE.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'item.writeToParcel()' to improve "
+ + "efficiency; saves overhead of Parcelable class name")
+ .build();
+ }
+
+ if (WRITE_LIST.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'writeTypedList()' to improve "
+ + "efficiency; saves overhead of repeated Parcelable class name")
+ .build();
+ }
+ if (WRITE_PARCELABLE_LIST.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'writeTypedList()' to improve "
+ + "efficiency; saves overhead of repeated Parcelable class name")
+ .build();
+ }
+ if (WRITE_PARCELABLE_ARRAY.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Recommended to use 'writeTypedArray()' to improve "
+ + "efficiency; saves overhead of repeated Parcelable class name")
+ .build();
+ }
+ }
+ return Description.NO_MATCH;
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java
new file mode 100644
index 0000000..75c76e3
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ParcelablePerformanceCheckerTest {
+ private CompilationTestHelper compilationHelper;
+
+ @Before
+ public void setUp() {
+ compilationHelper = CompilationTestHelper.newInstance(
+ ParcelablePerformanceChecker.class, getClass());
+ }
+
+ @Test
+ public void testString() {
+ compilationHelper
+ .addSourceFile("/android/os/Parcel.java")
+ .addSourceFile("/android/os/Parcelable.java")
+ .addSourceLines("FooInfo.java",
+ "import android.os.Parcel;",
+ "import android.os.Parcelable;",
+ "public class FooInfo implements Parcelable {",
+ " public void writeToParcel(Parcel dest, int flags) {",
+ " // BUG: Diagnostic contains:",
+ " dest.writeString(toString());",
+ " dest.writeString8(toString());",
+ " // BUG: Diagnostic contains:",
+ " dest.writeStringArray(new String[0]);",
+ " dest.writeString8Array(new String[0]);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testSingle() {
+ compilationHelper
+ .addSourceFile("/android/os/Parcel.java")
+ .addSourceFile("/android/os/Parcelable.java")
+ .addSourceLines("FooInfo.java",
+ "import android.os.Parcel;",
+ "import android.os.Parcelable;",
+ "public class FooInfo implements Parcelable {",
+ " public void writeToParcel(Parcel dest, int flags) {",
+ " // BUG: Diagnostic contains:",
+ " dest.writeValue(this);",
+ " this.writeToParcel(dest, flags);",
+ " // BUG: Diagnostic contains:",
+ " dest.writeParcelable(this, flags);",
+ " this.writeToParcel(dest, flags);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testList() {
+ compilationHelper
+ .addSourceFile("/android/os/Parcel.java")
+ .addSourceFile("/android/os/Parcelable.java")
+ .addSourceLines("FooInfo.java",
+ "import android.os.Parcel;",
+ "import android.os.Parcelable;",
+ "import java.util.List;",
+ "import java.util.ArrayList;",
+ "public class FooInfo implements Parcelable {",
+ " public void writeToParcel(Parcel dest, int flags) {",
+ " List<Parcelable> list = new ArrayList<Parcelable>();",
+ " Parcelable[] array = new Parcelable[0];",
+ " // BUG: Diagnostic contains:",
+ " dest.writeList(list);",
+ " dest.writeTypedList(list, flags);",
+ " // BUG: Diagnostic contains:",
+ " dest.writeParcelableList(list, flags);",
+ " dest.writeTypedList(list, flags);",
+ " // BUG: Diagnostic contains:",
+ " dest.writeParcelableArray(array, flags);",
+ " dest.writeTypedArray(array, flags);",
+ " }",
+ "}")
+ .doTest();
+ }
+}
diff --git a/errorprone/tests/res/android/os/Parcel.java b/errorprone/tests/res/android/os/Parcel.java
new file mode 100644
index 0000000..bafa236
--- /dev/null
+++ b/errorprone/tests/res/android/os/Parcel.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.util.List;
+
+public class Parcel {
+ public void writeString(String val) {
+ throw new UnsupportedOperationException();
+ }
+ public void writeString8(String val) {
+ throw new UnsupportedOperationException();
+ }
+ public final void writeStringArray(String[] val) {
+ throw new UnsupportedOperationException();
+ }
+ public final void writeString8Array(String[] val) {
+ throw new UnsupportedOperationException();
+ }
+
+ public final void writeValue(Object v) {
+ throw new UnsupportedOperationException();
+ }
+ public final void writeParcelable(Parcelable p, int flags) {
+ throw new UnsupportedOperationException();
+ }
+
+ public final void writeList(List val) {
+ throw new UnsupportedOperationException();
+ }
+ public final <T extends Parcelable> void writeParcelableList(List<T> val, int flags) {
+ throw new UnsupportedOperationException();
+ }
+ public <T extends Parcelable> void writeTypedList(List<T> val, int flags) {
+ throw new UnsupportedOperationException();
+ }
+ public final <T extends Parcelable> void writeParcelableArray(T[] value, int flags) {
+ throw new UnsupportedOperationException();
+ }
+ public final <T extends Parcelable> void writeTypedArray(T[] val, int flags) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/errorprone/tests/res/android/os/Parcelable.java b/errorprone/tests/res/android/os/Parcelable.java
new file mode 100644
index 0000000..217690d
--- /dev/null
+++ b/errorprone/tests/res/android/os/Parcelable.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+public interface Parcelable {
+ public void writeToParcel(Parcel dest, int flags);
+}
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 9047b712..b275331 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -22,18 +22,18 @@
import android.view.SurfaceControl;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FullscreenTaskOrg";
- private final TransactionPool mTransactionPool;
+ private final SyncTransactionQueue mSyncQueue;
private final ArraySet<Integer> mTasks = new ArraySet<>();
- FullscreenTaskListener(TransactionPool transactionPool) {
- mTransactionPool = transactionPool;
+ FullscreenTaskListener(SyncTransactionQueue syncQueue) {
+ mSyncQueue = syncQueue;
}
@Override
@@ -42,18 +42,18 @@
if (mTasks.contains(taskInfo.taskId)) {
throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
}
- mTasks.add(taskInfo.taskId);
- final SurfaceControl.Transaction t = mTransactionPool.acquire();
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
taskInfo.taskId);
- // Reset several properties back to fullscreen (PiP, for example, leaves all these
- // properties in a bad state).
- t.setPosition(leash, 0, 0);
- t.setWindowCrop(leash, null);
- t.setAlpha(leash, 1f);
- t.setMatrix(leash, 1, 0, 0, 1);
- t.show(leash);
- t.apply();
+ mTasks.add(taskInfo.taskId);
+ mSyncQueue.runInSync(t -> {
+ // Reset several properties back to fullscreen (PiP, for example, leaves all these
+ // properties in a bad state).
+ t.setPosition(leash, 0, 0);
+ t.setWindowCrop(leash, null);
+ t.setAlpha(leash, 1f);
+ t.setMatrix(leash, 1, 0, 0, 1);
+ t.show(leash);
+ });
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 2d82fb1..d650a95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -28,7 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
@@ -59,16 +59,16 @@
// require us to report to both old and new listeners)
private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>();
- public ShellTaskOrganizer(TransactionPool transactionPool) {
+ public ShellTaskOrganizer(SyncTransactionQueue syncQueue) {
super();
- addListener(new FullscreenTaskListener(transactionPool), WINDOWING_MODE_FULLSCREEN);
+ addListener(new FullscreenTaskListener(syncQueue), WINDOWING_MODE_FULLSCREEN);
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController,
- TransactionPool transactionPool) {
+ SyncTransactionQueue syncQueue) {
super(taskOrganizerController);
- addListener(new FullscreenTaskListener(transactionPool), WINDOWING_MODE_FULLSCREEN);
+ addListener(new FullscreenTaskListener(syncQueue), WINDOWING_MODE_FULLSCREEN);
}
/**
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 c8be937..ce49dd9 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
@@ -40,6 +40,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
@@ -99,7 +100,7 @@
public SplitScreenController(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController imeController, Handler handler, TransactionPool transactionPool,
- ShellTaskOrganizer shellTaskOrganizer) {
+ ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue) {
mContext = context;
mDisplayController = displayController;
mSystemWindows = systemWindows;
@@ -107,8 +108,7 @@
mHandler = handler;
mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
mTransactionPool = transactionPool;
- mWindowManagerProxy = new WindowManagerProxy(mTransactionPool, mHandler,
- shellTaskOrganizer);
+ mWindowManagerProxy = new WindowManagerProxy(syncQueue, shellTaskOrganizer);
mTaskOrganizer = shellTaskOrganizer;
mSplits = new SplitScreenTaskOrganizer(this, shellTaskOrganizer);
mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
index 25827cd..47e7c99 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
@@ -28,7 +28,6 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.graphics.Rect;
-import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
@@ -41,7 +40,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
import java.util.ArrayList;
import java.util.List;
@@ -85,9 +83,8 @@
private final TaskOrganizer mTaskOrganizer;
- WindowManagerProxy(TransactionPool transactionPool, Handler handler,
- TaskOrganizer taskOrganizer) {
- mSyncTransactionQueue = new SyncTransactionQueue(transactionPool, handler);
+ WindowManagerProxy(SyncTransactionQueue syncQueue, TaskOrganizer taskOrganizer) {
+ mSyncTransactionQueue = syncQueue;
mTaskOrganizer = taskOrganizer;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 7b499d4..823e0b7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -33,7 +33,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.SyncTransactionQueue;
import org.junit.Before;
import org.junit.Test;
@@ -54,7 +54,7 @@
private ITaskOrganizerController mTaskOrganizerController;
ShellTaskOrganizer mOrganizer;
- private final TransactionPool mTransactionPool = mock(TransactionPool.class);
+ private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
private class TrackingTaskListener implements ShellTaskOrganizer.TaskListener {
final ArrayList<RunningTaskInfo> appeared = new ArrayList<>();
@@ -85,7 +85,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController, mTransactionPool);
+ mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue);
}
@Test
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 6f05cbd..71c8e1f 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -30,23 +30,62 @@
namespace android {
-CursorWindow::CursorWindow(const String8& name, int ashmemFd,
- void* data, size_t size, bool readOnly) :
- mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) {
+/**
+ * By default windows are lightweight inline allocations of this size;
+ * they're only inflated to ashmem regions when more space is needed.
+ */
+static constexpr const size_t kInlineSize = 16384;
+
+CursorWindow::CursorWindow(const String8& name, int ashmemFd, void* data, size_t size,
+ size_t inflatedSize, bool readOnly) :
+ mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size),
+ mInflatedSize(inflatedSize), mReadOnly(readOnly) {
mHeader = static_cast<Header*>(mData);
}
CursorWindow::~CursorWindow() {
- ::munmap(mData, mSize);
- ::close(mAshmemFd);
+ if (mAshmemFd != -1) {
+ ::munmap(mData, mSize);
+ ::close(mAshmemFd);
+ } else {
+ free(mData);
+ }
}
-status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) {
+status_t CursorWindow::create(const String8& name, size_t inflatedSize,
+ CursorWindow** outCursorWindow) {
+ *outCursorWindow = nullptr;
+
+ size_t size = std::min(kInlineSize, inflatedSize);
+ void* data = calloc(size, 1);
+ if (!data) return NO_MEMORY;
+
+ CursorWindow* window = new CursorWindow(name, -1, data, size,
+ inflatedSize, false /*readOnly*/);
+ status_t result = window->clear();
+ if (!result) {
+ LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
+ "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
+ window->mHeader->freeOffset,
+ window->mHeader->numRows,
+ window->mHeader->numColumns,
+ window->mSize, window->mData);
+ *outCursorWindow = window;
+ return OK;
+ }
+ delete window;
+ return result;
+}
+
+status_t CursorWindow::inflate() {
+ // Shortcut when we can't expand any further
+ if (mSize == mInflatedSize) return INVALID_OPERATION;
+
String8 ashmemName("CursorWindow: ");
- ashmemName.append(name);
+ ashmemName.append(mName);
status_t result;
- int ashmemFd = ashmem_create_region(ashmemName.string(), size);
+ int ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize);
if (ashmemFd < 0) {
result = -errno;
ALOGE("CursorWindow: ashmem_create_region() failed: errno=%d.", errno);
@@ -55,7 +94,8 @@
if (result < 0) {
ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d",errno);
} else {
- void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
+ void* data = ::mmap(NULL, mInflatedSize, PROT_READ | PROT_WRITE,
+ MAP_SHARED, ashmemFd, 0);
if (data == MAP_FAILED) {
result = -errno;
ALOGE("CursorWindow: mmap() failed: errno=%d.", errno);
@@ -64,33 +104,49 @@
if (result < 0) {
ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d.", errno);
} else {
- CursorWindow* window = new CursorWindow(name, ashmemFd,
- data, size, false /*readOnly*/);
- result = window->clear();
- if (!result) {
- LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
- "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
- window->mHeader->freeOffset,
- window->mHeader->numRows,
- window->mHeader->numColumns,
- window->mSize, window->mData);
- *outCursorWindow = window;
- return OK;
- }
- delete window;
+ // Move inline contents into new ashmem region
+ memcpy(data, mData, mSize);
+ free(mData);
+ mAshmemFd = ashmemFd;
+ mData = data;
+ mHeader = static_cast<Header*>(mData);
+ mSize = mInflatedSize;
+ LOG_WINDOW("Inflated CursorWindow: freeOffset=%d, "
+ "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
+ mHeader->freeOffset,
+ mHeader->numRows,
+ mHeader->numColumns,
+ mSize, mData);
+ return OK;
}
}
- ::munmap(data, size);
+ ::munmap(data, mInflatedSize);
}
::close(ashmemFd);
}
- *outCursorWindow = NULL;
return result;
}
status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) {
- String8 name = parcel->readString8();
+ *outCursorWindow = nullptr;
+ String8 name;
+ status_t result = parcel->readString8(&name);
+ if (result) return result;
+
+ bool isAshmem;
+ result = parcel->readBool(&isAshmem);
+ if (result) return result;
+
+ if (isAshmem) {
+ return createFromParcelAshmem(parcel, name, outCursorWindow);
+ } else {
+ return createFromParcelInline(parcel, name, outCursorWindow);
+ }
+}
+
+status_t CursorWindow::createFromParcelAshmem(Parcel* parcel, String8& name,
+ CursorWindow** outCursorWindow) {
status_t result;
int actualSize;
int ashmemFd = parcel->readFileDescriptor();
@@ -122,8 +178,8 @@
actualSize, (int) size, errno);
} else {
CursorWindow* window = new CursorWindow(name, dupAshmemFd,
- data, size, true /*readOnly*/);
- LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, "
+ data, size, size, true /*readOnly*/);
+ LOG_WINDOW("Created CursorWindow from ashmem parcel: freeOffset=%d, "
"numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
window->mHeader->freeOffset,
window->mHeader->numRows,
@@ -140,12 +196,62 @@
return result;
}
+status_t CursorWindow::createFromParcelInline(Parcel* parcel, String8& name,
+ CursorWindow** outCursorWindow) {
+ uint32_t sentSize;
+ status_t result = parcel->readUint32(&sentSize);
+ if (result) return result;
+ if (sentSize > kInlineSize) return NO_MEMORY;
+
+ void* data = calloc(sentSize, 1);
+ if (!data) return NO_MEMORY;
+
+ result = parcel->read(data, sentSize);
+ if (result) return result;
+
+ CursorWindow* window = new CursorWindow(name, -1, data, sentSize,
+ sentSize, true /*readOnly*/);
+ LOG_WINDOW("Created CursorWindow from inline parcel: freeOffset=%d, "
+ "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
+ window->mHeader->freeOffset,
+ window->mHeader->numRows,
+ window->mHeader->numColumns,
+ window->mSize, window->mData);
+ *outCursorWindow = window;
+ return OK;
+}
+
status_t CursorWindow::writeToParcel(Parcel* parcel) {
- status_t status = parcel->writeString8(mName);
- if (!status) {
- status = parcel->writeDupFileDescriptor(mAshmemFd);
+ LOG_WINDOW("Writing CursorWindow: freeOffset=%d, "
+ "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
+ mHeader->freeOffset,
+ mHeader->numRows,
+ mHeader->numColumns,
+ mSize, mData);
+
+ status_t result = parcel->writeString8(mName);
+ if (result) return result;
+
+ if (mAshmemFd != -1) {
+ result = parcel->writeBool(true);
+ if (result) return result;
+ return writeToParcelAshmem(parcel);
+ } else {
+ result = parcel->writeBool(false);
+ if (result) return result;
+ return writeToParcelInline(parcel);
}
- return status;
+}
+
+status_t CursorWindow::writeToParcelAshmem(Parcel* parcel) {
+ return parcel->writeDupFileDescriptor(mAshmemFd);
+}
+
+status_t CursorWindow::writeToParcelInline(Parcel* parcel) {
+ status_t result = parcel->writeUint32(mHeader->freeOffset);
+ if (result) return result;
+
+ return parcel->write(mData, mHeader->freeOffset);
}
status_t CursorWindow::clear() {
@@ -187,6 +293,7 @@
if (rowSlot == NULL) {
return NO_MEMORY;
}
+ uint32_t rowSlotOffset = offsetFromPtr(rowSlot);
// Allocate the slots for the field directory
size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
@@ -201,7 +308,8 @@
memset(fieldDir, 0, fieldDirSize);
LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n",
- mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
+ mHeader->numRows - 1, rowSlotOffset, fieldDirSize, fieldDirOffset);
+ rowSlot = static_cast<RowSlot*>(offsetToPtr(rowSlotOffset));
rowSlot->offset = fieldDirOffset;
return OK;
}
@@ -229,10 +337,14 @@
uint32_t offset = mHeader->freeOffset + padding;
uint32_t nextFreeOffset = offset + size;
if (nextFreeOffset > mSize) {
- ALOGW("Window is full: requested allocation %zu bytes, "
- "free space %zu bytes, window size %zu bytes",
- size, freeSpace(), mSize);
- return 0;
+ // Try inflating to ashmem before finally giving up
+ inflate();
+ if (nextFreeOffset > mSize) {
+ ALOGW("Window is full: requested allocation %zu bytes, "
+ "free space %zu bytes, window size %zu bytes",
+ size, freeSpace(), mSize);
+ return 0;
+ }
}
mHeader->freeOffset = nextFreeOffset;
@@ -260,7 +372,10 @@
}
if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
if (!chunk->nextChunkOffset) {
- chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/);
+ uint32_t chunkOffset = offsetFromPtr(chunk);
+ uint32_t newChunk = alloc(sizeof(RowSlotChunk), true /*aligned*/);
+ chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunkOffset));
+ chunk->nextChunkOffset = newChunk;
if (!chunk->nextChunkOffset) {
return NULL;
}
@@ -308,6 +423,7 @@
if (!fieldSlot) {
return BAD_VALUE;
}
+ uint32_t fieldSlotOffset = offsetFromPtr(fieldSlot);
uint32_t offset = alloc(size);
if (!offset) {
@@ -316,6 +432,7 @@
memcpy(offsetToPtr(offset), value, size);
+ fieldSlot = static_cast<FieldSlot*>(offsetToPtr(fieldSlotOffset));
fieldSlot->type = type;
fieldSlot->data.buffer.offset = offset;
fieldSlot->data.buffer.size = size;
diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
index ad64b24..73c76f0 100644
--- a/libs/androidfw/include/androidfw/CursorWindow.h
+++ b/libs/androidfw/include/androidfw/CursorWindow.h
@@ -50,8 +50,8 @@
* Strings are stored in UTF-8.
*/
class CursorWindow {
- CursorWindow(const String8& name, int ashmemFd,
- void* data, size_t size, bool readOnly);
+ CursorWindow(const String8& name, int ashmemFd, void* data, size_t size,
+ size_t inflatedSize, bool readOnly);
public:
/* Field types. */
@@ -165,6 +165,7 @@
int mAshmemFd;
void* mData;
size_t mSize;
+ size_t mInflatedSize;
bool mReadOnly;
Header* mHeader;
@@ -185,6 +186,18 @@
return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
}
+ static status_t createFromParcelAshmem(Parcel*, String8&, CursorWindow**);
+ static status_t createFromParcelInline(Parcel*, String8&, CursorWindow**);
+
+ status_t writeToParcelAshmem(Parcel*);
+ status_t writeToParcelInline(Parcel*);
+
+ /**
+ * By default windows are lightweight inline allocations; this method
+ * inflates the window into a larger ashmem region.
+ */
+ status_t inflate();
+
/**
* Allocate a portion of the window. Returns the offset
* of the allocation, or 0 if there isn't enough space.
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 1fbb672..4380c13 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -25,6 +25,7 @@
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
import android.media.tv.ITvInputManagerCallback;
+import android.media.tv.TvChannelInfo;
import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
@@ -88,6 +89,8 @@
void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
+ List<TvChannelInfo> getCurrentTvChannelInfos(int userId);
+
// For the recording session
void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId);
void stopRecording(in IBinder sessionToken, int userId);
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 0f8bf2f..9f80bf5 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.media.tv.TvChannelInfo;
import android.media.tv.TvInputInfo;
/**
@@ -28,4 +29,5 @@
void onInputUpdated(in String inputId);
void onInputStateChanged(in String inputId, int state);
void onTvInputInfoUpdated(in TvInputInfo TvInputInfo);
+ void onCurrentTvChannelInfosUpdated(in List<TvChannelInfo> currentTvChannelInfos);
}
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/media/java/android/media/tv/TvChannelInfo.aidl
new file mode 100644
index 0000000..71cd0a7
--- /dev/null
+++ b/media/java/android/media/tv/TvChannelInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable TvChannelInfo;
diff --git a/media/java/android/media/tv/TvChannelInfo.java b/media/java/android/media/tv/TvChannelInfo.java
new file mode 100644
index 0000000..635b130
--- /dev/null
+++ b/media/java/android/media/tv/TvChannelInfo.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @hide
+ */
+public final class TvChannelInfo implements Parcelable {
+ static final String TAG = "TvChannelInfo";
+ public static final int APP_TAG_SELF = 0;
+ public static final int APP_TYPE_SELF = 1;
+ public static final int APP_TYPE_SYSTEM = 2;
+ public static final int APP_TYPE_NON_SYSTEM = 3;
+
+ /** @hide */
+ @IntDef(prefix = "APP_TYPE_", value = {APP_TYPE_SELF, APP_TYPE_SYSTEM, APP_TYPE_NON_SYSTEM})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AppType {}
+
+ public static final @NonNull Parcelable.Creator<TvChannelInfo> CREATOR =
+ new Parcelable.Creator<TvChannelInfo>() {
+ @Override
+ public TvChannelInfo createFromParcel(Parcel source) {
+ try {
+ return new TvChannelInfo(source);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TvChannelInfo from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TvChannelInfo[] newArray(int size) {
+ return new TvChannelInfo[size];
+ }
+ };
+
+
+ private final String mInputId;
+ @Nullable private final Uri mChannelUri;
+ private final boolean mIsRecordingSession;
+ private final boolean mIsForeground;
+ @AppType private final int mAppType;
+ private final int mAppTag;
+
+ public TvChannelInfo(
+ String inputId, @Nullable Uri channelUri, boolean isRecordingSession,
+ boolean isForeground, @AppType int appType, int appTag) {
+ mInputId = inputId;
+ mChannelUri = channelUri;
+ mIsRecordingSession = isRecordingSession;
+ mIsForeground = isForeground;
+ mAppType = appType;
+ mAppTag = appTag;
+ }
+
+
+ private TvChannelInfo(Parcel source) {
+ mInputId = source.readString();
+ String uriString = source.readString();
+ mChannelUri = uriString == null ? null : Uri.parse(uriString);
+ mIsRecordingSession = (source.readInt() == 1);
+ mIsForeground = (source.readInt() == 1);
+ mAppType = source.readInt();
+ mAppTag = source.readInt();
+ }
+
+ public String getInputId() {
+ return mInputId;
+ }
+
+ public Uri getChannelUri() {
+ return mChannelUri;
+ }
+
+ public boolean isRecordingSession() {
+ return mIsRecordingSession;
+ }
+
+ public boolean isForeground() {
+ return mIsForeground;
+ }
+
+ /**
+ * Gets app tag.
+ * <p>App tag is used to differentiate one app from another.
+ * {@link #APP_TAG_SELF} is for current app.
+ */
+ public int getAppTag() {
+ return mAppTag;
+ }
+
+ @AppType
+ public int getAppType() {
+ return mAppType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mInputId);
+ String uriString = mChannelUri == null ? null : mChannelUri.toString();
+ dest.writeString(uriString);
+ dest.writeInt(mIsRecordingSession ? 1 : 0);
+ dest.writeInt(mIsForeground ? 1 : 0);
+ dest.writeInt(mAppType);
+ dest.writeInt(mAppTag);
+ }
+
+ @Override
+ public String toString() {
+ return "inputID=" + mInputId
+ + ";channelUri=" + mChannelUri
+ + ";isRecording=" + mIsRecordingSession
+ + ";isForeground=" + mIsForeground
+ + ";appType=" + mAppType
+ + ";appTag=" + mAppTag;
+ }
+}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 98a01a4..d38369f 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -899,6 +899,10 @@
*/
public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
}
+
+ /** @hide */
+ public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> tvChannelInfos) {
+ }
}
private static final class TvInputCallbackRecord {
@@ -958,6 +962,16 @@
}
});
}
+
+ public void postCurrentTvChannelInfosUpdated(
+ final List<TvChannelInfo> currentTvChannelInfos) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+ }
+ });
+ }
}
/**
@@ -1262,6 +1276,15 @@
}
}
}
+
+ @Override
+ public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> currentTvChannelInfos) {
+ synchronized (mLock) {
+ for (TvInputCallbackRecord record : mCallbackRecords) {
+ record.postCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+ }
+ }
+ }
};
try {
if (mService != null) {
@@ -1953,6 +1976,17 @@
}
/**
+ * @hide
+ */
+ public List<TvChannelInfo> getCurrentTvChannelInfos() {
+ try {
+ return mService.getCurrentTvChannelInfos(mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* The Session provides the per-session functionality of TV inputs.
* @hide
*/
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index d46f1d1..dd06ae0 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -8783,6 +8783,7 @@
method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
+ method public void onServiceChanged(@NonNull android.bluetooth.BluetoothGatt);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
}
@@ -44233,6 +44234,7 @@
field public static final int MISSED = 5; // 0x5
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
+ field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 6c0dd33..44b0ab8 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -16,6 +16,14 @@
}
+package android.app.role {
+
+ public final class RoleManager {
+ method @Nullable public String getDefaultSmsPackage(int);
+ }
+
+}
+
package android.content.rollback {
public class RollbackManagerFrameworkInitializer {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 746f8aa..916e21f 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -9303,10 +9303,6 @@
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
- public final class DisconnectCause implements android.os.Parcelable {
- field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
- }
-
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index 3b00576..bf508b2 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"الألمانية السويسرية"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"البلجيكية"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"البلغارية"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"بلغارية صوتية"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"الإيطالية"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"الدانماركية"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"النرويجية"</string>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index 1929278..f387414 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"সুইস জার্মান"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"বেলজিয়ান"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"বুলগেরীয়"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"বুলগেরিয়ান, ফনেটিক"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ইতালীয়"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ডেনিশ"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"নরওয়েজীয়"</string>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 99aed5e..6b4f7ebf 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švýcarské (němčina)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgické"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bulharské"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulharská fonetická klávesnice"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italské"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"dánské"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norské"</string>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 697fff6..45aca35 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Allemand (Suisse)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgare"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Phonétique bulgare"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italien"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danois"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvégien"</string>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index 2e14019..b55a3c9 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Allemand (Suisse)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgare"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Phonétique bulgare"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italien"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danois"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvégien"</string>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 2ffebdd..7d1e2f8 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"स्विस जर्मन"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"बेल्जियाई"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"बुल्गारियाई"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"बुल्गेरियन, फ़ोनेटिक"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"इटैलियन"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"डैनिश"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"नार्वेजियाई"</string>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index cfdc3f8..dfe8c56 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Швейцариялық неміс"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Бельгиялық"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгар"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгар (фонетикалық)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Италиян"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дат"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвег"</string>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index 2aaf816c7..3bd7f20 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"អាល្លឺម៉ង់ ស្វីស"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"បែលហ្ស៊ិក"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ប៊ុលហ្ការី"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ប៊ុលហ្គារី សូរសព្ទ"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"អ៊ីតាលី"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ដាណឺម៉ាក"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ន័រវែស"</string>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index c036409..9584e15 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Германски (Швајцарија)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Белгиски"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Бугарски"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Бугарска, фонетска"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Италијански"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дански"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвешки"</string>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index 24816c1..4801f75 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"स्विस-जर्मन"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"बेल्जियन"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"बुल्गेरियन"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"बुल्गेरियन फोनेटिक किबोर्ड"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"इटालियन"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"डेनिश"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"नर्वेजियन"</string>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 0cb4f34..7066813 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -27,7 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"финский"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"хорватский"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"чешский"</string>
- <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чешский (QWERTY)"</string>
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"чешский (QWERTY)"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"эстонский"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"венгерский"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"исландский"</string>
@@ -44,9 +44,9 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"испанский (Латинская Америка)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"латышский"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Персидский"</string>
- <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Азербайджанский"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"азербайджанский"</string>
<string name="keyboard_layout_polish" msgid="1121588624094925325">"польский"</string>
- <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Белорусский"</string>
- <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монгольский"</string>
- <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинский"</string>
+ <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"белорусский"</string>
+ <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"монгольский"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузинский"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index b3677cd..9b4fe9e 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"gjermanishte zvicerane"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bullgarisht"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tastierë bullgare, fonetike"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italisht"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danisht"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norvegjisht"</string>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index b75b57d..d3c6000 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ஸ்விஸ் ஜெர்மன்"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"பெல்ஜியன்"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"பல்கேரியன்"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"பல்கேரியன், ஒலிப்புமுறை"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"இத்தாலியன்"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"டேனிஷ்"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"நார்வேஜியன்"</string>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index 21ea5de..b9aee76 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Swiss German"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgian"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarian"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarian, Phonetic"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italian"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danish"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegian"</string>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 1b42ece..76fd0bf 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -19,7 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tiếng Đức Thụy Sĩ"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Tiếng Bỉ"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Tiếng Bungary"</string>
- <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tiếng Bulgaria, Ngữ âm"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tiếng Bulgaria, Phiên âm"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Tiếng Ý"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Tiếng Đan Mạch"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Tiếng Na Uy"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 696ed29..f13d9b8 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -538,7 +538,7 @@
<string name="user_setup_button_setup_now" msgid="1708269547187760639">"지금 설정"</string>
<string name="user_setup_button_setup_later" msgid="8712980133555493516">"나중에"</string>
<string name="user_add_user_type_title" msgid="551279664052914497">"추가"</string>
- <string name="user_new_user_name" msgid="60979820612818840">"새 사용자"</string>
+ <string name="user_new_user_name" msgid="60979820612818840">"신규 사용자"</string>
<string name="user_new_profile_name" msgid="2405500423304678841">"새 프로필"</string>
<string name="user_info_settings_title" msgid="6351390762733279907">"사용자 정보"</string>
<string name="profile_info_settings_title" msgid="105699672534365099">"프로필 정보"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index e1d1950..b09e5b9 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -899,9 +899,9 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"إظهار رموز الإشعارات ذات الأولوية المنخفضة"</string>
<string name="other" msgid="429768510980739978">"غير ذلك"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة البطاقة"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"إضافة بطاقة إلى النهاية"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل البطاقة"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة بطاقة"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"إضافة بطاقة إلى نهاية الإعدادات السريعة"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل بطاقة"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"إضافة بطاقة"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"الانتقال إلى <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"الإضافة إلى الموضع <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index fcdd286..aed9522 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"কম-গুরুত্বপূর্ণ বিজ্ঞপ্তির আইকন দেখুন"</string>
<string name="other" msgid="429768510980739978">"অন্যান্য"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল সরান"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"শেষে টাইল যোগ করুন"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল সরান"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"টাইল যোগ করুন"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>-এ সরান"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>-এ যোগ করুন"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"দ্রুত সেটিংস সম্পাদক৷"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> বিজ্ঞপ্তি: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"সেটিংস খুলুন।"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 783f7877..0a03680 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -880,9 +880,9 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostra les icones de notificació amb prioritat baixa"</string>
<string name="other" msgid="429768510980739978">"Altres"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"suprimir el mosaic"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"afegir una targeta al final"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou la targeta"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Afegeix una targeta"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"afegir un mosaic al final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou el mosaic"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Afegeix un mosaic"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mou a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Afegeix a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 3f3d6be..419e625 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Zobrazit ikony oznámení s nízkou prioritou"</string>
<string name="other" msgid="429768510980739978">"Jiné"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranit dlaždici"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"přidat dlaždici na konec"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Přesunout dlaždici"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Přidat dlaždici"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Přesunout na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Přidat dlaždici na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Oznámení aplikace <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otevřít nastavení."</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 887aad5..4dbd74a 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -28,15 +28,15 @@
<string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string>
<string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Noch <xliff:g id="PERCENTAGE">%1$s</xliff:g> übrig; bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> ausstehend; noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Energiesparmodus ist aktiviert."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Stromsparmodus ist aktiviert."</string>
<string name="invalid_charger" msgid="4370074072117767416">"Aufladen über USB nicht möglich. Verwende das mit dem Gerät gelieferte Ladegerät."</string>
<string name="invalid_charger_title" msgid="938685362320735167">"Aufladen über USB nicht möglich"</string>
<string name="invalid_charger_text" msgid="2339310107232691577">"Verwende das mit dem Gerät gelieferte Ladegerät"</string>
<string name="battery_low_why" msgid="2056750982959359863">"Einstellungen"</string>
- <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Energiesparmodus aktivieren?"</string>
- <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Energiesparmodus"</string>
+ <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Stromsparmodus aktivieren?"</string>
+ <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Stromsparmodus"</string>
<string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Aktivieren"</string>
- <string name="battery_saver_start_action" msgid="4553256017945469937">"Energiesparmodus aktivieren"</string>
+ <string name="battery_saver_start_action" msgid="4553256017945469937">"Stromsparmodus aktivieren"</string>
<string name="status_bar_settings_settings_button" msgid="534331565185171556">"Einstellungen"</string>
<string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"WLAN"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Bildschirm automatisch drehen"</string>
@@ -419,7 +419,7 @@
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Bis <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Dunkles Design"</string>
- <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Energiesparmodus"</string>
+ <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Stromsparmodus"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"An bei Sonnenuntergang"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -497,9 +497,9 @@
<string name="user_remove_user_title" msgid="9124124694835811874">"Nutzer entfernen?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Alle Apps und Daten dieses Nutzers werden gelöscht."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Entfernen"</string>
- <string name="battery_saver_notification_title" msgid="8419266546034372562">"Energiesparmodus ist aktiviert"</string>
+ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Stromsparmodus ist aktiviert"</string>
<string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduzierung der Leistung und Hintergrunddaten"</string>
- <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Energiesparmodus deaktivieren"</string>
+ <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Stromsparmodus deaktivieren"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise angezeigte Passwörter und Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aufnahme oder Stream starten?"</string>
@@ -773,8 +773,8 @@
<item quantity="one">%d Minute</item>
</plurals>
<string name="battery_panel_title" msgid="5931157246673665963">"Akkunutzung"</string>
- <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string>
- <string name="battery_detail_switch_title" msgid="6940976502957380405">"Energiesparmodus"</string>
+ <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Stromsparmodus ist beim Aufladen nicht verfügbar."</string>
+ <string name="battery_detail_switch_title" msgid="6940976502957380405">"Stromsparmodus"</string>
<string name="battery_detail_switch_summary" msgid="3668748557848025990">"Reduzierung der Leistung und Hintergrunddaten"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string>
@@ -961,11 +961,11 @@
<string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> darf Teile aus jeder beliebigen App anzeigen"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"Zulassen"</string>
<string name="slice_permission_deny" msgid="6870256451658176895">"Ablehnen"</string>
- <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Energiesparmodus"</string>
+ <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Stromsparmodus"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string>
- <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Energiesparmodus aktiviert"</string>
- <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
+ <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Stromsparmodus aktiviert"</string>
+ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Stromsparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Einstellungen"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"Ok"</string>
<string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index dfd42c3..8a8f1e7 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -246,7 +246,7 @@
<string name="accessibility_remove_notification" msgid="1641455251495815527">"Eliminar notificación"</string>
<string name="accessibility_gps_enabled" msgid="4061313248217660858">"GPS habilitado"</string>
<string name="accessibility_gps_acquiring" msgid="896207402196024040">"Adquisición de GPS"</string>
- <string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter habilitado"</string>
+ <string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletipo habilitado"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Timbre en vibración"</string>
<string name="accessibility_ringer_silent" msgid="8994620163934249882">"Timbre en silencio"</string>
<!-- no translation found for accessibility_casting (8708751252897282313) -->
@@ -879,8 +879,8 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar íconos de notificaciones con prioridad baja"</string>
<string name="other" msgid="429768510980739978">"Otros"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Quitar tarjeta"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"Agregar tarjeta al final"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarjeta"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"agregar tarjeta al final"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover la tarjeta"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Agregar tarjeta"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index c1ab20c..e8d2f47 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -881,11 +881,11 @@
<string name="other" msgid="429768510980739978">"Beste bat"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"kendu lauza"</string>
<string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"gehitu lauza amaieran"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Aldatu tokiz lauza"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mugitu lauza"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Gehitu lauza"</string>
- <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren kokapenera"</string>
- <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren kokapenean"</string>
- <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kokapena: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren lekura"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren lekuan"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>garren lekua"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ezarpen bizkorren editorea."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> zerbitzuaren jakinarazpena: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ireki ezarpenak."</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index ea6de66..ff89ce2 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification de faible priorité"</string>
<string name="other" msgid="429768510980739978">"Autre"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"retirer la tuile"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ajouter la tuile à la fin"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la tuile"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ajouter la tuile"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de paramètres rapides."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index fc2691e3..a126cfa 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification à faible priorité"</string>
<string name="other" msgid="429768510980739978">"Autre"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"supprimer la carte"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ajouter la carte à la fin"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la carte"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ajouter une carte"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de configuration rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 1a69e81..9a563d9 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -881,20 +881,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकता वाली सूचना के आइकॉन दिखाएं"</string>
<string name="other" msgid="429768510980739978">"अन्य"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाएं"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल को आखिरी पोज़िशन पर जोड़ें"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल को किसी और पोज़िशन पर ले जाएं"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल जोड़ें"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर ले जाएं"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर जोड़ें"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"टाइल की पोज़िशन <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"त्वरित सेटिंग संपादक."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग खोलें."</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 28c79fb..171773c 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -880,7 +880,7 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Ցուցադրել ցածր առաջնահերթության ծանուցումների պատկերակները"</string>
<string name="other" msgid="429768510980739978">"Այլ"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"հեռացնել սալիկը"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ավելացնել սալիկ վերջում"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ավելացնել սալիկը վերջում"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Տեղափոխել սալիկը"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ավելացնել սալիկ"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Տեղափոխել դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 1f8f2aa..4a750a5 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -879,10 +879,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Sýna tákn fyrir tilkynningar með litlum forgangi"</string>
<string name="other" msgid="429768510980739978">"Annað"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja reit"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"bæta reit við aftast"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa reit"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Bæta reit við"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja flís"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"bæta flís við aftast"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa flís"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Bæta flís við"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Færa í <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bæta við í stöðu <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Staða <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 31e6bef..e6fbdb5 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -890,10 +890,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"הצגת סמלי התראות בעדיפות נמוכה"</string>
<string name="other" msgid="429768510980739978">"אחר"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"הסרת האריח"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"הוספת האריח לקצה"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"הזזת האריח"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"הוספת האריח בסוף הרשימה"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"העברת האריח"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"הוספת אריח"</string>
- <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה אל <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"הוספה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"עורך הגדרות מהירות."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index d775823..b1fab07 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -879,10 +879,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string>
<string name="other" msgid="429768510980739978">"Башка"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"плитканы өчүрүү"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"плитканы аягына кошуу"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Плитканы жылдыруу"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Плитка кошуу"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ыкчам баскычты өчүрүү"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ыкчам баскычты аягына кошуу"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ыкчам баскычты жылдыруу"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ыкчам баскыч кошуу"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Төмөнкүгө жылдыруу: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>-позицияга кошуу"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>-позиция"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index fc68681..80f0106 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -883,8 +883,8 @@
<string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ടൈൽ, അവസാന ഭാഗത്ത് ചേർക്കുക"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ടൈൽ നീക്കുക"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ടൈൽ ചേർക്കുക"</string>
- <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>-ലേക്ക് നീക്കുക"</string>
- <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>-ൽ ചേർക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> എന്നതിലേക്ക് നീക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> എന്ന സ്ഥാനത്തേക്ക് ചേർക്കുക"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> അറിയിപ്പ്: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 66e3ec3..7584173 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -889,10 +889,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показывать значки уведомлений с низким приоритетом"</string>
<string name="other" msgid="429768510980739978">"Другое"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить кнопку быстрого доступа"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавить кнопку быстрого доступа в конец"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить кнопку быстрого доступа"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавить кнопку быстрого доступа"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить панель"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавить панель в конец"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить панель"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавить панель"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Переместить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index abcde42..5172303 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Shfaq ikonat e njoftimeve me përparësi të ulët"</string>
<string name="other" msgid="429768510980739978">"Të tjera"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"hiq pllakëzën"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"shto pllakëzën në fund"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Zhvendos pllakëzën"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Shto pllakëzën"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Zhvendos te <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Shto te pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redaktori i cilësimeve të shpejta."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Njoftim nga <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Hap cilësimet."</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 9d5c54f..d91f1fd 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"குறைந்த முன்னுரிமை உள்ள அறிவிப்பு ஐகான்களைக் காட்டு"</string>
<string name="other" msgid="429768510980739978">"மற்றவை"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"டைலை அகற்றும்"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"கடைசியில் டைலைச் சேர்க்கும்"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"டைலை நகர்த்து"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"டைலைச் சேர்"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> இடத்திற்கு நகர்த்தும்"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> என்ற இடத்தில் சேர்க்கும்"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"இடம்: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"விரைவு அமைப்புகள் திருத்தி."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> அறிவிப்பு: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"அமைப்புகளைத் திற."</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index eeedac6..26053af 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -889,10 +889,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показувати значки сповіщень із низьким пріоритетом"</string>
<string name="other" msgid="429768510980739978">"Інше"</string>
- <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити значок"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додати значок у кінець"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити значок"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додати значок"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити опцію"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додати опцію в кінець"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити опцію"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додати опцію"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перемістити на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додати на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index facb47a..dbb9e03 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -880,8 +880,8 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
- <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到結尾"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移圖塊"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到最尾"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"加圖塊"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移去 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"加去位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 48da5d9..2e56df3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -506,6 +506,10 @@
<dimen name="qs_header_tile_margin_bottom">18dp</dimen>
<dimen name="qs_page_indicator_width">16dp</dimen>
<dimen name="qs_page_indicator_height">8dp</dimen>
+ <!-- The size of a single dot in relation to the whole animation.
+ Scaled @dimen/qs_page_indicator-width by .4f.
+ -->
+ <dimen name="qs_page_indicator_dot_width">6.4dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
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
new file mode 100644
index 0000000..27cb4f6
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.view.View;
+
+import com.android.internal.jank.InteractionJankMonitor;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public final class InteractionJankMonitorWrapper {
+ // Launcher journeys.
+ public static final int CUJ_APP_LAUNCH_FROM_RECENTS =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+ public static final int CUJ_APP_LAUNCH_FROM_ICON =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON;
+ public static final int CUJ_APP_CLOSE_TO_HOME =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_HOME;
+ public static final int CUJ_APP_CLOSE_TO_PIP =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP;
+ public static final int CUJ_QUICK_SWITCH =
+ InteractionJankMonitor.CUJ_LAUNCHER_QUICK_SWITCH;
+
+ @IntDef({
+ CUJ_APP_LAUNCH_FROM_RECENTS,
+ CUJ_APP_LAUNCH_FROM_ICON,
+ CUJ_APP_CLOSE_TO_HOME,
+ CUJ_APP_CLOSE_TO_PIP,
+ CUJ_QUICK_SWITCH,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CujType {
+ }
+
+ public static void init(@NonNull View view) {
+ InteractionJankMonitor.getInstance().init(view);
+ }
+
+ public static boolean begin(@CujType int cujType) {
+ return InteractionJankMonitor.getInstance().begin(cujType);
+ }
+
+ public static boolean begin(@CujType int cujType, long timeout) {
+ return InteractionJankMonitor.getInstance().begin(cujType, timeout);
+ }
+
+ public static boolean end(@CujType int cujType) {
+ return InteractionJankMonitor.getInstance().end(cujType);
+ }
+
+ public static boolean cancel(@CujType int cujType) {
+ return InteractionJankMonitor.getInstance().cancel(cujType);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index e99245f..23195af 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -33,9 +33,13 @@
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.systemui.dagger.qualifiers.Main;
import java.util.NoSuchElementException;
+import javax.inject.Inject;
+
/**
* Encapsulates all logic for secondary lockscreen state management.
*/
@@ -142,9 +146,9 @@
}
};
- public AdminSecondaryLockScreenController(Context context, ViewGroup parent,
+ private AdminSecondaryLockScreenController(Context context, KeyguardSecurityContainer parent,
KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
- Handler handler) {
+ @Main Handler handler) {
mContext = context;
mHandler = handler;
mParent = parent;
@@ -234,4 +238,26 @@
getHolder().removeCallback(mSurfaceHolderCallback);
}
}
+
+ @KeyguardBouncerScope
+ public static class Factory {
+ private final Context mContext;
+ private final KeyguardSecurityContainer mParent;
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final Handler mHandler;
+
+ @Inject
+ public Factory(Context context, KeyguardSecurityContainer parent,
+ KeyguardUpdateMonitor updateMonitor, @Main Handler handler) {
+ mContext = context;
+ mParent = parent;
+ mUpdateMonitor = updateMonitor;
+ mHandler = handler;
+ }
+
+ public AdminSecondaryLockScreenController create(KeyguardSecurityCallback callback) {
+ return new AdminSecondaryLockScreenController(mContext, mParent, mUpdateMonitor,
+ callback, mHandler);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 88f4176..cc6df45 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -16,46 +16,26 @@
package com.android.keyguard;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
-
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.os.AsyncTask;
-import android.os.CountDownTimer;
-import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.View;
-import android.widget.LinearLayout;
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
* Base class for PIN and password unlock screens.
*/
-public abstract class KeyguardAbsKeyInputView extends LinearLayout
- implements KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback {
- protected KeyguardSecurityCallback mCallback;
- protected LockPatternUtils mLockPatternUtils;
- protected AsyncTask<?, ?, ?> mPendingLockCheck;
- protected SecurityMessageDisplay mSecurityMessageDisplay;
+public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
protected View mEcaView;
protected boolean mEnableHaptics;
- private boolean mDismissing;
- protected boolean mResumed;
- private CountDownTimer mCountdownTimer = null;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
+ private KeyDownListener mKeyDownListener;
public KeyguardAbsKeyInputView(Context context) {
this(context, null);
@@ -63,38 +43,10 @@
public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
super(context, attrs);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
}
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
- }
-
- @Override
- public void reset() {
- // start fresh
- mDismissing = false;
- resetPasswordText(false /* animate */, false /* announce */);
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
- if (shouldLockout(deadline)) {
- handleAttemptLockout(deadline);
- } else {
- resetState();
- }
- }
-
- // Allow subclasses to override this behavior
- protected boolean shouldLockout(long deadline) {
- return deadline != 0;
+ void setEnableHaptics(boolean enableHaptics) {
+ mEnableHaptics = enableHaptics;
}
protected abstract int getPasswordTextViewId();
@@ -102,24 +54,7 @@
@Override
protected void onFinishInflate() {
- mLockPatternUtils = new LockPatternUtils(mContext);
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
-
- EmergencyButton button = findViewById(R.id.emergency_call_button);
- if (button != null) {
- button.setCallback(this);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this);
- }
-
- @Override
- public void onEmergencyButtonClickedWhenInCall() {
- mCallback.reset();
}
/*
@@ -131,195 +66,14 @@
return R.string.kg_wrong_password;
}
- protected void verifyPasswordAndUnlock() {
- if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
-
- final LockscreenCredential password = getEnteredCredential();
- setPasswordEntryInputEnabled(false);
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- }
-
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
- // to avoid accidental lockout, only count attempts that are long enough to be a
- // real password. This may require some tweaking.
- setPasswordEntryInputEnabled(true);
- onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
- password.zeroize();
- return;
- }
-
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
-
- mKeyguardUpdateMonitor.setCredentialAttempted();
- mPendingLockCheck = LockPatternChecker.checkCredential(
- mLockPatternUtils,
- password,
- userId,
- new LockPatternChecker.OnCheckCallback() {
-
- @Override
- public void onEarlyMatched() {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL);
- }
- onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
- true /* isValidPassword */);
- password.zeroize();
- }
-
- @Override
- public void onChecked(boolean matched, int timeoutMs) {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- setPasswordEntryInputEnabled(true);
- mPendingLockCheck = null;
- if (!matched) {
- onPasswordChecked(userId, false /* matched */, timeoutMs,
- true /* isValidPassword */);
- }
- password.zeroize();
- }
-
- @Override
- public void onCancelled() {
- // We already got dismissed with the early matched callback, so we cancelled
- // the check. However, we still need to note down the latency.
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- password.zeroize();
- }
- });
- }
-
- private void onPasswordChecked(int userId, boolean matched, int timeoutMs,
- boolean isValidPassword) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
- if (matched) {
- mCallback.reportUnlockAttempt(userId, true, 0);
- if (dismissKeyguard) {
- mDismissing = true;
- mCallback.dismiss(true, userId);
- }
- } else {
- if (isValidPassword) {
- mCallback.reportUnlockAttempt(userId, false, timeoutMs);
- if (timeoutMs > 0) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
- userId, timeoutMs);
- handleAttemptLockout(deadline);
- }
- }
- if (timeoutMs == 0) {
- mSecurityMessageDisplay.setMessage(getWrongPasswordStringId());
- }
- }
- resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
- }
-
protected abstract void resetPasswordText(boolean animate, boolean announce);
protected abstract LockscreenCredential getEnteredCredential();
protected abstract void setPasswordEntryEnabled(boolean enabled);
protected abstract void setPasswordEntryInputEnabled(boolean enabled);
- // Prevent user from using the PIN/Password entry until scheduled deadline.
- protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
- setPasswordEntryEnabled(false);
- long elapsedRealtime = SystemClock.elapsedRealtime();
- long secondsInFuture = (long) Math.ceil(
- (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
- mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
- mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString(
- R.plurals.kg_too_many_failed_attempts_countdown,
- secondsRemaining, secondsRemaining));
- }
-
- @Override
- public void onFinish() {
- mSecurityMessageDisplay.setMessage("");
- resetState();
- }
- }.start();
- }
-
- protected void onUserInput() {
- if (mCallback != null) {
- mCallback.userActivity();
- mCallback.onUserInput();
- }
- mSecurityMessageDisplay.setMessage("");
- }
-
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
- // We don't want to consider it valid user input because the UI
- // will already respond to the event.
- if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
- onUserInput();
- }
- return false;
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void onPause() {
- mResumed = false;
-
- if (mCountdownTimer != null) {
- mCountdownTimer.cancel();
- mCountdownTimer = null;
- }
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- mPendingLockCheck = null;
- }
- reset();
- }
-
- @Override
- public void onResume(int reason) {
- mResumed = true;
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void showPromptReason(int reason) {
- if (reason != PROMPT_REASON_NONE) {
- int promtReasonStringRes = getPromptReasonStringRes(reason);
- if (promtReasonStringRes != 0) {
- mSecurityMessageDisplay.setMessage(promtReasonStringRes);
- }
- }
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (colorState != null) {
- mSecurityMessageDisplay.setNextMessageColor(colorState);
- }
- mSecurityMessageDisplay.setMessage(message);
+ return mKeyDownListener != null && mKeyDownListener.onKeyDown(keyCode, event);
}
protected abstract int getPromptReasonStringRes(int reason);
@@ -333,9 +87,12 @@
}
}
- @Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return false;
+ public void setKeyDownListener(KeyDownListener keyDownListener) {
+ mKeyDownListener = keyDownListener;
+ }
+
+ public interface KeyDownListener {
+ boolean onKeyDown(int keyCode, KeyEvent keyEvent);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
new file mode 100644
index 0000000..53f8474
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
+
+import android.content.res.ColorStateList;
+import android.os.AsyncTask;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.view.KeyEvent;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
+import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView>
+ extends KeyguardInputViewController<T> {
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final LatencyTracker mLatencyTracker;
+ private CountDownTimer mCountdownTimer;
+ protected KeyguardMessageAreaController mMessageAreaController;
+ private boolean mDismissing;
+ protected AsyncTask<?, ?, ?> mPendingLockCheck;
+ protected boolean mResumed;
+
+ private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> {
+ // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
+ // We don't want to consider it valid user input because the UI
+ // will already respond to the event.
+ if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
+ onUserInput();
+ }
+ return false;
+ };
+
+ private final EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
+ @Override
+ public void onEmergencyButtonClickedWhenInCall() {
+ getKeyguardSecurityCallback().reset();
+ }
+ };
+
+ protected KeyguardAbsKeyInputViewController(T view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker) {
+ super(view, securityMode, keyguardSecurityCallback);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mLatencyTracker = latencyTracker;
+ KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
+ mMessageAreaController = messageAreaControllerFactory.create(kma);
+ }
+
+ abstract void resetState();
+
+ @Override
+ public void init() {
+ super.init();
+ mMessageAreaController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mView.setKeyDownListener(mKeyDownListener);
+ mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
+ EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(mEmergencyButtonCallback);
+ }
+ }
+
+ @Override
+ public void reset() {
+ // start fresh
+ mDismissing = false;
+ mView.resetPasswordText(false /* animate */, false /* announce */);
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (shouldLockout(deadline)) {
+ handleAttemptLockout(deadline);
+ } else {
+ resetState();
+ }
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (colorState != null) {
+ mMessageAreaController.setNextMessageColor(colorState);
+ }
+ mMessageAreaController.setMessage(message);
+ }
+
+ // Allow subclasses to override this behavior
+ protected boolean shouldLockout(long deadline) {
+ return deadline != 0;
+ }
+
+ // Prevent user from using the PIN/Password entry until scheduled deadline.
+ protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mView.setPasswordEntryEnabled(false);
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ long secondsInFuture = (long) Math.ceil(
+ (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
+ mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
+ mMessageAreaController.setMessage(mView.getResources().getQuantityString(
+ R.plurals.kg_too_many_failed_attempts_countdown,
+ secondsRemaining, secondsRemaining));
+ }
+
+ @Override
+ public void onFinish() {
+ mMessageAreaController.setMessage("");
+ resetState();
+ }
+ }.start();
+ }
+
+ void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
+ boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ if (matched) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
+ if (dismissKeyguard) {
+ mDismissing = true;
+ getKeyguardSecurityCallback().dismiss(true, userId);
+ }
+ } else {
+ if (isValidPassword) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
+ if (timeoutMs > 0) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
+ userId, timeoutMs);
+ handleAttemptLockout(deadline);
+ }
+ }
+ if (timeoutMs == 0) {
+ mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
+ }
+ }
+ mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
+ }
+
+ protected void verifyPasswordAndUnlock() {
+ if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
+
+ final LockscreenCredential password = mView.getEnteredCredential();
+ mView.setPasswordEntryInputEnabled(false);
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ }
+
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
+ // to avoid accidental lockout, only count attempts that are long enough to be a
+ // real password. This may require some tweaking.
+ mView.setPasswordEntryInputEnabled(true);
+ onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
+ password.zeroize();
+ return;
+ }
+
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+
+ mKeyguardUpdateMonitor.setCredentialAttempted();
+ mPendingLockCheck = LockPatternChecker.checkCredential(
+ mLockPatternUtils,
+ password,
+ userId,
+ new LockPatternChecker.OnCheckCallback() {
+
+ @Override
+ public void onEarlyMatched() {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);
+
+ onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
+ true /* isValidPassword */);
+ password.zeroize();
+ }
+
+ @Override
+ public void onChecked(boolean matched, int timeoutMs) {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ mView.setPasswordEntryInputEnabled(true);
+ mPendingLockCheck = null;
+ if (!matched) {
+ onPasswordChecked(userId, false /* matched */, timeoutMs,
+ true /* isValidPassword */);
+ }
+ password.zeroize();
+ }
+
+ @Override
+ public void onCancelled() {
+ // We already got dismissed with the early matched callback, so we cancelled
+ // the check. However, we still need to note down the latency.
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ password.zeroize();
+ }
+ });
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ if (reason != PROMPT_REASON_NONE) {
+ int promtReasonStringRes = mView.getPromptReasonStringRes(reason);
+ if (promtReasonStringRes != 0) {
+ mMessageAreaController.setMessage(promtReasonStringRes);
+ }
+ }
+ }
+
+ protected void onUserInput() {
+ getKeyguardSecurityCallback().userActivity();
+ getKeyguardSecurityCallback().onUserInput();
+ mMessageAreaController.setMessage("");
+ }
+
+ @Override
+ public void onResume(int reason) {
+ mResumed = true;
+ }
+
+ @Override
+ public void onPause() {
+ mResumed = false;
+
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ mPendingLockCheck = null;
+ }
+ reset();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index be21d20..36d5543 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -39,7 +39,6 @@
import com.android.systemui.R;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
-import com.android.systemui.util.InjectionInflationController;
import javax.inject.Inject;
@@ -49,7 +48,6 @@
private final MediaRouter mMediaRouter;
private final DisplayManager mDisplayService;
- private final InjectionInflationController mInjectableInflater;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final Context mContext;
@@ -92,10 +90,8 @@
@Inject
public KeyguardDisplayManager(Context context,
- InjectionInflationController injectableInflater,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
mContext = context;
- mInjectableInflater = injectableInflater;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mMediaRouter = mContext.getSystemService(MediaRouter.class);
mDisplayService = mContext.getSystemService(DisplayManager.class);
@@ -131,8 +127,7 @@
Presentation presentation = mPresentations.get(displayId);
if (presentation == null) {
final Presentation newPresentation = new KeyguardPresentation(mContext, display,
- mKeyguardStatusViewComponentFactory,
- mInjectableInflater.injectable(LayoutInflater.from(mContext)));
+ mKeyguardStatusViewComponentFactory, LayoutInflater.from(mContext));
newPresentation.setOnDismissListener(dialog -> {
if (newPresentation.equals(mPresentations.get(displayId))) {
mPresentations.remove(displayId);
@@ -250,7 +245,7 @@
private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private final LayoutInflater mInjectableLayoutInflater;
+ private final LayoutInflater mLayoutInflater;
private KeyguardClockSwitchController mKeyguardClockSwitchController;
private View mClock;
private int mUsableWidth;
@@ -270,10 +265,10 @@
KeyguardPresentation(Context context, Display display,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
- LayoutInflater injectionLayoutInflater) {
+ LayoutInflater layoutInflater) {
super(context, display, R.style.Theme_SystemUI_KeyguardPresentation);
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
- mInjectableLayoutInflater = injectionLayoutInflater;
+ mLayoutInflater = layoutInflater;
getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
setCancelable(false);
}
@@ -299,7 +294,7 @@
mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
- setContentView(mInjectableLayoutInflater.inflate(R.layout.keyguard_presentation, null));
+ setContentView(mLayoutInflater.inflate(R.layout.keyguard_presentation, null));
// Logic to make the lock screen fullscreen
getWindow().getDecorView().setSystemUiVisibility(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 7aabb17..351369c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -163,33 +163,34 @@
@Inject
public KeyguardHostViewController(KeyguardHostView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardSecurityContainerController keyguardSecurityContainerController,
AudioManager audioManager,
TelephonyManager telephonyManager,
- ViewMediatorCallback viewMediatorCallback) {
+ ViewMediatorCallback viewMediatorCallback,
+ KeyguardSecurityContainerController.Factory
+ keyguardSecurityContainerControllerFactory) {
super(view);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mKeyguardSecurityContainerController = keyguardSecurityContainerController;
mAudioManager = audioManager;
mTelephonyManager = telephonyManager;
mViewMediatorCallback = viewMediatorCallback;
+ mKeyguardSecurityContainerController = keyguardSecurityContainerControllerFactory.create(
+ mSecurityCallback);
}
/** Initialize the Controller. */
public void init() {
super.init();
- mView.setViewMediatorCallback(mViewMediatorCallback);
- // Update ViewMediator with the current input method requirements
- mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
mKeyguardSecurityContainerController.init();
- mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
- mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
}
@Override
protected void onViewAttached() {
+ mView.setViewMediatorCallback(mViewMediatorCallback);
+ // Update ViewMediator with the current input method requirements
+ mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
mView.setOnKeyListener(mOnKeyListener);
+ mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
}
@Override
@@ -350,7 +351,7 @@
}
public boolean handleBackKey() {
- if (mKeyguardSecurityContainerController.getCurrentSecuritySelection()
+ if (mKeyguardSecurityContainerController.getCurrentSecurityMode()
!= SecurityMode.None) {
mKeyguardSecurityContainerController.dismiss(
false, KeyguardUpdateMonitor.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
new file mode 100644
index 0000000..d42a53c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * A Base class for all Keyguard password/pattern/pin related inputs.
+ */
+public abstract class KeyguardInputView extends LinearLayout {
+
+ public KeyguardInputView(Context context) {
+ super(context);
+ }
+
+ public KeyguardInputView(Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public KeyguardInputView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ abstract CharSequence getTitle();
+
+ boolean disallowInterceptTouch(MotionEvent event) {
+ return false;
+ }
+
+ void startAppearAnimation() {}
+
+ boolean startDisappearAnimation(Runnable finishRunnable) {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
new file mode 100644
index 0000000..fbda818
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.telephony.TelephonyManager;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import javax.inject.Inject;
+
+
+/** Controller for a {@link KeyguardSecurityView}. */
+public abstract class KeyguardInputViewController<T extends KeyguardInputView>
+ extends ViewController<T> implements KeyguardSecurityView {
+
+ private final SecurityMode mSecurityMode;
+ private final KeyguardSecurityCallback mKeyguardSecurityCallback;
+ private boolean mPaused;
+
+
+ // The following is used to ignore callbacks from SecurityViews that are no longer current
+ // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
+ // state for the current security method.
+ private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
+ @Override
+ public void userActivity() { }
+ @Override
+ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
+ @Override
+ public boolean isVerifyUnlockOnly() {
+ return false;
+ }
+ @Override
+ public void dismiss(boolean securityVerified, int targetUserId) { }
+ @Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) { }
+ @Override
+ public void onUserInput() { }
+ @Override
+ public void reset() {}
+ };
+
+ protected KeyguardInputViewController(T view, SecurityMode securityMode,
+ KeyguardSecurityCallback keyguardSecurityCallback) {
+ super(view);
+ mSecurityMode = securityMode;
+ mKeyguardSecurityCallback = keyguardSecurityCallback;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ }
+
+ @Override
+ protected void onViewDetached() {
+ }
+
+ SecurityMode getSecurityMode() {
+ return mSecurityMode;
+ }
+
+ protected KeyguardSecurityCallback getKeyguardSecurityCallback() {
+ if (mPaused) {
+ return mNullCallback;
+ }
+
+ return mKeyguardSecurityCallback;
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ public void onPause() {
+ mPaused = true;
+ }
+
+ @Override
+ public void onResume(int reason) {
+ mPaused = false;
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ }
+
+ public void startAppearAnimation() {
+ mView.startAppearAnimation();
+ }
+
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return mView.startDisappearAnimation(finishRunnable);
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mView.getTitle();
+ }
+
+ /** Finds the index of this view in the suppplied parent view. */
+ public int getIndexIn(KeyguardSecurityViewFlipper view) {
+ return view.indexOfChild(mView);
+ }
+
+ /** Factory for a {@link KeyguardInputViewController}. */
+ public static class Factory {
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final LatencyTracker mLatencyTracker;
+ private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
+ private final InputMethodManager mInputMethodManager;
+ private final DelayableExecutor mMainExecutor;
+ private final Resources mResources;
+ private LiftToActivateListener mLiftToActivateListener;
+ private TelephonyManager mTelephonyManager;
+
+ @Inject
+ public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
+ LockPatternUtils lockPatternUtils,
+ LatencyTracker latencyTracker,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
+ @Main Resources resources, LiftToActivateListener liftToActivateListener,
+ TelephonyManager telephonyManager) {
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mLatencyTracker = latencyTracker;
+ mMessageAreaControllerFactory = messageAreaControllerFactory;
+ mInputMethodManager = inputMethodManager;
+ mMainExecutor = mainExecutor;
+ mResources = resources;
+ mLiftToActivateListener = liftToActivateListener;
+ mTelephonyManager = telephonyManager;
+ }
+
+ /** Create a new {@link KeyguardInputViewController}. */
+ public KeyguardInputViewController create(KeyguardInputView keyguardInputView,
+ SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback) {
+ if (keyguardInputView instanceof KeyguardPatternView) {
+ return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory);
+ } else if (keyguardInputView instanceof KeyguardPasswordView) {
+ return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mInputMethodManager, mMainExecutor, mResources);
+ } else if (keyguardInputView instanceof KeyguardPINView) {
+ return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mLiftToActivateListener);
+ } else if (keyguardInputView instanceof KeyguardSimPinView) {
+ return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mLiftToActivateListener, mTelephonyManager);
+ } else if (keyguardInputView instanceof KeyguardSimPukView) {
+ return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mLiftToActivateListener, mTelephonyManager);
+ }
+
+ throw new RuntimeException("Unable to find controller for " + keyguardInputView);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index a8b1451..1a0a437 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -16,8 +16,6 @@
package com.android.keyguard;
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -31,20 +29,14 @@
import android.view.View;
import android.widget.TextView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import java.lang.ref.WeakReference;
-import javax.inject.Inject;
-import javax.inject.Named;
-
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
*/
-public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay,
- ConfigurationController.ConfigurationListener {
+public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
/** Handler token posted with accessibility announcement runnables. */
private static final Object ANNOUNCE_TOKEN = new Object();
@@ -56,71 +48,26 @@
private static final int DEFAULT_COLOR = -1;
private final Handler mHandler;
- private final ConfigurationController mConfigurationController;
private ColorStateList mDefaultColorState;
private CharSequence mMessage;
private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
private boolean mBouncerVisible;
- private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
- public void onFinishedGoingToSleep(int why) {
- setSelected(false);
- }
-
- public void onStartedWakingUp() {
- setSelected(true);
- }
-
- @Override
- public void onKeyguardBouncerChanged(boolean bouncer) {
- mBouncerVisible = bouncer;
- update();
- }
- };
-
- public KeyguardMessageArea(Context context) {
- super(context, null);
- throw new IllegalStateException("This constructor should never be invoked");
- }
-
- @Inject
- public KeyguardMessageArea(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- ConfigurationController configurationController) {
- this(context, attrs, Dependency.get(KeyguardUpdateMonitor.class), configurationController);
- }
-
- public KeyguardMessageArea(Context context, AttributeSet attrs, KeyguardUpdateMonitor monitor,
- ConfigurationController configurationController) {
+ public KeyguardMessageArea(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
- monitor.registerCallback(mInfoCallback);
mHandler = new Handler(Looper.myLooper());
- mConfigurationController = configurationController;
onThemeChanged();
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mConfigurationController.addCallback(this);
- onThemeChanged();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mConfigurationController.removeCallback(this);
- }
-
- @Override
public void setNextMessageColor(ColorStateList colorState) {
mNextMessageColorState = colorState;
}
- @Override
- public void onThemeChanged() {
+ void onThemeChanged() {
TypedArray array = mContext.obtainStyledAttributes(new int[] {
R.attr.wallpaperTextColor
});
@@ -130,8 +77,7 @@
update();
}
- @Override
- public void onDensityOrFontScaleChanged() {
+ void onDensityOrFontScaleChanged() {
TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] {
android.R.attr.textSize
});
@@ -177,12 +123,6 @@
return messageArea;
}
- @Override
- protected void onFinishInflate() {
- boolean shouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive();
- setSelected(shouldMarquee); // This is required to ensure marquee works
- }
-
private void securityMessageChanged(CharSequence message) {
mMessage = message;
update();
@@ -196,7 +136,7 @@
update();
}
- private void update() {
+ void update() {
CharSequence status = mMessage;
setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE);
setText(status);
@@ -208,6 +148,9 @@
setTextColor(colorState);
}
+ public void setBouncerVisible(boolean bouncerVisible) {
+ mBouncerVisible = bouncerVisible;
+ }
/**
* Runnable used to delay accessibility announcements.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index f056bdb..1618e8e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -16,7 +16,10 @@
package com.android.keyguard;
+import android.content.res.ColorStateList;
+
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -26,6 +29,35 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
+
+ private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+ public void onFinishedGoingToSleep(int why) {
+ mView.setSelected(false);
+ }
+
+ public void onStartedWakingUp() {
+ mView.setSelected(true);
+ }
+
+ @Override
+ public void onKeyguardBouncerChanged(boolean bouncer) {
+ mView.setBouncerVisible(bouncer);
+ mView.update();
+ }
+ };
+
+ private ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ @Override
+ public void onThemeChanged() {
+ mView.onThemeChanged();
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mView.onDensityOrFontScaleChanged();
+ }
+ };
+
private KeyguardMessageAreaController(KeyguardMessageArea view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController) {
@@ -37,17 +69,31 @@
@Override
protected void onViewAttached() {
- //mConfigurationController.addCallback();
- //mKeyguardUpdateMonitor.registerCallback();
+ mConfigurationController.addCallback(mConfigurationListener);
+ mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
+ mView.setSelected(mKeyguardUpdateMonitor.isDeviceInteractive());
+ mView.onThemeChanged();
}
@Override
protected void onViewDetached() {
- //mConfigurationController.removeCallback();
- //mKeyguardUpdateMonitor.removeCallback();
+ mConfigurationController.removeCallback(mConfigurationListener);
+ mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
}
- /** Factory for createing {@link com.android.keyguard.KeyguardMessageAreaController}. */
+ public void setMessage(CharSequence s) {
+ mView.setMessage(s);
+ }
+
+ public void setMessage(int resId) {
+ mView.setMessage(resId);
+ }
+
+ public void setNextMessageColor(ColorStateList colorState) {
+ mView.setNextMessageColor(colorState);
+ }
+
+ /** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */
public static class Factory {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 12ea1d5..580d704 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -24,7 +24,6 @@
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
@@ -40,10 +39,8 @@
private ViewGroup mRow1;
private ViewGroup mRow2;
private ViewGroup mRow3;
- private View mDivider;
private int mDisappearYTranslation;
private View[][] mViews;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
public KeyguardPINView(Context context) {
this(context, null);
@@ -63,15 +60,10 @@
mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
}
@Override
protected void resetState() {
- super.resetState();
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage("");
- }
}
@Override
@@ -88,7 +80,6 @@
mRow1 = findViewById(R.id.row1);
mRow2 = findViewById(R.id.row2);
mRow3 = findViewById(R.id.row3);
- mDivider = findViewById(R.id.divider);
mViews = new View[][]{
new View[]{
mRow0, null, null
@@ -112,18 +103,6 @@
new View[]{
null, mEcaView, null
}};
-
- View cancelBtn = findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- mCallback.reset();
- mCallback.onCancelClicked();
- });
- }
- }
-
- @Override
- public void showUsabilityHint() {
}
@Override
@@ -147,24 +126,21 @@
});
}
- @Override
- public boolean startDisappearAnimation(final Runnable finishRunnable) {
+ public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
+ final Runnable finishRunnable) {
+
enableClipping(false);
setTranslationY(0);
AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */,
mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator());
- DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor
- .needsSlowUnlockTransition()
+ DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
? mDisappearAnimationUtilsLocked
: mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mViews,
- new Runnable() {
- @Override
- public void run() {
- enableClipping(true);
- if (finishRunnable != null) {
- finishRunnable.run();
- }
+ () -> {
+ enableClipping(true);
+ if (finishRunnable != null) {
+ finishRunnable.run();
}
});
return true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 97317cf..aaa5efe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -16,50 +16,37 @@
package com.android.keyguard;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+
import android.content.Context;
import android.graphics.Rect;
-import android.os.UserHandle;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.text.method.TextKeyListener;
import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
import android.widget.TextView;
-import android.widget.TextView.OnEditorActionListener;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.systemui.R;
-
-import java.util.List;
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
* an unlock password
*/
-public class KeyguardPasswordView extends KeyguardAbsKeyInputView
- implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
- private final boolean mShowImeAtScreenOn;
private final int mDisappearYTranslation;
// A delay constant to be used in a workaround for the situation where InputMethodManagerService
// is not switched to the new user yet.
// TODO: Remove this by ensuring such a race condition never happens.
- private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
- InputMethodManager mImm;
private TextView mPasswordEntry;
private TextViewInputDisabler mPasswordEntryDisabler;
- private View mSwitchImeButton;
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
@@ -70,8 +57,6 @@
public KeyguardPasswordView(Context context, AttributeSet attrs) {
super(context, attrs);
- mShowImeAtScreenOn = context.getResources().
- getBoolean(R.bool.kg_show_ime_at_screen_on);
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
@@ -82,20 +67,6 @@
@Override
protected void resetState() {
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage("");
- }
- final boolean wasDisabled = mPasswordEntry.isEnabled();
- setPasswordEntryEnabled(true);
- setPasswordEntryInputEnabled(true);
- // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
- if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
- return;
- }
- if (wasDisabled) {
- mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
- }
}
@Override
@@ -104,29 +75,6 @@
}
@Override
- public boolean needsInput() {
- return true;
- }
-
- @Override
- public void onResume(final int reason) {
- super.onResume(reason);
-
- // Wait a bit to focus the field so the focusable flag on the window is already set then.
- post(new Runnable() {
- @Override
- public void run() {
- if (isShown() && mPasswordEntry.isEnabled()) {
- mPasswordEntry.requestFocus();
- if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
- mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
- }
- }
- }
- });
- }
-
- @Override
protected int getPromptReasonStringRes(int reason) {
switch (reason) {
case PROMPT_REASON_RESTART:
@@ -146,97 +94,13 @@
}
}
- @Override
- public void onPause() {
- super.onPause();
- mImm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
-
- @Override
- public void onStartingToHide() {
- mImm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
-
- private void updateSwitchImeButton() {
- // If there's more than one IME, enable the IME switcher button
- final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
- final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false);
- if (wasVisible != shouldBeVisible) {
- mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
- }
-
- // TODO: Check if we still need this hack.
- // If no icon is visible, reset the start margin on the password field so the text is
- // still centered.
- if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
- android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
- if (params instanceof MarginLayoutParams) {
- final MarginLayoutParams mlp = (MarginLayoutParams) params;
- mlp.setMarginStart(0);
- mPasswordEntry.setLayoutParams(params);
- }
- }
- }
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mImm = (InputMethodManager) getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
-
mPasswordEntry = findViewById(getPasswordTextViewId());
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
- mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
- | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- mPasswordEntry.setOnEditorActionListener(this);
- mPasswordEntry.addTextChangedListener(this);
-
- // Poke the wakelock any time the text is selected or modified
- mPasswordEntry.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCallback.userActivity();
- }
- });
-
- // Set selected property on so the view can send accessibility events.
- mPasswordEntry.setSelected(true);
-
- mSwitchImeButton = findViewById(R.id.switch_ime_button);
- mSwitchImeButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCallback.userActivity(); // Leave the screen on a bit longer
- // Do not show auxiliary subtypes in password lock screen.
- mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */,
- getContext().getDisplayId());
- }
- });
-
- View cancelBtn = findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- mCallback.reset();
- mCallback.onCancelClicked();
- });
- }
-
- // If there's more than one IME, enable the IME switcher button
- updateSwitchImeButton();
-
- // When we the current user is switching, InputMethodManagerService sometimes has not
- // switched internal state yet here. As a quick workaround, we check the keyboard state
- // again.
- // TODO: Remove this workaround by ensuring such a race condition never happens.
- postDelayed(new Runnable() {
- @Override
- public void run() {
- updateSwitchImeButton();
- }
- }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
}
@Override
@@ -265,59 +129,6 @@
mPasswordEntryDisabler.setInputEnabled(enabled);
}
- /**
- * Method adapted from com.android.inputmethod.latin.Utils
- *
- * @param imm The input method manager
- * @param shouldIncludeAuxiliarySubtypes
- * @return true if we have multiple IMEs to choose from
- */
- private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
- final boolean shouldIncludeAuxiliarySubtypes) {
- final List<InputMethodInfo> enabledImis =
- imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
-
- // Number of the filtered IMEs
- int filteredImisCount = 0;
-
- for (InputMethodInfo imi : enabledImis) {
- // We can return true immediately after we find two or more filtered IMEs.
- if (filteredImisCount > 1) return true;
- final List<InputMethodSubtype> subtypes =
- imm.getEnabledInputMethodSubtypeList(imi, true);
- // IMEs that have no subtypes should be counted.
- if (subtypes.isEmpty()) {
- ++filteredImisCount;
- continue;
- }
-
- int auxCount = 0;
- for (InputMethodSubtype subtype : subtypes) {
- if (subtype.isAuxiliary()) {
- ++auxCount;
- }
- }
- final int nonAuxCount = subtypes.size() - auxCount;
-
- // IMEs that have one or more non-auxiliary subtypes should be counted.
- // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
- // subtypes should be counted as well.
- if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
- ++filteredImisCount;
- continue;
- }
- }
-
- return filteredImisCount > 1
- // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
- // input method subtype (The current IME should be LatinIME.)
- || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
@Override
public int getWrongPasswordStringId() {
return R.string.kg_wrong_password;
@@ -346,45 +157,8 @@
}
@Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- if (mCallback != null) {
- mCallback.userActivity();
- }
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- // Poor man's user edit detection, assuming empty text is programmatic and everything else
- // is from the user.
- if (!TextUtils.isEmpty(s)) {
- onUserInput();
- }
- }
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- // Check if this was the result of hitting the enter key
- final boolean isSoftImeEvent = event == null
- && (actionId == EditorInfo.IME_NULL
- || actionId == EditorInfo.IME_ACTION_DONE
- || actionId == EditorInfo.IME_ACTION_NEXT);
- final boolean isKeyboardEnterKey = event != null
- && KeyEvent.isConfirmKey(event.getKeyCode())
- && event.getAction() == KeyEvent.ACTION_DOWN;
- if (isSoftImeEvent || isKeyboardEnterKey) {
- verifyPasswordAndUnlock();
- return true;
- }
- return false;
- }
-
- @Override
public CharSequence getTitle() {
- return getContext().getString(
+ return getResources().getString(
com.android.internal.R.string.keyguard_accessibility_password_unlock);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
new file mode 100644
index 0000000..d34ea8c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.method.TextKeyListener;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import java.util.List;
+
+public class KeyguardPasswordViewController
+ extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> {
+
+ private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
+
+ private final KeyguardSecurityCallback mKeyguardSecurityCallback;
+ private final InputMethodManager mInputMethodManager;
+ private final DelayableExecutor mMainExecutor;
+ private final boolean mShowImeAtScreenOn;
+ private TextView mPasswordEntry;
+ private View mSwitchImeButton;
+
+ private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> {
+ // Check if this was the result of hitting the enter key
+ final boolean isSoftImeEvent = event == null
+ && (actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT);
+ final boolean isKeyboardEnterKey = event != null
+ && KeyEvent.isConfirmKey(event.getKeyCode())
+ && event.getAction() == KeyEvent.ACTION_DOWN;
+ if (isSoftImeEvent || isKeyboardEnterKey) {
+ verifyPasswordAndUnlock();
+ return true;
+ }
+ return false;
+ };
+
+ private final TextWatcher mTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ mKeyguardSecurityCallback.userActivity();
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (!TextUtils.isEmpty(s)) {
+ onUserInput();
+ }
+ }
+ };
+
+ protected KeyguardPasswordViewController(KeyguardPasswordView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ InputMethodManager inputMethodManager,
+ @Main DelayableExecutor mainExecutor,
+ @Main Resources resources) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker);
+ mKeyguardSecurityCallback = keyguardSecurityCallback;
+ mInputMethodManager = inputMethodManager;
+ mMainExecutor = mainExecutor;
+ mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on);
+ mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
+ mSwitchImeButton = mView.findViewById(R.id.switch_ime_button);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
+ mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+
+ // Set selected property on so the view can send accessibility events.
+ mPasswordEntry.setSelected(true);
+ mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener);
+ mPasswordEntry.addTextChangedListener(mTextWatcher);
+ // Poke the wakelock any time the text is selected or modified
+ mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity());
+
+ mSwitchImeButton.setOnClickListener(v -> {
+ mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer
+ // Do not show auxiliary subtypes in password lock screen.
+ mInputMethodManager.showInputMethodPickerFromSystem(false,
+ mView.getContext().getDisplayId());
+ });
+
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ mKeyguardSecurityCallback.reset();
+ mKeyguardSecurityCallback.onCancelClicked();
+ });
+ }
+
+ // If there's more than one IME, enable the IME switcher button
+ updateSwitchImeButton();
+
+ // When we the current user is switching, InputMethodManagerService sometimes has not
+ // switched internal state yet here. As a quick workaround, we check the keyboard state
+ // again.
+ // TODO: Remove this workaround by ensuring such a race condition never happens.
+ mMainExecutor.executeDelayed(
+ this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mPasswordEntry.setOnEditorActionListener(null);
+ }
+
+ @Override
+ public boolean needsInput() {
+ return true;
+ }
+
+ @Override
+ void resetState() {
+ mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ mMessageAreaController.setMessage("");
+ final boolean wasDisabled = mPasswordEntry.isEnabled();
+ mView.setPasswordEntryEnabled(true);
+ mView.setPasswordEntryInputEnabled(true);
+ // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
+ if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
+ return;
+ }
+ if (wasDisabled) {
+ mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ // Wait a bit to focus the field so the focusable flag on the window is already set then.
+ mMainExecutor.execute(() -> {
+ if (mView.isShown() && mPasswordEntry.isEnabled()) {
+ mPasswordEntry.requestFocus();
+ if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
+ mInputMethodManager.showSoftInput(
+ mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
+ }
+
+ @Override
+ public void onStartingToHide() {
+ mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
+ }
+
+ private void updateSwitchImeButton() {
+ // If there's more than one IME, enable the IME switcher button
+ final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
+ final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(
+ mInputMethodManager, false);
+ if (wasVisible != shouldBeVisible) {
+ mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
+ }
+
+ // TODO: Check if we still need this hack.
+ // If no icon is visible, reset the start margin on the password field so the text is
+ // still centered.
+ if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
+ android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
+ if (params instanceof MarginLayoutParams) {
+ final MarginLayoutParams mlp = (MarginLayoutParams) params;
+ mlp.setMarginStart(0);
+ mPasswordEntry.setLayoutParams(params);
+ }
+ }
+ }
+
+ /**
+ * Method adapted from com.android.inputmethod.latin.Utils
+ *
+ * @param imm The input method manager
+ * @param shouldIncludeAuxiliarySubtypes
+ * @return true if we have multiple IMEs to choose from
+ */
+ private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
+ final boolean shouldIncludeAuxiliarySubtypes) {
+ final List<InputMethodInfo> enabledImis =
+ imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
+
+ // Number of the filtered IMEs
+ int filteredImisCount = 0;
+
+ for (InputMethodInfo imi : enabledImis) {
+ // We can return true immediately after we find two or more filtered IMEs.
+ if (filteredImisCount > 1) return true;
+ final List<InputMethodSubtype> subtypes =
+ imm.getEnabledInputMethodSubtypeList(imi, true);
+ // IMEs that have no subtypes should be counted.
+ if (subtypes.isEmpty()) {
+ ++filteredImisCount;
+ continue;
+ }
+
+ int auxCount = 0;
+ for (InputMethodSubtype subtype : subtypes) {
+ if (subtype.isAuxiliary()) {
+ ++auxCount;
+ }
+ }
+ final int nonAuxCount = subtypes.size() - auxCount;
+
+ // IMEs that have one or more non-auxiliary subtypes should be counted.
+ // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
+ // subtypes should be counted as well.
+ if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
+ ++filteredImisCount;
+ continue;
+ }
+ }
+
+ return filteredImisCount > 1
+ // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's
+ //enabled input method subtype (The current IME should be LatinIME.)
+ || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index c4a9fcb..bdcf467 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,62 +15,39 @@
*/
package com.android.keyguard;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
-
import android.content.Context;
-import android.content.res.ColorStateList;
import android.graphics.Rect;
-import android.os.AsyncTask;
-import android.os.CountDownTimer;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.widget.LinearLayout;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
-import com.android.internal.widget.LockscreenCredential;
import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import java.util.List;
-
-public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView,
- AppearAnimationCreator<LockPatternView.CellState>,
- EmergencyButton.EmergencyButtonCallback {
+public class KeyguardPatternView extends KeyguardInputView
+ implements AppearAnimationCreator<LockPatternView.CellState> {
private static final String TAG = "SecurityPatternView";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
- // how long before we clear the wrong pattern
- private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
// how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
- // how many cells the user has to cross before we poke the wakelock
- private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
-
// How much we scale up the duration of the disappear animation when the current user is locked
public static final float DISAPPEAR_MULTIPLIER_LOCKED = 1.5f;
// Extra padding, in pixels, that should eat touch events.
private static final int PATTERNS_TOUCH_AREA_EXTENSION = 40;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AppearAnimationUtils mAppearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
@@ -78,11 +55,7 @@
private final Rect mTempRect = new Rect();
private final Rect mLockPatternScreenBounds = new Rect();
- private CountDownTimer mCountdownTimer = null;
- private LockPatternUtils mLockPatternUtils;
- private AsyncTask<?, ?, ?> mPendingLockCheck;
private LockPatternView mLockPatternView;
- private KeyguardSecurityCallback mCallback;
/**
* Keeps track of the last time we poked the wake lock during dispatching of the touch event.
@@ -92,26 +65,9 @@
*/
private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
- /**
- * Useful for clearing out the wrong pattern after a delay
- */
- private Runnable mCancelPatternRunnable = new Runnable() {
- @Override
- public void run() {
- mLockPatternView.clearPattern();
- }
- };
- @VisibleForTesting
KeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
private ViewGroup mContainer;
- private int mDisappearYTranslation;
-
- enum FooterMode {
- Normal,
- ForgotLockPattern,
- VerifyUnlocked
- }
public KeyguardPatternView(Context context) {
this(context, null);
@@ -119,7 +75,6 @@
public KeyguardPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mAppearAnimationUtils = new AppearAnimationUtils(context,
AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 1.5f /* translationScale */,
2.0f /* delayScale */, AnimationUtils.loadInterpolator(
@@ -132,50 +87,16 @@
(long) (125 * DISAPPEAR_MULTIPLIER_LOCKED), 1.2f /* translationScale */,
0.6f /* delayScale */, AnimationUtils.loadInterpolator(
mContext, android.R.interpolator.fast_out_linear_in));
- mDisappearYTranslation = getResources().getDimensionPixelSize(
- R.dimen.disappear_y_translation);
- }
-
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mLockPatternUtils = mLockPatternUtils == null
- ? new LockPatternUtils(mContext) : mLockPatternUtils;
mLockPatternView = findViewById(R.id.lockPatternView);
- mLockPatternView.setSaveEnabled(false);
- mLockPatternView.setOnPatternListener(new UnlockPatternListener());
- mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
-
- // vibrate mode will be the same for the life of this screen
- mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
mContainer = findViewById(R.id.container);
-
- EmergencyButton button = findViewById(R.id.emergency_call_button);
- if (button != null) {
- button.setCallback(this);
- }
-
- View cancelBtn = findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- mCallback.reset();
- mCallback.onCancelClicked();
- });
- }
}
@Override
@@ -185,11 +106,6 @@
}
@Override
- public void onEmergencyButtonClickedWhenInCall() {
- mCallback.reset();
- }
-
- @Override
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
// as long as the user is entering a pattern (i.e sending a touch event that was handled
@@ -217,248 +133,11 @@
}
@Override
- public void reset() {
- // reset lock pattern
- mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
- mLockPatternView.enableInput();
- mLockPatternView.setEnabled(true);
- mLockPatternView.clearPattern();
-
- if (mSecurityMessageDisplay == null) {
- return;
- }
-
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
- if (deadline != 0) {
- handleAttemptLockout(deadline);
- } else {
- displayDefaultSecurityMessage();
- }
- }
-
- private void displayDefaultSecurityMessage() {
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage("");
- }
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public boolean disallowInterceptTouch(MotionEvent event) {
+ boolean disallowInterceptTouch(MotionEvent event) {
return !mLockPatternView.isEmpty()
|| mLockPatternScreenBounds.contains((int) event.getRawX(), (int) event.getRawY());
}
- /** TODO: hook this up */
- public void cleanUp() {
- if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
- mLockPatternUtils = null;
- mLockPatternView.setOnPatternListener(null);
- }
-
- private class UnlockPatternListener implements LockPatternView.OnPatternListener {
-
- @Override
- public void onPatternStart() {
- mLockPatternView.removeCallbacks(mCancelPatternRunnable);
- mSecurityMessageDisplay.setMessage("");
- }
-
- @Override
- public void onPatternCleared() {
- }
-
- @Override
- public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
- mCallback.userActivity();
- mCallback.onUserInput();
- }
-
- @Override
- public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
- mKeyguardUpdateMonitor.setCredentialAttempted();
- mLockPatternView.disableInput();
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- }
-
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
- mLockPatternView.enableInput();
- onPatternChecked(userId, false, 0, false /* not valid - too short */);
- return;
- }
-
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- mPendingLockCheck = LockPatternChecker.checkCredential(
- mLockPatternUtils,
- LockscreenCredential.createPattern(pattern),
- userId,
- new LockPatternChecker.OnCheckCallback() {
-
- @Override
- public void onEarlyMatched() {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL);
- }
- onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
- true /* isValidPattern */);
- }
-
- @Override
- public void onChecked(boolean matched, int timeoutMs) {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- mLockPatternView.enableInput();
- mPendingLockCheck = null;
- if (!matched) {
- onPatternChecked(userId, false /* matched */, timeoutMs,
- true /* isValidPattern */);
- }
- }
-
- @Override
- public void onCancelled() {
- // We already got dismissed with the early matched callback, so we
- // cancelled the check. However, we still need to note down the latency.
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- }
- });
- if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
- mCallback.userActivity();
- mCallback.onUserInput();
- }
- }
-
- private void onPatternChecked(int userId, boolean matched, int timeoutMs,
- boolean isValidPattern) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
- if (matched) {
- mCallback.reportUnlockAttempt(userId, true, 0);
- if (dismissKeyguard) {
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
- mCallback.dismiss(true, userId);
- }
- } else {
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
- if (isValidPattern) {
- mCallback.reportUnlockAttempt(userId, false, timeoutMs);
- if (timeoutMs > 0) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
- userId, timeoutMs);
- handleAttemptLockout(deadline);
- }
- }
- if (timeoutMs == 0) {
- mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern);
- mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
- }
- }
- }
- }
-
- private void handleAttemptLockout(long elapsedRealtimeDeadline) {
- mLockPatternView.clearPattern();
- mLockPatternView.setEnabled(false);
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long secondsInFuture = (long) Math.ceil(
- (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
- mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- final int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
- mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString(
- R.plurals.kg_too_many_failed_attempts_countdown,
- secondsRemaining, secondsRemaining));
- }
-
- @Override
- public void onFinish() {
- mLockPatternView.setEnabled(true);
- displayDefaultSecurityMessage();
- }
-
- }.start();
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void onPause() {
- if (mCountdownTimer != null) {
- mCountdownTimer.cancel();
- mCountdownTimer = null;
- }
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- mPendingLockCheck = null;
- }
- displayDefaultSecurityMessage();
- }
-
- @Override
- public void onResume(int reason) {
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void showPromptReason(int reason) {
- switch (reason) {
- case PROMPT_REASON_RESTART:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_restart_pattern);
- break;
- case PROMPT_REASON_TIMEOUT:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- case PROMPT_REASON_DEVICE_ADMIN:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_device_admin);
- break;
- case PROMPT_REASON_USER_REQUEST:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request);
- break;
- case PROMPT_REASON_PREPARE_FOR_UPDATE:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- case PROMPT_REASON_NONE:
- break;
- default:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- }
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (colorState != null) {
- mSecurityMessageDisplay.setNextMessageColor(colorState);
- }
- mSecurityMessageDisplay.setMessage(message);
- }
-
- @Override
public void startAppearAnimation() {
enableClipping(false);
setAlpha(1f);
@@ -467,12 +146,7 @@
0, mAppearAnimationUtils.getInterpolator());
mAppearAnimationUtils.startAnimation2d(
mLockPatternView.getCellStates(),
- new Runnable() {
- @Override
- public void run() {
- enableClipping(true);
- }
- },
+ () -> enableClipping(true),
this);
if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
@@ -484,11 +158,9 @@
}
}
- @Override
- public boolean startDisappearAnimation(final Runnable finishRunnable) {
- float durationMultiplier = mKeyguardUpdateMonitor.needsSlowUnlockTransition()
- ? DISAPPEAR_MULTIPLIER_LOCKED
- : 1f;
+ public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
+ final Runnable finishRunnable) {
+ float durationMultiplier = needsSlowUnlockTransition ? DISAPPEAR_MULTIPLIER_LOCKED : 1f;
mLockPatternView.clearPattern();
enableClipping(false);
setTranslationY(0);
@@ -497,10 +169,8 @@
-mDisappearAnimationUtils.getStartTranslation(),
mDisappearAnimationUtils.getInterpolator());
- DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor
- .needsSlowUnlockTransition()
- ? mDisappearAnimationUtilsLocked
- : mDisappearAnimationUtils;
+ DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
+ ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
() -> {
enableClipping(true);
@@ -549,7 +219,7 @@
@Override
public CharSequence getTitle() {
- return getContext().getString(
+ return getResources().getString(
com.android.internal.R.string.keyguard_accessibility_pattern_unlock);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
new file mode 100644
index 0000000..3db9db7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+
+import android.content.res.ColorStateList;
+import android.os.AsyncTask;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.view.View;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockPatternView.Cell;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+import java.util.List;
+
+public class KeyguardPatternViewController
+ extends KeyguardInputViewController<KeyguardPatternView> {
+
+ // how many cells the user has to cross before we poke the wakelock
+ private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
+
+ // how long before we clear the wrong pattern
+ private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
+
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final LatencyTracker mLatencyTracker;
+ private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
+
+ private KeyguardMessageAreaController mMessageAreaController;
+ private LockPatternView mLockPatternView;
+ private CountDownTimer mCountdownTimer;
+ private AsyncTask<?, ?, ?> mPendingLockCheck;
+
+ private EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
+ @Override
+ public void onEmergencyButtonClickedWhenInCall() {
+ getKeyguardSecurityCallback().reset();
+ }
+ };
+
+ /**
+ * Useful for clearing out the wrong pattern after a delay
+ */
+ private Runnable mCancelPatternRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLockPatternView.clearPattern();
+ }
+ };
+
+ private class UnlockPatternListener implements LockPatternView.OnPatternListener {
+
+ @Override
+ public void onPatternStart() {
+ mLockPatternView.removeCallbacks(mCancelPatternRunnable);
+ mMessageAreaController.setMessage("");
+ }
+
+ @Override
+ public void onPatternCleared() {
+ }
+
+ @Override
+ public void onPatternCellAdded(List<Cell> pattern) {
+ getKeyguardSecurityCallback().userActivity();
+ getKeyguardSecurityCallback().onUserInput();
+ }
+
+ @Override
+ public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
+ mKeyguardUpdateMonitor.setCredentialAttempted();
+ mLockPatternView.disableInput();
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ }
+
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+ mLockPatternView.enableInput();
+ onPatternChecked(userId, false, 0, false /* not valid - too short */);
+ return;
+ }
+
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ mPendingLockCheck = LockPatternChecker.checkCredential(
+ mLockPatternUtils,
+ LockscreenCredential.createPattern(pattern),
+ userId,
+ new LockPatternChecker.OnCheckCallback() {
+
+ @Override
+ public void onEarlyMatched() {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);
+ onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
+ true /* isValidPattern */);
+ }
+
+ @Override
+ public void onChecked(boolean matched, int timeoutMs) {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ mLockPatternView.enableInput();
+ mPendingLockCheck = null;
+ if (!matched) {
+ onPatternChecked(userId, false /* matched */, timeoutMs,
+ true /* isValidPattern */);
+ }
+ }
+
+ @Override
+ public void onCancelled() {
+ // We already got dismissed with the early matched callback, so we
+ // cancelled the check. However, we still need to note down the latency.
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ }
+ });
+ if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
+ getKeyguardSecurityCallback().userActivity();
+ getKeyguardSecurityCallback().onUserInput();
+ }
+ }
+
+ private void onPatternChecked(int userId, boolean matched, int timeoutMs,
+ boolean isValidPattern) {
+ boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ if (matched) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
+ if (dismissKeyguard) {
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
+ getKeyguardSecurityCallback().dismiss(true, userId);
+ }
+ } else {
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
+ if (isValidPattern) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
+ if (timeoutMs > 0) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
+ userId, timeoutMs);
+ handleAttemptLockout(deadline);
+ }
+ }
+ if (timeoutMs == 0) {
+ mMessageAreaController.setMessage(R.string.kg_wrong_pattern);
+ mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
+ }
+ }
+ }
+ }
+
+ protected KeyguardPatternViewController(KeyguardPatternView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ LatencyTracker latencyTracker,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
+ super(view, securityMode, keyguardSecurityCallback);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mLatencyTracker = latencyTracker;
+ mMessageAreaControllerFactory = messageAreaControllerFactory;
+ KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
+ mMessageAreaController = mMessageAreaControllerFactory.create(kma);
+ mLockPatternView = mView.findViewById(R.id.lockPatternView);
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ mMessageAreaController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mLockPatternView.setOnPatternListener(new UnlockPatternListener());
+ mLockPatternView.setSaveEnabled(false);
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ // vibrate mode will be the same for the life of this screen
+ mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+
+ EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(mEmergencyButtonCallback);
+ }
+
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ getKeyguardSecurityCallback().reset();
+ getKeyguardSecurityCallback().onCancelClicked();
+ });
+ }
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mLockPatternView.setOnPatternListener(null);
+ EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(null);
+ }
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(null);
+ }
+ }
+
+ @Override
+ public void reset() {
+ // reset lock pattern
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ mLockPatternView.enableInput();
+ mLockPatternView.setEnabled(true);
+ mLockPatternView.clearPattern();
+
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
+ } else {
+ displayDefaultSecurityMessage();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ mPendingLockCheck = null;
+ }
+ displayDefaultSecurityMessage();
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ /// TODO: move all this logic into the MessageAreaController?
+ switch (reason) {
+ case PROMPT_REASON_RESTART:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_restart_pattern);
+ break;
+ case PROMPT_REASON_TIMEOUT:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ case PROMPT_REASON_DEVICE_ADMIN:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_device_admin);
+ break;
+ case PROMPT_REASON_USER_REQUEST:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_user_request);
+ break;
+ case PROMPT_REASON_PREPARE_FOR_UPDATE:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ case PROMPT_REASON_NONE:
+ break;
+ default:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ }
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (colorState != null) {
+ mMessageAreaController.setNextMessageColor(colorState);
+ }
+ mMessageAreaController.setMessage(message);
+ }
+
+ @Override
+ public void startAppearAnimation() {
+ super.startAppearAnimation();
+ }
+
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return mView.startDisappearAnimation(
+ mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
+ }
+
+ private void displayDefaultSecurityMessage() {
+ mMessageAreaController.setMessage("");
+ }
+
+ private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mLockPatternView.clearPattern();
+ mLockPatternView.setEnabled(false);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long secondsInFuture = (long) Math.ceil(
+ (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
+ mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ final int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
+ mMessageAreaController.setMessage(mView.getResources().getQuantityString(
+ R.plurals.kg_too_many_failed_attempts_countdown,
+ secondsRemaining, secondsRemaining));
+ }
+
+ @Override
+ public void onFinish() {
+ mLockPatternView.setEnabled(true);
+ displayDefaultSecurityMessage();
+ }
+
+ }.start();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index c7f27cf..7fa43116 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -16,11 +16,17 @@
package com.android.keyguard;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.View;
import com.android.internal.widget.LockscreenCredential;
@@ -29,22 +35,12 @@
/**
* A Pin based Keyguard input view
*/
-public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
- implements View.OnKeyListener, View.OnTouchListener {
+public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView {
protected PasswordTextView mPasswordEntry;
private View mOkButton;
private View mDeleteButton;
- private View mButton0;
- private View mButton1;
- private View mButton2;
- private View mButton3;
- private View mButton4;
- private View mButton5;
- private View mButton6;
- private View mButton7;
- private View mButton8;
- private View mButton9;
+ private View[] mButtons = new View[10];
public KeyguardPinBasedInputView(Context context) {
this(context, null);
@@ -62,7 +58,6 @@
@Override
protected void resetState() {
- setPasswordEntryEnabled(true);
}
@Override
@@ -86,10 +81,10 @@
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.isConfirmKey(keyCode)) {
- performClick(mOkButton);
+ mOkButton.performClick();
return true;
} else if (keyCode == KeyEvent.KEYCODE_DEL) {
- performClick(mDeleteButton);
+ mDeleteButton.performClick();
return true;
}
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
@@ -125,42 +120,9 @@
}
}
- private void performClick(View view) {
- view.performClick();
- }
-
private void performNumberClick(int number) {
- switch (number) {
- case 0:
- performClick(mButton0);
- break;
- case 1:
- performClick(mButton1);
- break;
- case 2:
- performClick(mButton2);
- break;
- case 3:
- performClick(mButton3);
- break;
- case 4:
- performClick(mButton4);
- break;
- case 5:
- performClick(mButton5);
- break;
- case 6:
- performClick(mButton6);
- break;
- case 7:
- performClick(mButton7);
- break;
- case 8:
- performClick(mButton8);
- break;
- case 9:
- performClick(mButton9);
- break;
+ if (number >= 0 && number <= 9) {
+ mButtons[number].performClick();
}
}
@@ -177,94 +139,31 @@
@Override
protected void onFinishInflate() {
mPasswordEntry = findViewById(getPasswordTextViewId());
- mPasswordEntry.setOnKeyListener(this);
// Set selected property on so the view can send accessibility events.
mPasswordEntry.setSelected(true);
- mPasswordEntry.setUserActivityListener(new PasswordTextView.UserActivityListener() {
- @Override
- public void onUserActivity() {
- onUserInput();
- }
- });
-
mOkButton = findViewById(R.id.key_enter);
- if (mOkButton != null) {
- mOkButton.setOnTouchListener(this);
- mOkButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mPasswordEntry.isEnabled()) {
- verifyPasswordAndUnlock();
- }
- }
- });
- mOkButton.setOnHoverListener(new LiftToActivateListener(getContext()));
- }
mDeleteButton = findViewById(R.id.delete_button);
mDeleteButton.setVisibility(View.VISIBLE);
- mDeleteButton.setOnTouchListener(this);
- mDeleteButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // check for time-based lockouts
- if (mPasswordEntry.isEnabled()) {
- mPasswordEntry.deleteLastChar();
- }
- }
- });
- mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- // check for time-based lockouts
- if (mPasswordEntry.isEnabled()) {
- resetPasswordText(true /* animate */, true /* announce */);
- }
- doHapticKeyClick();
- return true;
- }
- });
- mButton0 = findViewById(R.id.key0);
- mButton1 = findViewById(R.id.key1);
- mButton2 = findViewById(R.id.key2);
- mButton3 = findViewById(R.id.key3);
- mButton4 = findViewById(R.id.key4);
- mButton5 = findViewById(R.id.key5);
- mButton6 = findViewById(R.id.key6);
- mButton7 = findViewById(R.id.key7);
- mButton8 = findViewById(R.id.key8);
- mButton9 = findViewById(R.id.key9);
+ mButtons[0] = findViewById(R.id.key0);
+ mButtons[1] = findViewById(R.id.key1);
+ mButtons[2] = findViewById(R.id.key2);
+ mButtons[3] = findViewById(R.id.key3);
+ mButtons[4] = findViewById(R.id.key4);
+ mButtons[5] = findViewById(R.id.key5);
+ mButtons[6] = findViewById(R.id.key6);
+ mButtons[7] = findViewById(R.id.key7);
+ mButtons[8] = findViewById(R.id.key8);
+ mButtons[9] = findViewById(R.id.key9);
mPasswordEntry.requestFocus();
super.onFinishInflate();
}
@Override
- public void onResume(int reason) {
- super.onResume(reason);
- mPasswordEntry.requestFocus();
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- doHapticKeyClick();
- }
- return false;
- }
-
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- return onKeyDown(keyCode, event);
- }
- return false;
- }
-
- @Override
public CharSequence getTitle() {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_pin_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
new file mode 100644
index 0000000..4d0ebff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnKeyListener;
+import android.view.View.OnTouchListener;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
+ extends KeyguardAbsKeyInputViewController<T> {
+
+ private final LiftToActivateListener mLiftToActivateListener;
+ protected PasswordTextView mPasswordEntry;
+
+ private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ return mView.onKeyDown(keyCode, event);
+ }
+ return false;
+ };
+
+ private final OnTouchListener mOnTouchListener = (v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mView.doHapticKeyClick();
+ }
+ return false;
+ };
+
+ protected KeyguardPinBasedInputViewController(T view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker);
+ mLiftToActivateListener = liftToActivateListener;
+ mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+
+ mPasswordEntry.setOnKeyListener(mOnKeyListener);
+ mPasswordEntry.setUserActivityListener(this::onUserInput);
+
+ View deleteButton = mView.findViewById(R.id.delete_button);
+ deleteButton.setOnTouchListener(mOnTouchListener);
+ deleteButton.setOnClickListener(v -> {
+ // check for time-based lockouts
+ if (mPasswordEntry.isEnabled()) {
+ mPasswordEntry.deleteLastChar();
+ }
+ });
+ deleteButton.setOnLongClickListener(v -> {
+ // check for time-based lockouts
+ if (mPasswordEntry.isEnabled()) {
+ mView.resetPasswordText(true /* animate */, true /* announce */);
+ }
+ mView.doHapticKeyClick();
+ return true;
+ });
+
+ View okButton = mView.findViewById(R.id.key_enter);
+ if (okButton != null) {
+ okButton.setOnTouchListener(mOnTouchListener);
+ okButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mPasswordEntry.isEnabled()) {
+ verifyPasswordAndUnlock();
+ }
+ }
+ });
+ okButton.setOnHoverListener(mLiftToActivateListener);
+ }
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ mPasswordEntry.requestFocus();
+ }
+
+ @Override
+ void resetState() {
+ mView.setPasswordEntryEnabled(true);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
new file mode 100644
index 0000000..6769436
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.view.View;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public class KeyguardPinViewController
+ extends KeyguardPinBasedInputViewController<KeyguardPINView> {
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+ protected KeyguardPinViewController(KeyguardPINView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode, LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ getKeyguardSecurityCallback().reset();
+ getKeyguardSecurityCallback().onCancelClicked();
+ });
+ }
+ }
+
+ @Override
+ void resetState() {
+ super.resetState();
+ mMessageAreaController.setMessage("");
+ }
+
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return mView.startDisappearAnimation(
+ mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 81d37a8..b62ea6b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -19,8 +19,6 @@
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
-
import static java.lang.Integer.max;
import android.animation.Animator;
@@ -28,25 +26,14 @@
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.Intent;
-import android.content.res.ColorStateList;
import android.graphics.Insets;
import android.graphics.Rect;
-import android.metrics.LogMaker;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.MathUtils;
-import android.util.Slog;
import android.util.TypedValue;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
-import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
@@ -61,42 +48,30 @@
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
-import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.InjectionInflationController;
import java.util.List;
-public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
- private static final boolean DEBUG = KeyguardConstants.DEBUG;
- private static final String TAG = "KeyguardSecurityView";
-
- private static final int USER_TYPE_PRIMARY = 1;
- private static final int USER_TYPE_WORK_PROFILE = 2;
- private static final int USER_TYPE_SECONDARY_USER = 3;
+public class KeyguardSecurityContainer extends FrameLayout {
+ static final int USER_TYPE_PRIMARY = 1;
+ static final int USER_TYPE_WORK_PROFILE = 2;
+ static final int USER_TYPE_SECONDARY_USER = 3;
// Bouncer is dismissed due to no security.
- private static final int BOUNCER_DISMISS_NONE_SECURITY = 0;
+ static final int BOUNCER_DISMISS_NONE_SECURITY = 0;
// Bouncer is dismissed due to pin, password or pattern entered.
- private static final int BOUNCER_DISMISS_PASSWORD = 1;
+ static final int BOUNCER_DISMISS_PASSWORD = 1;
// Bouncer is dismissed due to biometric (face, fingerprint or iris) authenticated.
- private static final int BOUNCER_DISMISS_BIOMETRIC = 2;
+ static final int BOUNCER_DISMISS_BIOMETRIC = 2;
// Bouncer is dismissed due to extended access granted.
- private static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
+ static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
// Bouncer is dismissed due to sim card unlock code entered.
- private static final int BOUNCER_DISMISS_SIM = 4;
+ static final int BOUNCER_DISMISS_SIM = 4;
// Make the view move slower than the finger, as if the spring were applying force.
private static final float TOUCH_Y_MULTIPLIER = 0.25f;
@@ -105,36 +80,23 @@
// How much to scale the default slop by, to avoid accidental drags.
private static final float SLOP_SCALE = 4f;
- private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
-
private static final long IME_DISAPPEAR_DURATION_MS = 125;
- private KeyguardSecurityModel mSecurityModel;
- private LockPatternUtils mLockPatternUtils;
-
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
- private boolean mIsVerifyUnlockOnly;
- private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
- private KeyguardSecurityView mCurrentSecurityView;
- private SecurityCallback mSecurityCallback;
private AlertDialog mAlertDialog;
- private InjectionInflationController mInjectionInflationController;
private boolean mSwipeUpToRetry;
- private AdminSecondaryLockScreenController mSecondaryLockScreenController;
private final ViewConfiguration mViewConfiguration;
private final SpringAnimation mSpringAnimation;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
- private final KeyguardUpdateMonitor mUpdateMonitor;
- private final KeyguardStateController mKeyguardStateController;
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private float mLastTouchY = -1;
private int mActivePointerId = -1;
private boolean mIsDragging;
private float mStartTouchY = -1;
private boolean mDisappearAnimRunning;
+ private SwipeListener mSwipeListener;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -186,19 +148,22 @@
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
- public boolean dismiss(boolean authenticated, int targetUserId,
- boolean bypassSecondaryLockScreen);
- public void userActivity();
- public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
+ boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
+ void userActivity();
+ void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
/**
* @param strongAuth wheher the user has authenticated with strong authentication like
* pattern, password or PIN but not by trust agents or fingerprint
* @param targetUserId a user that needs to be the foreground user at the finish completion.
*/
- public void finish(boolean strongAuth, int targetUserId);
- public void reset();
- public void onCancelClicked();
+ void finish(boolean strongAuth, int targetUserId);
+ void reset();
+ void onCancelClicked();
+ }
+
+ public interface SwipeListener {
+ void onSwipeUp();
}
@VisibleForTesting
@@ -249,52 +214,24 @@
public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mSecurityModel = Dependency.get(KeyguardSecurityModel.class);
- mLockPatternUtils = new LockPatternUtils(context);
- mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
- mInjectionInflationController = new InjectionInflationController(
- SystemUIFactory.getInstance().getSysUIComponent().createViewInstanceCreatorFactory());
mViewConfiguration = ViewConfiguration.get(context);
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this,
- mUpdateMonitor, mCallback, new Handler(Looper.myLooper()));
}
- public void setSecurityCallback(SecurityCallback callback) {
- mSecurityCallback = callback;
- }
-
- @Override
- public void onResume(int reason) {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).onResume(reason);
- }
+ void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
- updateBiometricRetry();
+ updateBiometricRetry(securityMode, faceAuthEnabled);
}
- @Override
public void onPause() {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
mAlertDialog = null;
}
- mSecondaryLockScreenController.hide();
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).onPause();
- }
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
}
@Override
- public void onStartingToHide() {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).onStartingToHide();
- }
- }
-
- @Override
public boolean shouldDelayChildPressedState() {
return true;
}
@@ -316,13 +253,12 @@
return false;
}
// Avoid dragging the pattern view
- if (mCurrentSecurityView.disallowInterceptTouch(event)) {
+ if (mSecurityViewFlipper.getSecurityView().disallowInterceptTouch(event)) {
return false;
}
int index = event.findPointerIndex(mActivePointerId);
float touchSlop = mViewConfiguration.getScaledTouchSlop() * SLOP_SCALE;
- if (mCurrentSecurityView != null && index != -1
- && mStartTouchY - event.getY(index) > touchSlop) {
+ if (index != -1 && mStartTouchY - event.getY(index) > touchSlop) {
mIsDragging = true;
return true;
}
@@ -370,31 +306,28 @@
}
if (action == MotionEvent.ACTION_UP) {
if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- MIN_DRAG_SIZE, getResources().getDisplayMetrics())
- && !mUpdateMonitor.isFaceDetectionRunning()) {
- mUpdateMonitor.requestFaceAuth();
- mCallback.userActivity();
- showMessage(null, null);
+ MIN_DRAG_SIZE, getResources().getDisplayMetrics())) {
+ if (mSwipeListener != null) {
+ mSwipeListener.onSwipeUp();
+ }
}
}
return true;
}
+ void setSwipeListener(SwipeListener swipeListener) {
+ mSwipeListener = swipeListener;
+ }
+
private void startSpringAnimation(float startVelocity) {
mSpringAnimation
.setStartVelocity(startVelocity)
.animateToFinalPosition(0);
}
- public void startAppearAnimation() {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
- }
- }
-
- public boolean startDisappearAnimation(Runnable onFinishRunnable) {
+ public void startDisappearAnimation(SecurityMode securitySelection) {
mDisappearAnimRunning = true;
- if (mCurrentSecuritySelection == SecurityMode.Password) {
+ if (securitySelection == SecurityMode.Password) {
mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
IME_DISAPPEAR_DURATION_MS,
Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
@@ -439,19 +372,13 @@
}
});
}
- if (mCurrentSecuritySelection != SecurityMode.None) {
- return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
- onFinishRunnable);
- }
- return false;
}
/**
* Enables/disables swipe up to retry on the bouncer.
*/
- private void updateBiometricRetry() {
- SecurityMode securityMode = getSecurityMode();
- mSwipeUpToRetry = mKeyguardStateController.isFaceAuthEnabled()
+ private void updateBiometricRetry(SecurityMode securityMode, boolean faceAuthEnabled) {
+ mSwipeUpToRetry = faceAuthEnabled
&& securityMode != SecurityMode.SimPin
&& securityMode != SecurityMode.SimPuk
&& securityMode != SecurityMode.None;
@@ -461,53 +388,11 @@
return mSecurityViewFlipper.getTitle();
}
- @VisibleForTesting
- protected KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
- final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
- KeyguardSecurityView view = null;
- final int children = mSecurityViewFlipper.getChildCount();
- for (int child = 0; child < children; child++) {
- if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
- view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
- break;
- }
- }
- int layoutId = getLayoutIdFor(securityMode);
- if (view == null && layoutId != 0) {
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
- View v = mInjectionInflationController.injectable(inflater)
- .inflate(layoutId, mSecurityViewFlipper, false);
- mSecurityViewFlipper.addView(v);
- updateSecurityView(v);
- view = (KeyguardSecurityView)v;
- view.reset();
- }
-
- return view;
- }
-
- private void updateSecurityView(View view) {
- if (view instanceof KeyguardSecurityView) {
- KeyguardSecurityView ksv = (KeyguardSecurityView) view;
- ksv.setKeyguardCallback(mCallback);
- ksv.setLockPatternUtils(mLockPatternUtils);
- } else {
- Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
- }
- }
@Override
public void onFinishInflate() {
super.onFinishInflate();
mSecurityViewFlipper = findViewById(R.id.view_flipper);
- mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
- }
-
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- mSecurityModel.setLockPatternUtils(utils);
- mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
}
@Override
@@ -539,11 +424,12 @@
mAlertDialog.show();
}
- private void showTimeoutDialog(int userId, int timeoutMs) {
- int timeoutInSeconds = (int) timeoutMs / 1000;
+ void showTimeoutDialog(int userId, int timeoutMs, LockPatternUtils lockPatternUtils,
+ SecurityMode securityMode) {
+ int timeoutInSeconds = timeoutMs / 1000;
int messageId = 0;
- switch (mSecurityModel.getSecurityMode(userId)) {
+ switch (securityMode) {
case Pattern:
messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
break;
@@ -563,13 +449,13 @@
if (messageId != 0) {
final String message = mContext.getString(messageId,
- mLockPatternUtils.getCurrentFailedPasswordAttempts(userId),
+ lockPatternUtils.getCurrentFailedPasswordAttempts(userId),
timeoutInSeconds);
showDialog(null, message);
}
}
- private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
+ void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
String message = null;
switch (userType) {
case USER_TYPE_PRIMARY:
@@ -588,7 +474,7 @@
showDialog(null, message);
}
- private void showWipeDialog(int attempts, int userType) {
+ void showWipeDialog(int attempts, int userType) {
String message = null;
switch (userType) {
case USER_TYPE_PRIMARY:
@@ -607,358 +493,8 @@
showDialog(null, message);
}
- private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
- // +1 for this time
- final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
-
- if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
-
- final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
- final int failedAttemptsBeforeWipe =
- dpm.getMaximumFailedPasswordsForWipe(null, userId);
-
- final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
- (failedAttemptsBeforeWipe - failedAttempts)
- : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
- if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
- // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
- // N attempts. Once we get below the grace period, we post this dialog every time as a
- // clear warning until the deletion fires.
- // Check which profile has the strictest policy for failed password attempts
- final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
- int userType = USER_TYPE_PRIMARY;
- if (expiringUser == userId) {
- // TODO: http://b/23522538
- if (expiringUser != UserHandle.USER_SYSTEM) {
- userType = USER_TYPE_SECONDARY_USER;
- }
- } else if (expiringUser != UserHandle.USER_NULL) {
- userType = USER_TYPE_WORK_PROFILE;
- } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
- if (remainingBeforeWipe > 0) {
- showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
- } else {
- // Too many attempts. The device will be wiped shortly.
- Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
- showWipeDialog(failedAttempts, userType);
- }
- }
- mLockPatternUtils.reportFailedPasswordAttempt(userId);
- if (timeoutMs > 0) {
- mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
- showTimeoutDialog(userId, timeoutMs);
- }
- }
-
- /**
- * Shows the primary security screen for the user. This will be either the multi-selector
- * or the user's security method.
- * @param turningOff true if the device is being turned off
- */
- void showPrimarySecurityScreen(boolean turningOff) {
- SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()));
- if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
- showSecurityScreen(securityMode);
- }
-
- /**
- * Shows the next security screen if there is one.
- * @param authenticated true if the user entered the correct authentication
- * @param targetUserId a user that needs to be the foreground user at the finish (if called)
- * completion.
- * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
- * secondary lock screen requirement, if any.
- * @return true if keyguard is done
- */
- boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
- boolean bypassSecondaryLockScreen) {
- if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
- boolean finish = false;
- boolean strongAuth = false;
- int eventSubtype = -1;
- BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
- if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS;
- } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC;
- } else if (SecurityMode.None == mCurrentSecuritySelection) {
- SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (SecurityMode.None == securityMode) {
- finish = true; // no security required
- eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY;
- } else {
- showSecurityScreen(securityMode); // switch to the alternate security view
- }
- } else if (authenticated) {
- switch (mCurrentSecuritySelection) {
- case Pattern:
- case Password:
- case PIN:
- strongAuth = true;
- finish = true;
- eventSubtype = BOUNCER_DISMISS_PASSWORD;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD;
- break;
-
- case SimPin:
- case SimPuk:
- // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
- SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser())) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_SIM;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
- } else {
- showSecurityScreen(securityMode);
- }
- break;
-
- default:
- Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
- showPrimarySecurityScreen(false);
- break;
- }
- }
- // Check for device admin specified additional security measures.
- if (finish && !bypassSecondaryLockScreen) {
- Intent secondaryLockscreenIntent =
- mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
- if (secondaryLockscreenIntent != null) {
- mSecondaryLockScreenController.show(secondaryLockscreenIntent);
- return false;
- }
- }
- if (eventSubtype != -1) {
- mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
- .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
- }
- if (uiEvent != BouncerUiEvent.UNKNOWN) {
- sUiEventLogger.log(uiEvent);
- }
- if (finish) {
- mSecurityCallback.finish(strongAuth, targetUserId);
- }
- return finish;
- }
-
- /**
- * Switches to the given security view unless it's already being shown, in which case
- * this is a no-op.
- *
- * @param securityMode
- */
- private void showSecurityScreen(SecurityMode securityMode) {
- if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
-
- if (securityMode == mCurrentSecuritySelection) return;
-
- KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
- KeyguardSecurityView newView = getSecurityView(securityMode);
-
- // Emulate Activity life cycle
- if (oldView != null) {
- oldView.onPause();
- oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
- }
- if (securityMode != SecurityMode.None) {
- newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
- newView.setKeyguardCallback(mCallback);
- }
-
- // Find and show this child.
- final int childCount = mSecurityViewFlipper.getChildCount();
-
- final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
- for (int i = 0; i < childCount; i++) {
- if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
- mSecurityViewFlipper.setDisplayedChild(i);
- break;
- }
- }
-
- mCurrentSecuritySelection = securityMode;
- mCurrentSecurityView = newView;
- mSecurityCallback.onSecurityModeChanged(securityMode,
- securityMode != SecurityMode.None && newView.needsInput());
- }
-
- private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
- public void userActivity() {
- if (mSecurityCallback != null) {
- mSecurityCallback.userActivity();
- }
- }
-
- @Override
- public void onUserInput() {
- mUpdateMonitor.cancelFaceAuth();
- }
-
- @Override
- public void dismiss(boolean authenticated, int targetId) {
- dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
- }
-
- @Override
- public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) {
- mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
- }
-
- public boolean isVerifyUnlockOnly() {
- return mIsVerifyUnlockOnly;
- }
-
- public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
- if (success) {
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
- mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
- // Force a garbage collection in an attempt to erase any lockscreen password left in
- // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
- // dismiss animation janky.
- ThreadUtils.postOnBackgroundThread(() -> {
- try {
- Thread.sleep(5000);
- } catch (InterruptedException ignored) { }
- Runtime.getRuntime().gc();
- });
- } else {
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
- KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
- }
- mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
- .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
- sUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
- : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE);
- }
-
- public void reset() {
- mSecurityCallback.reset();
- }
-
- public void onCancelClicked() {
- mSecurityCallback.onCancelClicked();
- }
- };
-
- // The following is used to ignore callbacks from SecurityViews that are no longer current
- // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
- // state for the current security method.
- private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
- @Override
- public void userActivity() { }
- @Override
- public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
- @Override
- public boolean isVerifyUnlockOnly() { return false; }
- @Override
- public void dismiss(boolean securityVerified, int targetUserId) { }
- @Override
- public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) { }
- @Override
- public void onUserInput() { }
- @Override
- public void reset() {}
- };
-
- private int getSecurityViewIdForMode(SecurityMode securityMode) {
- switch (securityMode) {
- case Pattern: return R.id.keyguard_pattern_view;
- case PIN: return R.id.keyguard_pin_view;
- case Password: return R.id.keyguard_password_view;
- case SimPin: return R.id.keyguard_sim_pin_view;
- case SimPuk: return R.id.keyguard_sim_puk_view;
- }
- return 0;
- }
-
- @VisibleForTesting
- public int getLayoutIdFor(SecurityMode securityMode) {
- switch (securityMode) {
- case Pattern: return R.layout.keyguard_pattern_view;
- case PIN: return R.layout.keyguard_pin_view;
- case Password: return R.layout.keyguard_password_view;
- case SimPin: return R.layout.keyguard_sim_pin_view;
- case SimPuk: return R.layout.keyguard_sim_puk_view;
- default:
- return 0;
- }
- }
-
- public SecurityMode getSecurityMode() {
- return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser());
- }
-
- public SecurityMode getCurrentSecurityMode() {
- return mCurrentSecuritySelection;
- }
-
- public KeyguardSecurityView getCurrentSecurityView() {
- return mCurrentSecurityView;
- }
-
- public void verifyUnlock() {
- mIsVerifyUnlockOnly = true;
- showSecurityScreen(getSecurityMode());
- }
-
- public SecurityMode getCurrentSecuritySelection() {
- return mCurrentSecuritySelection;
- }
-
- public void dismiss(boolean authenticated, int targetUserId) {
- mCallback.dismiss(authenticated, targetUserId);
- }
-
- public boolean needsInput() {
- return mSecurityViewFlipper.needsInput();
- }
-
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mSecurityViewFlipper.setKeyguardCallback(callback);
- }
-
- @Override
public void reset() {
- mSecurityViewFlipper.reset();
mDisappearAnimRunning = false;
}
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mSecurityViewFlipper.getCallback();
- }
-
- @Override
- public void showPromptReason(int reason) {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- if (reason != PROMPT_REASON_NONE) {
- Log.i(TAG, "Strong auth required, reason: " + reason);
- }
- getSecurityView(mCurrentSecuritySelection).showPromptReason(reason);
- }
- }
-
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState);
- }
- }
-
- @Override
- public void showUsabilityHint() {
- mSecurityViewFlipper.showUsabilityHint();
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 17f25bd08ef..1c23605 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -16,33 +16,167 @@
package com.android.keyguard;
-import android.content.res.ColorStateList;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_PASSWORD;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_SIM;
+import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_PRIMARY;
+import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER;
+import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE;
+import static com.android.systemui.DejankUtils.whitelistIpcs;
+import android.app.admin.DevicePolicyManager;
+import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.metrics.LogMaker;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityContainer.BouncerUiEvent;
import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
+import com.android.keyguard.KeyguardSecurityContainer.SwipeListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
/** Controller for {@link KeyguardSecurityContainer} */
-public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> {
+@KeyguardBouncerScope
+public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer>
+ implements KeyguardSecurityView {
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final String TAG = "KeyguardSecurityView";
+
+ private final AdminSecondaryLockScreenController mAdminSecondaryLockScreenController;
private final LockPatternUtils mLockPatternUtils;
- private final KeyguardSecurityViewController.Factory mKeyguardSecurityViewControllerFactory;
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final KeyguardSecurityModel mSecurityModel;
+ private final MetricsLogger mMetricsLogger;
+ private final UiEventLogger mUiEventLogger;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
+ private final SecurityCallback mSecurityCallback;
- @Inject
- KeyguardSecurityContainerController(KeyguardSecurityContainer view,
+ private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
+
+ private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() {
+ public void userActivity() {
+ if (mSecurityCallback != null) {
+ mSecurityCallback.userActivity();
+ }
+ }
+
+ @Override
+ public void onUserInput() {
+ mUpdateMonitor.cancelFaceAuth();
+ }
+
+ @Override
+ public void dismiss(boolean authenticated, int targetId) {
+ dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
+ }
+
+ @Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) {
+ mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
+ }
+
+ public boolean isVerifyUnlockOnly() {
+ return false;
+ }
+
+ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
+ if (success) {
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
+ mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
+ // Force a garbage collection in an attempt to erase any lockscreen password left in
+ // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
+ // dismiss animation janky.
+ ThreadUtils.postOnBackgroundThread(() -> {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException ignored) { }
+ Runtime.getRuntime().gc();
+ });
+ } else {
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
+ reportFailedUnlockAttempt(userId, timeoutMs);
+ }
+ mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
+ .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
+ mUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
+ : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE);
+ }
+
+ public void reset() {
+ mSecurityCallback.reset();
+ }
+
+ public void onCancelClicked() {
+ mSecurityCallback.onCancelClicked();
+ }
+ };
+
+
+ private SwipeListener mSwipeListener = new SwipeListener() {
+ @Override
+ public void onSwipeUp() {
+ if (!mUpdateMonitor.isFaceDetectionRunning()) {
+ mUpdateMonitor.requestFaceAuth();
+ mKeyguardSecurityCallback.userActivity();
+ showMessage(null, null);
+ }
+ }
+ };
+
+ private KeyguardSecurityContainerController(KeyguardSecurityContainer view,
+ AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory,
LockPatternUtils lockPatternUtils,
- KeyguardSecurityViewController.Factory keyguardSecurityViewControllerFactory) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardSecurityModel keyguardSecurityModel,
+ MetricsLogger metricsLogger,
+ UiEventLogger uiEventLogger,
+ KeyguardStateController keyguardStateController,
+ SecurityCallback securityCallback,
+ KeyguardSecurityViewFlipperController securityViewFlipperController) {
super(view);
mLockPatternUtils = lockPatternUtils;
- view.setLockPatternUtils(mLockPatternUtils);
- mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
+ mUpdateMonitor = keyguardUpdateMonitor;
+ mSecurityModel = keyguardSecurityModel;
+ mMetricsLogger = metricsLogger;
+ mUiEventLogger = uiEventLogger;
+ mKeyguardStateController = keyguardStateController;
+ mSecurityCallback = securityCallback;
+ mSecurityViewFlipperController = securityViewFlipperController;
+ mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create(
+ mKeyguardSecurityCallback);
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ mSecurityViewFlipperController.init();
}
@Override
protected void onViewAttached() {
+ mView.setSwipeListener(mSwipeListener);
}
@Override
@@ -51,68 +185,311 @@
/** */
public void onPause() {
+ mAdminSecondaryLockScreenController.hide();
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().onPause();
+ }
mView.onPause();
}
+
+ /**
+ * Shows the primary security screen for the user. This will be either the multi-selector
+ * or the user's security method.
+ * @param turningOff true if the device is being turned off
+ */
public void showPrimarySecurityScreen(boolean turningOff) {
- mView.showPrimarySecurityScreen(turningOff);
+ SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
+ showSecurityScreen(securityMode);
}
+ @Override
public void showPromptReason(int reason) {
- mView.showPromptReason(reason);
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ if (reason != PROMPT_REASON_NONE) {
+ Log.i(TAG, "Strong auth required, reason: " + reason);
+ }
+ getCurrentSecurityController().showPromptReason(reason);
+ }
}
public void showMessage(CharSequence message, ColorStateList colorState) {
- mView.showMessage(message, colorState);
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().showMessage(message, colorState);
+ }
}
- public SecurityMode getCurrentSecuritySelection() {
- return mView.getCurrentSecuritySelection();
+ public SecurityMode getCurrentSecurityMode() {
+ return mCurrentSecurityMode;
}
public void dismiss(boolean authenticated, int targetUserId) {
- mView.dismiss(authenticated, targetUserId);
+ mKeyguardSecurityCallback.dismiss(authenticated, targetUserId);
}
public void reset() {
mView.reset();
+ mSecurityViewFlipperController.reset();
}
public CharSequence getTitle() {
return mView.getTitle();
}
- public void onResume(int screenOn) {
- mView.onResume(screenOn);
+ @Override
+ public void onResume(int reason) {
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().onResume(reason);
+ }
+ mView.onResume(
+ mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()),
+ mKeyguardStateController.isFaceAuthEnabled());
}
public void startAppearAnimation() {
- mView.startAppearAnimation();
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().startAppearAnimation();
+ }
}
public boolean startDisappearAnimation(Runnable onFinishRunnable) {
- return mView.startDisappearAnimation(onFinishRunnable);
+ mView.startDisappearAnimation(getCurrentSecurityMode());
+
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable);
+ }
+
+ return false;
}
public void onStartingToHide() {
- mView.onStartingToHide();
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().onStartingToHide();
+ }
}
- public void setSecurityCallback(SecurityCallback securityCallback) {
- mView.setSecurityCallback(securityCallback);
- }
-
+ /**
+ * Shows the next security screen if there is one.
+ * @param authenticated true if the user entered the correct authentication
+ * @param targetUserId a user that needs to be the foreground user at the finish (if called)
+ * completion.
+ * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
+ * secondary lock screen requirement, if any.
+ * @return true if keyguard is done
+ */
public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) {
- return mView.showNextSecurityScreenOrFinish(
- authenticated, targetUserId, bypassSecondaryLockScreen);
+
+ if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
+ boolean finish = false;
+ boolean strongAuth = false;
+ int eventSubtype = -1;
+ BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
+ if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS;
+ } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC;
+ } else if (SecurityMode.None == getCurrentSecurityMode()) {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+ if (SecurityMode.None == securityMode) {
+ finish = true; // no security required
+ eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY;
+ } else {
+ showSecurityScreen(securityMode); // switch to the alternate security view
+ }
+ } else if (authenticated) {
+ switch (getCurrentSecurityMode()) {
+ case Pattern:
+ case Password:
+ case PIN:
+ strongAuth = true;
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_PASSWORD;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD;
+ break;
+
+ case SimPin:
+ case SimPuk:
+ // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
+ SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+ if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
+ KeyguardUpdateMonitor.getCurrentUser())) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_SIM;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
+ } else {
+ showSecurityScreen(securityMode);
+ }
+ break;
+
+ default:
+ Log.v(TAG, "Bad security screen " + getCurrentSecurityMode()
+ + ", fail safe");
+ showPrimarySecurityScreen(false);
+ break;
+ }
+ }
+ // Check for device admin specified additional security measures.
+ if (finish && !bypassSecondaryLockScreen) {
+ Intent secondaryLockscreenIntent =
+ mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
+ if (secondaryLockscreenIntent != null) {
+ mAdminSecondaryLockScreenController.show(secondaryLockscreenIntent);
+ return false;
+ }
+ }
+ if (eventSubtype != -1) {
+ mMetricsLogger.write(new LogMaker(MetricsProto.MetricsEvent.BOUNCER)
+ .setType(MetricsProto.MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
+ }
+ if (uiEvent != BouncerUiEvent.UNKNOWN) {
+ mUiEventLogger.log(uiEvent);
+ }
+ if (finish) {
+ mSecurityCallback.finish(strongAuth, targetUserId);
+ }
+ return finish;
}
public boolean needsInput() {
- return mView.needsInput();
+ return getCurrentSecurityController().needsInput();
}
- public SecurityMode getCurrentSecurityMode() {
- return mView.getCurrentSecurityMode();
+ /**
+ * Switches to the given security view unless it's already being shown, in which case
+ * this is a no-op.
+ *
+ * @param securityMode
+ */
+ @VisibleForTesting
+ void showSecurityScreen(SecurityMode securityMode) {
+ if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
+
+ if (securityMode == SecurityMode.Invalid || securityMode == mCurrentSecurityMode) {
+ return;
+ }
+
+ KeyguardInputViewController<KeyguardInputView> oldView = getCurrentSecurityController();
+
+ // Emulate Activity life cycle
+ if (oldView != null) {
+ oldView.onPause();
+ }
+
+ KeyguardInputViewController<KeyguardInputView> newView = changeSecurityMode(securityMode);
+ if (newView != null) {
+ newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
+ mSecurityViewFlipperController.show(newView);
+ }
+
+ mSecurityCallback.onSecurityModeChanged(
+ securityMode, newView != null && newView.needsInput());
+ }
+
+ public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
+ // +1 for this time
+ final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
+
+ if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
+
+ final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
+ final int failedAttemptsBeforeWipe =
+ dpm.getMaximumFailedPasswordsForWipe(null, userId);
+
+ final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0
+ ? (failedAttemptsBeforeWipe - failedAttempts)
+ : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
+ if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+ // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
+ // N attempts. Once we get below the grace period, we post this dialog every time as a
+ // clear warning until the deletion fires.
+ // Check which profile has the strictest policy for failed password attempts
+ final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
+ int userType = USER_TYPE_PRIMARY;
+ if (expiringUser == userId) {
+ // TODO: http://b/23522538
+ if (expiringUser != UserHandle.USER_SYSTEM) {
+ userType = USER_TYPE_SECONDARY_USER;
+ }
+ } else if (expiringUser != UserHandle.USER_NULL) {
+ userType = USER_TYPE_WORK_PROFILE;
+ } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
+ if (remainingBeforeWipe > 0) {
+ mView.showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
+ } else {
+ // Too many attempts. The device will be wiped shortly.
+ Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
+ mView.showWipeDialog(failedAttempts, userType);
+ }
+ }
+ mLockPatternUtils.reportFailedPasswordAttempt(userId);
+ if (timeoutMs > 0) {
+ mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
+ mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils,
+ mSecurityModel.getSecurityMode(userId));
+ }
+ }
+
+ private KeyguardInputViewController<KeyguardInputView> getCurrentSecurityController() {
+ return mSecurityViewFlipperController
+ .getSecurityView(mCurrentSecurityMode, mKeyguardSecurityCallback);
+ }
+
+ private KeyguardInputViewController<KeyguardInputView> changeSecurityMode(
+ SecurityMode securityMode) {
+ mCurrentSecurityMode = securityMode;
+ return getCurrentSecurityController();
+ }
+
+ static class Factory {
+
+ private final KeyguardSecurityContainer mView;
+ private final AdminSecondaryLockScreenController.Factory
+ mAdminSecondaryLockScreenControllerFactory;
+ private final LockPatternUtils mLockPatternUtils;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardSecurityModel mKeyguardSecurityModel;
+ private final MetricsLogger mMetricsLogger;
+ private final UiEventLogger mUiEventLogger;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
+
+ @Inject
+ Factory(KeyguardSecurityContainer view,
+ AdminSecondaryLockScreenController.Factory
+ adminSecondaryLockScreenControllerFactory,
+ LockPatternUtils lockPatternUtils,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardSecurityModel keyguardSecurityModel,
+ MetricsLogger metricsLogger,
+ UiEventLogger uiEventLogger,
+ KeyguardStateController keyguardStateController,
+ KeyguardSecurityViewFlipperController securityViewFlipperController) {
+ mView = view;
+ mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
+ mLockPatternUtils = lockPatternUtils;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mKeyguardSecurityModel = keyguardSecurityModel;
+ mMetricsLogger = metricsLogger;
+ mUiEventLogger = uiEventLogger;
+ mKeyguardStateController = keyguardStateController;
+ mSecurityViewFlipperController = securityViewFlipperController;
+ }
+
+ public KeyguardSecurityContainerController create(
+ SecurityCallback securityCallback) {
+ return new KeyguardSecurityContainerController(mView,
+ mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
+ mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
+ mKeyguardStateController, securityCallback, mSecurityViewFlipperController);
+ }
+
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index ac2160e..c77c867 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -18,13 +18,14 @@
import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.app.admin.DevicePolicyManager;
-import android.content.Context;
+import android.content.res.Resources;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
@@ -33,7 +34,7 @@
/**
* The different types of security available.
- * @see KeyguardSecurityContainer#showSecurityScreen
+ * @see KeyguardSecurityContainerController#showSecurityScreen
*/
public enum SecurityMode {
Invalid, // NULL state
@@ -45,21 +46,15 @@
SimPuk // Unlock by entering a sim puk
}
- private final Context mContext;
private final boolean mIsPukScreenAvailable;
- private LockPatternUtils mLockPatternUtils;
+ private final LockPatternUtils mLockPatternUtils;
@Inject
- KeyguardSecurityModel(Context context) {
- mContext = context;
- mLockPatternUtils = new LockPatternUtils(context);
- mIsPukScreenAvailable = mContext.getResources().getBoolean(
+ KeyguardSecurityModel(@Main Resources resources, LockPatternUtils lockPatternUtils) {
+ mIsPukScreenAvailable = resources.getBoolean(
com.android.internal.R.bool.config_enable_puk_unlock_screen);
- }
-
- void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
+ mLockPatternUtils = lockPatternUtils;
}
public SecurityMode getSecurityMode(int userId) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index 43cef3a..ac00e94 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -18,11 +18,9 @@
import android.content.res.ColorStateList;
import android.view.MotionEvent;
-import com.android.internal.widget.LockPatternUtils;
-
public interface KeyguardSecurityView {
- static public final int SCREEN_ON = 1;
- static public final int VIEW_REVEALED = 2;
+ int SCREEN_ON = 1;
+ int VIEW_REVEALED = 2;
int PROMPT_REASON_NONE = 0;
@@ -63,18 +61,6 @@
int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
/**
- * Interface back to keyguard to tell it when security
- * @param callback
- */
- void setKeyguardCallback(KeyguardSecurityCallback callback);
-
- /**
- * Set {@link LockPatternUtils} object. Useful for providing a mock interface.
- * @param utils
- */
- void setLockPatternUtils(LockPatternUtils utils);
-
- /**
* Reset the view and prepare to take input. This should do things like clearing the
* password or pattern and clear error messages.
*/
@@ -101,12 +87,6 @@
boolean needsInput();
/**
- * Get {@link KeyguardSecurityCallback} for the given object
- * @return KeyguardSecurityCallback
- */
- KeyguardSecurityCallback getCallback();
-
- /**
* Show a string explaining why the security view needs to be solved.
*
* @param reason a flag indicating which string should be shown, see {@link #PROMPT_REASON_NONE}
@@ -123,12 +103,6 @@
void showMessage(CharSequence message, ColorStateList colorState);
/**
- * Instruct the view to show usability hints, if any.
- *
- */
- void showUsabilityHint();
-
- /**
* Starts the animation which should run when the security view appears.
*/
void startAppearAnimation();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java
deleted file mode 100644
index ef9ba19..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java
+++ /dev/null
@@ -1,58 +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.keyguard;
-
-import android.view.View;
-
-import com.android.systemui.util.ViewController;
-
-import javax.inject.Inject;
-
-
-/** Controller for a {@link KeyguardSecurityView}. */
-public class KeyguardSecurityViewController extends ViewController<View> {
-
- private final KeyguardSecurityView mView;
-
- private KeyguardSecurityViewController(KeyguardSecurityView view) {
- super((View) view);
- // KeyguardSecurityView isn't actually a View, so we need to track it ourselves.
- mView = view;
- }
-
- @Override
- protected void onViewAttached() {
-
- }
-
- @Override
- protected void onViewDetached() {
-
- }
-
- /** Factory for a {@link KeyguardSecurityViewController}. */
- public static class Factory {
- @Inject
- public Factory() {
- }
-
- /** Create a new {@link KeyguardSecurityViewController}. */
- public KeyguardSecurityViewController create(KeyguardSecurityView view) {
- return new KeyguardSecurityViewController(view);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 24da3ad..b8439af 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -31,7 +30,6 @@
import android.widget.FrameLayout;
import android.widget.ViewFlipper;
-import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
/**
@@ -39,7 +37,7 @@
* we can emulate {@link android.view.WindowManager.LayoutParams#FLAG_SLIPPERY} within a view
* hierarchy.
*/
-public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
+public class KeyguardSecurityViewFlipper extends ViewFlipper {
private static final String TAG = "KeyguardSecurityViewFlipper";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -69,111 +67,16 @@
return result;
}
- KeyguardSecurityView getSecurityView() {
+ KeyguardInputView getSecurityView() {
View child = getChildAt(getDisplayedChild());
- if (child instanceof KeyguardSecurityView) {
- return (KeyguardSecurityView) child;
+ if (child instanceof KeyguardInputView) {
+ return (KeyguardInputView) child;
}
return null;
}
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.setKeyguardCallback(callback);
- }
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.setLockPatternUtils(utils);
- }
- }
-
- @Override
- public void reset() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.reset();
- }
- }
-
- @Override
- public void onPause() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.onPause();
- }
- }
-
- @Override
- public void onResume(int reason) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.onResume(reason);
- }
- }
-
- @Override
- public boolean needsInput() {
- KeyguardSecurityView ksv = getSecurityView();
- return (ksv != null) ? ksv.needsInput() : false;
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- KeyguardSecurityView ksv = getSecurityView();
- return (ksv != null) ? ksv.getCallback() : null;
- }
-
- @Override
- public void showPromptReason(int reason) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.showPromptReason(reason);
- }
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.showMessage(message, colorState);
- }
- }
-
- @Override
- public void showUsabilityHint() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.showUsabilityHint();
- }
- }
-
- @Override
- public void startAppearAnimation() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.startAppearAnimation();
- }
- }
-
- @Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- return ksv.startDisappearAnimation(finishRunnable);
- } else {
- return false;
- }
- }
-
- @Override
public CharSequence getTitle() {
- KeyguardSecurityView ksv = getSecurityView();
+ KeyguardInputView ksv = getSecurityView();
if (ksv != null) {
return ksv.getTitle();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
new file mode 100644
index 0000000..4953035
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardInputViewController.Factory;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.systemui.R;
+import com.android.systemui.util.ViewController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for a {@link KeyguardSecurityViewFlipper}.
+ */
+@KeyguardBouncerScope
+public class KeyguardSecurityViewFlipperController
+ extends ViewController<KeyguardSecurityViewFlipper> {
+
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final String TAG = "KeyguardSecurityView";
+
+ private final List<KeyguardInputViewController<KeyguardInputView>> mChildren =
+ new ArrayList<>();
+ private final LayoutInflater mLayoutInflater;
+ private final Factory mKeyguardSecurityViewControllerFactory;
+
+ @Inject
+ protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view,
+ LayoutInflater layoutInflater,
+ KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory) {
+ super(view);
+ mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
+ mLayoutInflater = layoutInflater;
+ }
+
+ @Override
+ protected void onViewAttached() {
+
+ }
+
+ @Override
+ protected void onViewDetached() {
+
+ }
+
+ public void reset() {
+ for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
+ child.reset();
+ }
+ }
+
+ @VisibleForTesting
+ KeyguardInputViewController<KeyguardInputView> getSecurityView(SecurityMode securityMode,
+ KeyguardSecurityCallback keyguardSecurityCallback) {
+ KeyguardInputViewController<KeyguardInputView> childController = null;
+ for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
+ if (child.getSecurityMode() == securityMode) {
+ childController = child;
+ break;
+ }
+ }
+
+ if (childController == null
+ && securityMode != SecurityMode.None && securityMode != SecurityMode.Invalid) {
+
+ int layoutId = getLayoutIdFor(securityMode);
+ KeyguardInputView view = null;
+ if (layoutId != 0) {
+ if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
+ view = (KeyguardInputView) mLayoutInflater.inflate(
+ layoutId, mView, false);
+ mView.addView(view);
+ childController = mKeyguardSecurityViewControllerFactory.create(
+ view, securityMode, keyguardSecurityCallback);
+ childController.init();
+
+ mChildren.add(childController);
+ }
+ }
+
+ if (childController == null) {
+ childController = new NullKeyguardInputViewController(
+ securityMode, keyguardSecurityCallback);
+ }
+
+ return childController;
+ }
+
+ private int getLayoutIdFor(SecurityMode securityMode) {
+ switch (securityMode) {
+ case Pattern: return com.android.systemui.R.layout.keyguard_pattern_view;
+ case PIN: return com.android.systemui.R.layout.keyguard_pin_view;
+ case Password: return com.android.systemui.R.layout.keyguard_password_view;
+ case SimPin: return com.android.systemui.R.layout.keyguard_sim_pin_view;
+ case SimPuk: return R.layout.keyguard_sim_puk_view;
+ default:
+ return 0;
+ }
+ }
+
+ /** Makes the supplied child visible if it is contained win this view, */
+ public void show(KeyguardInputViewController<KeyguardInputView> childController) {
+ int index = childController.getIndexIn(mView);
+ if (index != -1) {
+ mView.setDisplayedChild(index);
+ }
+ }
+
+ private static class NullKeyguardInputViewController
+ extends KeyguardInputViewController<KeyguardInputView> {
+ protected NullKeyguardInputViewController(SecurityMode securityMode,
+ KeyguardSecurityCallback keyguardSecurityCallback) {
+ super(null, securityMode, keyguardSecurityCallback);
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onStartingToHide() {
+
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 1c47aa0..c0f9ce7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -16,66 +16,19 @@
package com.android.keyguard;
-import android.annotation.NonNull;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
-import android.app.Dialog;
-import android.app.ProgressDialog;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.telephony.PinResult;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
* Displays a PIN pad for unlocking.
*/
public class KeyguardSimPinView extends KeyguardPinBasedInputView {
- private static final String LOG_TAG = "KeyguardSimPinView";
- private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
public static final String TAG = "KeyguardSimPinView";
- private ProgressDialog mSimUnlockProgressDialog = null;
- private CheckSimPin mCheckSimPinThread;
-
- // Below flag is set to true during power-up or when a new SIM card inserted on device.
- // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
- // be displayed to inform user about the number of remaining PIN attempts left.
- private boolean mShowDefaultMessage = true;
- private int mRemainingAttempts = -1;
- private AlertDialog mRemainingAttemptsDialog;
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private ImageView mSimImageView;
-
- KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onSimStateChanged(int subId, int slotId, int simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
- switch(simState) {
- case TelephonyManager.SIM_STATE_READY: {
- mRemainingAttempts = -1;
- resetState();
- break;
- }
- default:
- resetState();
- }
- }
- };
-
public KeyguardSimPinView(Context context) {
this(context, null);
}
@@ -84,81 +37,9 @@
super(context, attrs);
}
- @Override
- public void resetState() {
- super.resetState();
- if (DEBUG) Log.v(TAG, "Resetting state");
- handleSubInfoChangeIfNeeded();
- if (mShowDefaultMessage) {
- showDefaultMessage();
- }
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
-
+ public void setEsimLocked(boolean locked) {
KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
- esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
- }
-
- private void setLockedSimMessage() {
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
- int count = 1;
- TelephonyManager telephonyManager =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephonyManager != null) {
- count = telephonyManager.getActiveModemCount();
- }
- Resources rez = getResources();
- String msg;
- TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
- int color = array.getColor(0, Color.WHITE);
- array.recycle();
- if (count < 2) {
- msg = rez.getString(R.string.kg_sim_pin_instructions);
- } else {
- SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
- .getSubscriptionInfoForSubId(mSubId);
- CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
- msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
- if (info != null) {
- color = info.getIconTint();
- }
- }
- if (isEsimLocked) {
- msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
- }
-
- if (mSecurityMessageDisplay != null && getVisibility() == VISIBLE) {
- mSecurityMessageDisplay.setMessage(msg);
- }
- mSimImageView.setImageTintList(ColorStateList.valueOf(color));
- }
-
- private void showDefaultMessage() {
- setLockedSimMessage();
- if (mRemainingAttempts >= 0) {
- return;
- }
-
- // Sending empty PIN here to query the number of remaining PIN attempts
- new CheckSimPin("", mSubId) {
- void onSimCheckResponse(final PinResult result) {
- Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
- + result.toString());
- if (result.getAttemptsRemaining() >= 0) {
- mRemainingAttempts = result.getAttemptsRemaining();
- setLockedSimMessage();
- }
- }
- }.start();
- }
-
- private void handleSubInfoChangeIfNeeded() {
- KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
- int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
- if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
- mSubId = subId;
- mShowDefaultMessage = true;
- mRemainingAttempts = -1;
- }
+ esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
}
@Override
@@ -173,35 +54,6 @@
return 0;
}
- private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
- String displayMessage;
- int msgId;
- if (attemptsRemaining == 0) {
- displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
- } else if (attemptsRemaining > 0) {
- msgId = isDefault ? R.plurals.kg_password_default_pin_message :
- R.plurals.kg_password_wrong_pin_code;
- displayMessage = getContext().getResources()
- .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
- } else {
- msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
- displayMessage = getContext().getString(msgId);
- }
- if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
- displayMessage = getResources()
- .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
- }
- if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
- + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
- return displayMessage;
- }
-
- @Override
- protected boolean shouldLockout(long deadline) {
- // SIM PIN doesn't have a timed lockout
- return false;
- }
-
@Override
protected int getPasswordTextViewId() {
return R.id.simPinEntry;
@@ -214,173 +66,6 @@
if (mEcaView instanceof EmergencyCarrierArea) {
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
}
- mSimImageView = findViewById(R.id.keyguard_sim);
- }
-
- @Override
- public void showUsabilityHint() {
-
- }
-
- @Override
- public void onResume(int reason) {
- super.onResume(reason);
- Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
- resetState();
- }
-
- @Override
- public void onPause() {
- // dismiss the dialog.
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.dismiss();
- mSimUnlockProgressDialog = null;
- }
- Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
- }
-
- /**
- * Since the IPC can block, we want to run the request in a separate thread
- * with a callback.
- */
- private abstract class CheckSimPin extends Thread {
- private final String mPin;
- private int mSubId;
-
- protected CheckSimPin(String pin, int subId) {
- mPin = pin;
- mSubId = subId;
- }
-
- abstract void onSimCheckResponse(@NonNull PinResult result);
-
- @Override
- public void run() {
- if (DEBUG) {
- Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
- }
- TelephonyManager telephonyManager =
- ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
- .createForSubscriptionId(mSubId);
- final PinResult result = telephonyManager.supplyPinReportPinResult(mPin);
- if (result == null) {
- Log.e(TAG, "Error result for supplyPinReportResult.");
- post(new Runnable() {
- @Override
- public void run() {
- onSimCheckResponse(PinResult.getDefaultFailedResult());
- }
- });
- } else {
- if (DEBUG) {
- Log.v(TAG, "supplyPinReportResult returned: " + result.toString());
- }
- post(new Runnable() {
- @Override
- public void run() {
- onSimCheckResponse(result);
- }
- });
- }
- }
- }
-
- private Dialog getSimUnlockProgressDialog() {
- if (mSimUnlockProgressDialog == null) {
- mSimUnlockProgressDialog = new ProgressDialog(mContext);
- mSimUnlockProgressDialog.setMessage(
- mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
- mSimUnlockProgressDialog.setIndeterminate(true);
- mSimUnlockProgressDialog.setCancelable(false);
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- return mSimUnlockProgressDialog;
- }
-
- private Dialog getSimRemainingAttemptsDialog(int remaining) {
- String msg = getPinPasswordErrorMessage(remaining, false);
- if (mRemainingAttemptsDialog == null) {
- Builder builder = new AlertDialog.Builder(mContext);
- builder.setMessage(msg);
- builder.setCancelable(false);
- builder.setNeutralButton(R.string.ok, null);
- mRemainingAttemptsDialog = builder.create();
- mRemainingAttemptsDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- } else {
- mRemainingAttemptsDialog.setMessage(msg);
- }
- return mRemainingAttemptsDialog;
- }
-
- @Override
- protected void verifyPasswordAndUnlock() {
- String entry = mPasswordEntry.getText();
-
- if (entry.length() < 4) {
- // otherwise, display a message to the user, and don't submit.
- mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint);
- resetPasswordText(true /* animate */, true /* announce */);
- mCallback.userActivity();
- return;
- }
-
- getSimUnlockProgressDialog().show();
-
- if (mCheckSimPinThread == null) {
- mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
- @Override
- void onSimCheckResponse(final PinResult result) {
- post(new Runnable() {
- @Override
- public void run() {
- mRemainingAttempts = result.getAttemptsRemaining();
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
- }
- resetPasswordText(true /* animate */,
- /* announce */
- result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
- if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
- Dependency.get(KeyguardUpdateMonitor.class)
- .reportSimUnlocked(mSubId);
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- if (mCallback != null) {
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
- }
- } else {
- mShowDefaultMessage = false;
- if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
- if (result.getAttemptsRemaining() <= 2) {
- // this is getting critical - show dialog
- getSimRemainingAttemptsDialog(
- result.getAttemptsRemaining()).show();
- } else {
- // show message
- mSecurityMessageDisplay.setMessage(
- getPinPasswordErrorMessage(
- result.getAttemptsRemaining(), false));
- }
- } else {
- // "PIN operation failed!" - no idea what this was and no way to
- // find out. :/
- mSecurityMessageDisplay.setMessage(getContext().getString(
- R.string.kg_password_pin_failed));
- }
- if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
- + " CheckSimPin.onSimCheckResponse: " + result
- + " attemptsRemaining=" + result.getAttemptsRemaining());
- }
- mCallback.userActivity();
- mCheckSimPinThread = null;
- }
- });
- }
- };
- mCheckSimPinThread.start();
- }
}
@Override
@@ -389,11 +74,6 @@
}
@Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return false;
- }
-
- @Override
public CharSequence getTitle() {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_sim_pin_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
new file mode 100644
index 0000000..cc8bf4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.telephony.PinResult;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public class KeyguardSimPinViewController
+ extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
+ public static final String TAG = "KeyguardSimPinView";
+ private static final String LOG_TAG = "KeyguardSimPinView";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final TelephonyManager mTelephonyManager;
+
+ private ProgressDialog mSimUnlockProgressDialog;
+ private CheckSimPin mCheckSimPinThread;
+ private int mRemainingAttempts;
+ // Below flag is set to true during power-up or when a new SIM card inserted on device.
+ // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
+ // be displayed to inform user about the number of remaining PIN attempts left.
+ private boolean mShowDefaultMessage;
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private AlertDialog mRemainingAttemptsDialog;
+ private ImageView mSimImageView;
+
+ KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ if (simState == TelephonyManager.SIM_STATE_READY) {
+ mRemainingAttempts = -1;
+ resetState();
+ } else {
+ resetState();
+ }
+ }
+ };
+
+ protected KeyguardSimPinViewController(KeyguardSimPinView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode, LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener,
+ TelephonyManager telephonyManager) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mTelephonyManager = telephonyManager;
+ mSimImageView = mView.findViewById(R.id.keyguard_sim);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ }
+
+ @Override
+ void resetState() {
+ super.resetState();
+ if (DEBUG) Log.v(TAG, "Resetting state");
+ handleSubInfoChangeIfNeeded();
+ mMessageAreaController.setMessage("");
+ if (mShowDefaultMessage) {
+ showDefaultMessage();
+ }
+
+ mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
+ }
+
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return false;
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+ mView.resetState();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ @Override
+ protected void verifyPasswordAndUnlock() {
+ String entry = mPasswordEntry.getText();
+
+ if (entry.length() < 4) {
+ // otherwise, display a message to the user, and don't submit.
+ mMessageAreaController.setMessage(
+ com.android.systemui.R.string.kg_invalid_sim_pin_hint);
+ mView.resetPasswordText(true /* animate */, true /* announce */);
+ getKeyguardSecurityCallback().userActivity();
+ return;
+ }
+
+ getSimUnlockProgressDialog().show();
+
+ if (mCheckSimPinThread == null) {
+ mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
+ @Override
+ void onSimCheckResponse(final PinResult result) {
+ mView.post(() -> {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ mView.resetPasswordText(true /* animate */,
+ /* announce */
+ result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
+ mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+ getKeyguardSecurityCallback().dismiss(
+ true, KeyguardUpdateMonitor.getCurrentUser());
+ } else {
+ mShowDefaultMessage = false;
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
+ if (result.getAttemptsRemaining() <= 2) {
+ // this is getting critical - show dialog
+ getSimRemainingAttemptsDialog(
+ result.getAttemptsRemaining()).show();
+ } else {
+ // show message
+ mMessageAreaController.setMessage(
+ getPinPasswordErrorMessage(
+ result.getAttemptsRemaining(), false));
+ }
+ } else {
+ // "PIN operation failed!" - no idea what this was and no way to
+ // find out. :/
+ mMessageAreaController.setMessage(mView.getResources().getString(
+ R.string.kg_password_pin_failed));
+ }
+ if (DEBUG) {
+ Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ + " CheckSimPin.onSimCheckResponse: " + result
+ + " attemptsRemaining=" + result.getAttemptsRemaining());
+ }
+ }
+ getKeyguardSecurityCallback().userActivity();
+ mCheckSimPinThread = null;
+ });
+ }
+ };
+ mCheckSimPinThread.start();
+ }
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
+ mSimUnlockProgressDialog.setMessage(
+ mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+
+ private Dialog getSimRemainingAttemptsDialog(int remaining) {
+ String msg = getPinPasswordErrorMessage(remaining, false);
+ if (mRemainingAttemptsDialog == null) {
+ Builder builder = new AlertDialog.Builder(mView.getContext());
+ builder.setMessage(msg);
+ builder.setCancelable(false);
+ builder.setNeutralButton(R.string.ok, null);
+ mRemainingAttemptsDialog = builder.create();
+ mRemainingAttemptsDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ } else {
+ mRemainingAttemptsDialog.setMessage(msg);
+ }
+ return mRemainingAttemptsDialog;
+ }
+
+
+ private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
+ String displayMessage;
+ int msgId;
+ if (attemptsRemaining == 0) {
+ displayMessage = mView.getResources().getString(
+ R.string.kg_password_wrong_pin_code_pukked);
+ } else if (attemptsRemaining > 0) {
+ msgId = isDefault ? R.plurals.kg_password_default_pin_message :
+ R.plurals.kg_password_wrong_pin_code;
+ displayMessage = mView.getResources()
+ .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
+ } else {
+ msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
+ displayMessage = mView.getResources().getString(msgId);
+ }
+ if (KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)) {
+ displayMessage = mView.getResources()
+ .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
+ }
+ if (DEBUG) {
+ Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining="
+ + attemptsRemaining + " displayMessage=" + displayMessage);
+ }
+ return displayMessage;
+ }
+
+ private void showDefaultMessage() {
+ setLockedSimMessage();
+ if (mRemainingAttempts >= 0) {
+ return;
+ }
+
+ // Sending empty PIN here to query the number of remaining PIN attempts
+ new CheckSimPin("", mSubId) {
+ void onSimCheckResponse(final PinResult result) {
+ Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
+ + result.toString());
+ if (result.getAttemptsRemaining() >= 0) {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ setLockedSimMessage();
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPin extends Thread {
+ private final String mPin;
+ private int mSubId;
+
+ protected CheckSimPin(String pin, int subId) {
+ mPin = pin;
+ mSubId = subId;
+ }
+
+ abstract void onSimCheckResponse(@NonNull PinResult result);
+
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
+ }
+ TelephonyManager telephonyManager =
+ mTelephonyManager.createForSubscriptionId(mSubId);
+ final PinResult result = telephonyManager.supplyPinReportPinResult(mPin);
+ if (result == null) {
+ Log.e(TAG, "Error result for supplyPinReportResult.");
+ mView.post(() -> onSimCheckResponse(PinResult.getDefaultFailedResult()));
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "supplyPinReportResult returned: " + result.toString());
+ }
+ mView.post(() -> onSimCheckResponse(result));
+ }
+ }
+ }
+
+ private void setLockedSimMessage() {
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
+ int count = 1;
+ if (mTelephonyManager != null) {
+ count = mTelephonyManager.getActiveModemCount();
+ }
+ Resources rez = mView.getResources();
+ String msg;
+ TypedArray array = mView.getContext().obtainStyledAttributes(
+ new int[] { R.attr.wallpaperTextColor });
+ int color = array.getColor(0, Color.WHITE);
+ array.recycle();
+ if (count < 2) {
+ msg = rez.getString(R.string.kg_sim_pin_instructions);
+ } else {
+ SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId);
+ CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
+ msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
+ if (info != null) {
+ color = info.getIconTint();
+ }
+ }
+ if (isEsimLocked) {
+ msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
+ }
+
+ if (mView.getVisibility() == View.VISIBLE) {
+ mMessageAreaController.setMessage(msg);
+ }
+ mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+ }
+
+ private void handleSubInfoChangeIfNeeded() {
+ int subId = mKeyguardUpdateMonitor
+ .getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
+ if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
+ mSubId = subId;
+ mShowDefaultMessage = true;
+ mRemainingAttempts = -1;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 5148dd7..0d72c93 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -16,27 +16,10 @@
package com.android.keyguard;
-import android.annotation.NonNull;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.telephony.PinResult;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -44,48 +27,9 @@
* Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
*/
public class KeyguardSimPukView extends KeyguardPinBasedInputView {
- private static final String LOG_TAG = "KeyguardSimPukView";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
public static final String TAG = "KeyguardSimPukView";
- private ProgressDialog mSimUnlockProgressDialog = null;
- private CheckSimPuk mCheckSimPukThread;
-
- // Below flag is set to true during power-up or when a new SIM card inserted on device.
- // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
- // be displayed to inform user about the number of remaining PUK attempts left.
- private boolean mShowDefaultMessage = true;
- private int mRemainingAttempts = -1;
- private String mPukText;
- private String mPinText;
- private StateMachine mStateMachine = new StateMachine();
- private AlertDialog mRemainingAttemptsDialog;
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private ImageView mSimImageView;
-
- KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onSimStateChanged(int subId, int slotId, int simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
- switch(simState) {
- // If the SIM is unlocked via a key sequence through the emergency dialer, it will
- // move into the READY state and the PUK lock keyguard should be removed.
- case TelephonyManager.SIM_STATE_READY: {
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- // mCallback can be null if onSimStateChanged callback is called when keyguard
- // isn't active.
- if (mCallback != null) {
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
- }
- break;
- }
- default:
- resetState();
- }
- }
- };
-
public KeyguardSimPukView(Context context) {
this(context, null);
}
@@ -94,136 +38,14 @@
super(context, attrs);
}
- private class StateMachine {
- final int ENTER_PUK = 0;
- final int ENTER_PIN = 1;
- final int CONFIRM_PIN = 2;
- final int DONE = 3;
- private int state = ENTER_PUK;
-
- public void next() {
- int msg = 0;
- if (state == ENTER_PUK) {
- if (checkPuk()) {
- state = ENTER_PIN;
- msg = R.string.kg_puk_enter_pin_hint;
- } else {
- msg = R.string.kg_invalid_sim_puk_hint;
- }
- } else if (state == ENTER_PIN) {
- if (checkPin()) {
- state = CONFIRM_PIN;
- msg = R.string.kg_enter_confirm_pin_hint;
- } else {
- msg = R.string.kg_invalid_sim_pin_hint;
- }
- } else if (state == CONFIRM_PIN) {
- if (confirmPin()) {
- state = DONE;
- msg = R.string.keyguard_sim_unlock_progress_dialog_message;
- updateSim();
- } else {
- state = ENTER_PIN; // try again?
- msg = R.string.kg_invalid_confirm_pin_hint;
- }
- }
- resetPasswordText(true /* animate */, true /* announce */);
- if (msg != 0) {
- mSecurityMessageDisplay.setMessage(msg);
- }
- }
-
-
- void reset() {
- mPinText="";
- mPukText="";
- state = ENTER_PUK;
- handleSubInfoChangeIfNeeded();
- if (mShowDefaultMessage) {
- showDefaultMessage();
- }
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
-
- KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
- esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
- mPasswordEntry.requestFocus();
- }
-
-
- }
-
- private void showDefaultMessage() {
- if (mRemainingAttempts >= 0) {
- mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
- mRemainingAttempts, true));
- return;
- }
-
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
- int count = 1;
- TelephonyManager telephonyManager =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephonyManager != null) {
- count = telephonyManager.getActiveModemCount();
- }
- Resources rez = getResources();
- String msg;
- TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
- int color = array.getColor(0, Color.WHITE);
- array.recycle();
- if (count < 2) {
- msg = rez.getString(R.string.kg_puk_enter_puk_hint);
- } else {
- SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
- .getSubscriptionInfoForSubId(mSubId);
- CharSequence displayName = info != null ? info.getDisplayName() : "";
- msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
- if (info != null) {
- color = info.getIconTint();
- }
- }
- if (isEsimLocked) {
- msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
- }
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage(msg);
- }
- mSimImageView.setImageTintList(ColorStateList.valueOf(color));
-
- // Sending empty PUK here to query the number of remaining PIN attempts
- new CheckSimPuk("", "", mSubId) {
- void onSimLockChangedResponse(final PinResult result) {
- if (result == null) Log.e(LOG_TAG, "onSimCheckResponse, pin result is NULL");
- else {
- Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
- + result.toString());
- if (result.getAttemptsRemaining() >= 0) {
- mRemainingAttempts = result.getAttemptsRemaining();
- mSecurityMessageDisplay.setMessage(
- getPukPasswordErrorMessage(result.getAttemptsRemaining(), true));
- }
- }
- }
- }.start();
- }
-
- private void handleSubInfoChangeIfNeeded() {
- KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
- int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PUK_REQUIRED);
- if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
- mSubId = subId;
- mShowDefaultMessage = true;
- mRemainingAttempts = -1;
- }
- }
-
@Override
protected int getPromptReasonStringRes(int reason) {
// No message on SIM Puk
return 0;
}
- private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
+ String getPukPasswordErrorMessage(
+ int attemptsRemaining, boolean isDefault, boolean isEsimLocked) {
String displayMessage;
if (attemptsRemaining == 0) {
@@ -238,28 +60,19 @@
R.string.kg_password_puk_failed;
displayMessage = getContext().getString(msgId);
}
- if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
+ if (isEsimLocked) {
displayMessage = getResources()
.getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
}
- if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:"
- + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
+ if (DEBUG) {
+ Log.d(TAG, "getPukPasswordErrorMessage:"
+ + " attemptsRemaining=" + attemptsRemaining
+ + " displayMessage=" + displayMessage);
+ }
return displayMessage;
}
@Override
- public void resetState() {
- super.resetState();
- mStateMachine.reset();
- }
-
- @Override
- protected boolean shouldLockout(long deadline) {
- // SIM PUK doesn't have a timed lockout
- return false;
- }
-
- @Override
protected int getPasswordTextViewId() {
return R.id.pukEntry;
}
@@ -271,197 +84,6 @@
if (mEcaView instanceof EmergencyCarrierArea) {
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
}
- mSimImageView = findViewById(R.id.keyguard_sim);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
- resetState();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public void onPause() {
- // dismiss the dialog.
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.dismiss();
- mSimUnlockProgressDialog = null;
- }
- }
-
- /**
- * Since the IPC can block, we want to run the request in a separate thread
- * with a callback.
- */
- private abstract class CheckSimPuk extends Thread {
-
- private final String mPin, mPuk;
- private final int mSubId;
-
- protected CheckSimPuk(String puk, String pin, int subId) {
- mPuk = puk;
- mPin = pin;
- mSubId = subId;
- }
-
- abstract void onSimLockChangedResponse(@NonNull PinResult result);
-
- @Override
- public void run() {
- if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
- TelephonyManager telephonyManager =
- ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
- .createForSubscriptionId(mSubId);
- final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin);
- if (result == null) {
- Log.e(TAG, "Error result for supplyPukReportResult.");
- post(new Runnable() {
- @Override
- public void run() {
- onSimLockChangedResponse(PinResult.getDefaultFailedResult());
- }
- });
- } else {
- if (DEBUG) {
- Log.v(TAG, "supplyPukReportResult returned: " + result.toString());
- }
- post(new Runnable() {
- @Override
- public void run() {
- onSimLockChangedResponse(result);
- }
- });
- }
- }
- }
-
- private Dialog getSimUnlockProgressDialog() {
- if (mSimUnlockProgressDialog == null) {
- mSimUnlockProgressDialog = new ProgressDialog(mContext);
- mSimUnlockProgressDialog.setMessage(
- mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
- mSimUnlockProgressDialog.setIndeterminate(true);
- mSimUnlockProgressDialog.setCancelable(false);
- if (!(mContext instanceof Activity)) {
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- }
- return mSimUnlockProgressDialog;
- }
-
- private Dialog getPukRemainingAttemptsDialog(int remaining) {
- String msg = getPukPasswordErrorMessage(remaining, false);
- if (mRemainingAttemptsDialog == null) {
- AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
- builder.setMessage(msg);
- builder.setCancelable(false);
- builder.setNeutralButton(R.string.ok, null);
- mRemainingAttemptsDialog = builder.create();
- mRemainingAttemptsDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- } else {
- mRemainingAttemptsDialog.setMessage(msg);
- }
- return mRemainingAttemptsDialog;
- }
-
- private boolean checkPuk() {
- // make sure the puk is at least 8 digits long.
- if (mPasswordEntry.getText().length() == 8) {
- mPukText = mPasswordEntry.getText();
- return true;
- }
- return false;
- }
-
- private boolean checkPin() {
- // make sure the PIN is between 4 and 8 digits
- int length = mPasswordEntry.getText().length();
- if (length >= 4 && length <= 8) {
- mPinText = mPasswordEntry.getText();
- return true;
- }
- return false;
- }
-
- public boolean confirmPin() {
- return mPinText.equals(mPasswordEntry.getText());
- }
-
- private void updateSim() {
- getSimUnlockProgressDialog().show();
-
- if (mCheckSimPukThread == null) {
- mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
- @Override
- void onSimLockChangedResponse(final PinResult result) {
- post(new Runnable() {
- @Override
- public void run() {
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
- }
- resetPasswordText(true /* animate */,
- /* announce */
- result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
- if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
- Dependency.get(KeyguardUpdateMonitor.class)
- .reportSimUnlocked(mSubId);
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- if (mCallback != null) {
- mCallback.dismiss(true,
- KeyguardUpdateMonitor.getCurrentUser());
- }
- } else {
- mShowDefaultMessage = false;
- if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
- // show message
- mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
- result.getAttemptsRemaining(), false));
- if (result.getAttemptsRemaining() <= 2) {
- // this is getting critical - show dialog
- getPukRemainingAttemptsDialog(
- result.getAttemptsRemaining()).show();
- } else {
- // show message
- mSecurityMessageDisplay.setMessage(
- getPukPasswordErrorMessage(
- result.getAttemptsRemaining(), false));
- }
- } else {
- mSecurityMessageDisplay.setMessage(getContext().getString(
- R.string.kg_password_puk_failed));
- }
- if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
- + " UpdateSim.onSimCheckResponse: "
- + " attemptsRemaining=" + result.getAttemptsRemaining());
- }
- mStateMachine.reset();
- mCheckSimPukThread = null;
- }
- });
- }
- };
- mCheckSimPukThread.start();
- }
- }
-
- @Override
- protected void verifyPasswordAndUnlock() {
- mStateMachine.next();
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
new file mode 100644
index 0000000..a873749
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.telephony.PinResult;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+
+public class KeyguardSimPukViewController
+ extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ public static final String TAG = "KeyguardSimPukView";
+
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final TelephonyManager mTelephonyManager;
+
+ private String mPukText;
+ private String mPinText;
+ private int mRemainingAttempts;
+ // Below flag is set to true during power-up or when a new SIM card inserted on device.
+ // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
+ // be displayed to inform user about the number of remaining PUK attempts left.
+ private boolean mShowDefaultMessage;
+ private StateMachine mStateMachine = new StateMachine();
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private CheckSimPuk mCheckSimPukThread;
+ private ProgressDialog mSimUnlockProgressDialog;
+
+ KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ // If the SIM is unlocked via a key sequence through the emergency dialer, it will
+ // move into the READY state and the PUK lock keyguard should be removed.
+ if (simState == TelephonyManager.SIM_STATE_READY) {
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+ getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ } else {
+ resetState();
+ }
+ }
+ };
+ private ImageView mSimImageView;
+ private AlertDialog mRemainingAttemptsDialog;
+
+ protected KeyguardSimPukViewController(KeyguardSimPukView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode, LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener,
+ TelephonyManager telephonyManager) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mTelephonyManager = telephonyManager;
+ mSimImageView = mView.findViewById(R.id.keyguard_sim);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+ }
+
+ @Override
+ void resetState() {
+ super.resetState();
+ mStateMachine.reset();
+ }
+
+ @Override
+ protected void verifyPasswordAndUnlock() {
+ mStateMachine.next();
+ }
+
+ private class StateMachine {
+ static final int ENTER_PUK = 0;
+ static final int ENTER_PIN = 1;
+ static final int CONFIRM_PIN = 2;
+ static final int DONE = 3;
+
+ private int mState = ENTER_PUK;
+
+ public void next() {
+ int msg = 0;
+ if (mState == ENTER_PUK) {
+ if (checkPuk()) {
+ mState = ENTER_PIN;
+ msg = com.android.systemui.R.string.kg_puk_enter_pin_hint;
+ } else {
+ msg = com.android.systemui.R.string.kg_invalid_sim_puk_hint;
+ }
+ } else if (mState == ENTER_PIN) {
+ if (checkPin()) {
+ mState = CONFIRM_PIN;
+ msg = com.android.systemui.R.string.kg_enter_confirm_pin_hint;
+ } else {
+ msg = com.android.systemui.R.string.kg_invalid_sim_pin_hint;
+ }
+ } else if (mState == CONFIRM_PIN) {
+ if (confirmPin()) {
+ mState = DONE;
+ msg = com.android.systemui.R.string.keyguard_sim_unlock_progress_dialog_message;
+ updateSim();
+ } else {
+ mState = ENTER_PIN; // try again?
+ msg = com.android.systemui.R.string.kg_invalid_confirm_pin_hint;
+ }
+ }
+ mView.resetPasswordText(true /* animate */, true /* announce */);
+ if (msg != 0) {
+ mMessageAreaController.setMessage(msg);
+ }
+ }
+
+
+ void reset() {
+ mPinText = "";
+ mPukText = "";
+ mState = ENTER_PUK;
+ handleSubInfoChangeIfNeeded();
+ if (mShowDefaultMessage) {
+ showDefaultMessage();
+ }
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
+
+ KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area);
+ esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
+ mPasswordEntry.requestFocus();
+ }
+ }
+
+ private void showDefaultMessage() {
+ if (mRemainingAttempts >= 0) {
+ mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage(
+ mRemainingAttempts, true,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
+ return;
+ }
+
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
+ int count = 1;
+ if (mTelephonyManager != null) {
+ count = mTelephonyManager.getActiveModemCount();
+ }
+ Resources rez = mView.getResources();
+ String msg;
+ TypedArray array = mView.getContext().obtainStyledAttributes(
+ new int[] { R.attr.wallpaperTextColor });
+ int color = array.getColor(0, Color.WHITE);
+ array.recycle();
+ if (count < 2) {
+ msg = rez.getString(R.string.kg_puk_enter_puk_hint);
+ } else {
+ SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
+ .getSubscriptionInfoForSubId(mSubId);
+ CharSequence displayName = info != null ? info.getDisplayName() : "";
+ msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
+ if (info != null) {
+ color = info.getIconTint();
+ }
+ }
+ if (isEsimLocked) {
+ msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
+ }
+ mMessageAreaController.setMessage(msg);
+ mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+
+ // Sending empty PUK here to query the number of remaining PIN attempts
+ new CheckSimPuk("", "", mSubId) {
+ void onSimLockChangedResponse(final PinResult result) {
+ if (result == null) Log.e(TAG, "onSimCheckResponse, pin result is NULL");
+ else {
+ Log.d(TAG, "onSimCheckResponse " + " empty One result "
+ + result.toString());
+ if (result.getAttemptsRemaining() >= 0) {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ mMessageAreaController.setMessage(
+ mView.getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), true,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
+ }
+ }
+ }
+ }.start();
+ }
+
+ private boolean checkPuk() {
+ // make sure the puk is at least 8 digits long.
+ if (mPasswordEntry.getText().length() == 8) {
+ mPukText = mPasswordEntry.getText();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean checkPin() {
+ // make sure the PIN is between 4 and 8 digits
+ int length = mPasswordEntry.getText().length();
+ if (length >= 4 && length <= 8) {
+ mPinText = mPasswordEntry.getText();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean confirmPin() {
+ return mPinText.equals(mPasswordEntry.getText());
+ }
+
+
+
+
+ private void updateSim() {
+ getSimUnlockProgressDialog().show();
+
+ if (mCheckSimPukThread == null) {
+ mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
+ @Override
+ void onSimLockChangedResponse(final PinResult result) {
+ mView.post(() -> {
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ mView.resetPasswordText(true /* animate */,
+ /* announce */
+ result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
+ mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+
+ getKeyguardSecurityCallback().dismiss(
+ true, KeyguardUpdateMonitor.getCurrentUser());
+ } else {
+ mShowDefaultMessage = false;
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
+ // show message
+ mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), false,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
+ if (result.getAttemptsRemaining() <= 2) {
+ // this is getting critical - show dialog
+ getPukRemainingAttemptsDialog(
+ result.getAttemptsRemaining()).show();
+ } else {
+ // show message
+ mMessageAreaController.setMessage(
+ mView.getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), false,
+ KeyguardEsimArea.isEsimLocked(
+ mView.getContext(), mSubId)));
+ }
+ } else {
+ mMessageAreaController.setMessage(mView.getResources().getString(
+ R.string.kg_password_puk_failed));
+ }
+ if (DEBUG) {
+ Log.d(TAG, "verifyPasswordAndUnlock "
+ + " UpdateSim.onSimCheckResponse: "
+ + " attemptsRemaining=" + result.getAttemptsRemaining());
+ }
+ }
+ mStateMachine.reset();
+ mCheckSimPukThread = null;
+ });
+ }
+ };
+ mCheckSimPukThread.start();
+ }
+ }
+
+ @Override
+ protected boolean shouldLockout(long deadline) {
+ // SIM PUK doesn't have a timed lockout
+ return false;
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
+ mSimUnlockProgressDialog.setMessage(
+ mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ if (!(mView.getContext() instanceof Activity)) {
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+ private void handleSubInfoChangeIfNeeded() {
+ int subId = mKeyguardUpdateMonitor.getNextSubIdForState(
+ TelephonyManager.SIM_STATE_PUK_REQUIRED);
+ if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
+ mSubId = subId;
+ mShowDefaultMessage = true;
+ mRemainingAttempts = -1;
+ }
+ }
+
+
+ private Dialog getPukRemainingAttemptsDialog(int remaining) {
+ String msg = mView.getPukPasswordErrorMessage(remaining, false,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
+ if (mRemainingAttemptsDialog == null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(mView.getContext());
+ builder.setMessage(msg);
+ builder.setCancelable(false);
+ builder.setNeutralButton(R.string.ok, null);
+ mRemainingAttemptsDialog = builder.create();
+ mRemainingAttemptsDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ } else {
+ mRemainingAttemptsDialog.setMessage(msg);
+ }
+ return mRemainingAttemptsDialog;
+ }
+
+ @Override
+ public void onPause() {
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPuk extends Thread {
+
+ private final String mPin, mPuk;
+ private final int mSubId;
+
+ protected CheckSimPuk(String puk, String pin, int subId) {
+ mPuk = puk;
+ mPin = pin;
+ mSubId = subId;
+ }
+
+ abstract void onSimLockChangedResponse(@NonNull PinResult result);
+
+ @Override
+ public void run() {
+ if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
+ TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
+ final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin);
+ if (result == null) {
+ Log.e(TAG, "Error result for supplyPukReportResult.");
+ mView.post(() -> onSimLockChangedResponse(PinResult.getDefaultFailedResult()));
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "supplyPukReportResult returned: " + result.toString());
+ }
+ mView.post(new Runnable() {
+ @Override
+ public void run() {
+ onSimLockChangedResponse(result);
+ }
+ });
+ }
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
index e59602b..425e50e 100644
--- a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
+++ b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
@@ -16,11 +16,12 @@
package com.android.keyguard;
-import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import javax.inject.Inject;
+
/**
* Hover listener that implements lift-to-activate interaction for
* accessibility. May be added to multiple views.
@@ -31,9 +32,9 @@
private boolean mCachedClickableState;
- public LiftToActivateListener(Context context) {
- mAccessibilityManager = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+ @Inject
+ LiftToActivateListener(AccessibilityManager accessibilityManager) {
+ mAccessibilityManager = accessibilityManager;
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index b0457fc..2205fdd 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -26,6 +26,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
@@ -90,7 +91,8 @@
}
setOnClickListener(mListener);
- setOnHoverListener(new LiftToActivateListener(context));
+ setOnHoverListener(new LiftToActivateListener(
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE)));
mLockPatternUtils = new LockPatternUtils(context);
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index b6010c8..8811088 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -22,6 +22,7 @@
import com.android.keyguard.KeyguardHostView;
import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardSecurityContainer;
+import com.android.keyguard.KeyguardSecurityViewFlipper;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
@@ -58,7 +59,15 @@
/** */
@Provides
@KeyguardBouncerScope
- static KeyguardSecurityContainer preovidesKeyguardSecurityContainer(KeyguardHostView hostView) {
+ static KeyguardSecurityContainer providesKeyguardSecurityContainer(KeyguardHostView hostView) {
return hostView.findViewById(R.id.keyguard_security_container);
}
+
+ /** */
+ @Provides
+ @KeyguardBouncerScope
+ static KeyguardSecurityViewFlipper providesKeyguardSecurityViewFlipper(
+ KeyguardSecurityContainer containerView) {
+ return containerView.findViewById(R.id.view_flipper);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index c90e6b1..e303754 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -289,6 +289,7 @@
/** */
@Provides
+ @SysUISingleton
public LockPatternUtils provideLockPatternUtils(Context context) {
return new LockPatternUtils(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index b35579d..79925ba 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -62,6 +62,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
+import android.view.inputmethod.InputMethodManager;
import com.android.internal.app.IBatteryStats;
import com.android.internal.statusbar.IStatusBarService;
@@ -183,6 +184,12 @@
@Provides
@Singleton
+ static InputMethodManager provideInputMethodManager(Context context) {
+ return context.getSystemService(InputMethodManager.class);
+ }
+
+ @Provides
+ @Singleton
static IPackageManager provideIPackageManager() {
return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index 52a2cec..0053fea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -2,6 +2,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -31,9 +32,6 @@
private static final long ANIMATION_DURATION = 250;
- // The size of a single dot in relation to the whole animation.
- private static final float SINGLE_SCALE = .4f;
-
private static final float MINOR_ALPHA = .42f;
private final ArrayList<Integer> mQueuedPositions = new ArrayList<>();
@@ -75,11 +73,10 @@
}
array.recycle();
- mPageIndicatorWidth =
- (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_width);
- mPageIndicatorHeight =
- (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_height);
- mPageDotWidth = (int) (mPageIndicatorWidth * SINGLE_SCALE);
+ Resources res = context.getResources();
+ mPageIndicatorWidth = res.getDimensionPixelSize(R.dimen.qs_page_indicator_width);
+ mPageIndicatorHeight = res.getDimensionPixelSize(R.dimen.qs_page_indicator_height);
+ mPageDotWidth = res.getDimensionPixelSize(R.dimen.qs_page_indicator_dot_width);
}
public void setNumPages(int numPages) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 22c735d..04f379e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -177,6 +177,16 @@
}
@Override
+ public void endFakeDrag() {
+ try {
+ super.endFakeDrag();
+ } catch (NullPointerException e) {
+ // Not sure what's going on. Let's log it
+ Log.e(TAG, "endFakeDrag called without velocityTracker", e);
+ }
+ }
+
+ @Override
public void computeScroll() {
if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
if (!isFakeDragging()) {
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 95edfe3..d7a8202 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
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.FooterView;
@@ -687,15 +688,27 @@
AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
float childrenOnTop = 0.0f;
+
+ int topHunIndex = -1;
+ for (int i = 0; i < childCount; i++) {
+ ExpandableView child = algorithmState.visibleChildren.get(i);
+ if (child instanceof ActivatableNotificationView
+ && (child.isAboveShelf() || child.showingPulsing())) {
+ topHunIndex = i;
+ break;
+ }
+ }
+
for (int i = childCount - 1; i >= 0; i--) {
childrenOnTop = updateChildZValue(i, childrenOnTop,
- algorithmState, ambientState);
+ algorithmState, ambientState, i == topHunIndex);
}
}
protected float updateChildZValue(int i, float childrenOnTop,
StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState,
+ boolean shouldElevateHun) {
ExpandableView child = algorithmState.visibleChildren.get(i);
ExpandableViewState childViewState = child.getViewState();
int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
@@ -713,8 +726,7 @@
}
childViewState.zTranslation = baseZ
+ childrenOnTop * zDistanceBetweenElements;
- } else if (child == ambientState.getTrackedHeadsUpRow()
- || (i == 0 && (child.isAboveShelf() || child.showingPulsing()))) {
+ } else if (shouldElevateHun) {
// In case this is a new view that has never been measured before, we don't want to
// elevate if we are currently expanded more then the notification
int shelfHeight = ambientState.getShelf() == null ? 0 :
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index eb8f065..a6cd350 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -23,7 +23,6 @@
import android.view.LayoutInflater;
import android.view.View;
-import com.android.keyguard.KeyguardMessageArea;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.qs.QSFooterImpl;
import com.android.systemui.qs.QSPanel;
@@ -108,11 +107,6 @@
NotificationStackScrollLayout createNotificationStackScrollLayout();
/**
- * Creates the KeyguardMessageArea.
- */
- KeyguardMessageArea createKeyguardMessageArea();
-
- /**
* Creates the QSPanel.
*/
QSPanel createQSPanel();
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
index 64f8dbb..c7aa780fc 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -23,7 +23,20 @@
* Utility class that handles view lifecycle events for View Controllers.
*
* Implementations should handle setup and teardown related activities inside of
- * {@link #onViewAttached()} and {@link #onViewDetached()}.
+ * {@link #onViewAttached()} and {@link #onViewDetached()}. Be sure to call {@link #init()} on
+ * any child controllers that this uses. This can be done in {@link init()} if the controllers
+ * are injected, or right after creation time of the child controller.
+ *
+ * Tip: View "attachment" happens top down - parents are notified that they are attached before
+ * any children. That means that if you call a method on a child controller in
+ * {@link #onViewAttached()}, the child controller may not have had its onViewAttach method
+ * called, so it may not be fully set up.
+ *
+ * As such, make sure that methods on your controller are safe to call _before_ its {@link #init()}
+ * and {@link #onViewAttached()} methods are called. Specifically, if your controller must call
+ * {@link View#findViewById(int)} on its root view to setup member variables, do so in its
+ * constructor. Save {@link #onViewAttached()} for things that can happen post-construction - adding
+ * listeners, dynamically changing content, or other runtime decisions.
*
* @param <T> View class that this ViewController is for.
*/
@@ -54,10 +67,12 @@
}
mInited = true;
- if (mView.isAttachedToWindow()) {
- mOnAttachStateListener.onViewAttachedToWindow(mView);
+ if (mView != null) {
+ if (mView.isAttachedToWindow()) {
+ mOnAttachStateListener.onViewAttachedToWindow(mView);
+ }
+ mView.addOnAttachStateChangeListener(mOnAttachStateListener);
}
- mView.addOnAttachStateChangeListener(mOnAttachStateListener);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index d6595b2..a50de45 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -26,6 +26,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.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -52,8 +53,9 @@
static SplitScreen provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, @Main Handler handler,
- TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer) {
+ TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue) {
return new SplitScreenController(context, displayController, systemWindows,
- displayImeController, handler, transactionPool, shellTaskOrganizer);
+ displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index bf9df8e..dfb30b4 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -39,6 +39,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.FlingAnimationUtils;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.onehanded.OneHanded;
@@ -123,8 +124,15 @@
@SysUISingleton
@Provides
- static ShellTaskOrganizer provideShellTaskOrganizer(TransactionPool transactionPool) {
- ShellTaskOrganizer organizer = new ShellTaskOrganizer(transactionPool);
+ static SyncTransactionQueue provideSyncTransactionQueue(@Main Handler handler,
+ TransactionPool pool) {
+ return new SyncTransactionQueue(pool, handler);
+ }
+
+ @SysUISingleton
+ @Provides
+ static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue) {
+ ShellTaskOrganizer organizer = new ShellTaskOrganizer(syncQueue);
organizer.registerOrganizer();
return organizer;
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 6ed836c..3142c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -36,6 +36,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.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.onehanded.OneHanded;
@@ -84,9 +85,10 @@
static SplitScreen provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, @Main Handler handler,
- TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer) {
+ TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue) {
return new SplitScreenController(context, displayController, systemWindows,
- displayImeController, handler, transactionPool, shellTaskOrganizer);
+ displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue);
}
@SysUISingleton
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index 9be2d12..dffad6c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -41,8 +41,6 @@
import android.testing.ViewUtils;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceView;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
@@ -67,7 +65,7 @@
private ComponentName mComponentName;
private Intent mServiceIntent;
private TestableLooper mTestableLooper;
- private ViewGroup mParent;
+ private KeyguardSecurityContainer mKeyguardSecurityContainer;
@Mock
private Handler mHandler;
@@ -84,8 +82,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mParent = spy(new FrameLayout(mContext));
- ViewUtils.attachView(mParent);
+ mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext));
+ ViewUtils.attachView(mKeyguardSecurityContainer);
mTestableLooper = TestableLooper.get(this);
mComponentName = new ComponentName(mContext, "FakeKeyguardClient.class");
@@ -96,13 +94,14 @@
when(mKeyguardClient.queryLocalInterface(anyString())).thenReturn(mKeyguardClient);
when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient);
- mTestController = new AdminSecondaryLockScreenController(
- mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler);
+ mTestController = new AdminSecondaryLockScreenController.Factory(
+ mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler)
+ .create(mKeyguardCallback);
}
@After
public void tearDown() {
- ViewUtils.detachView(mParent);
+ ViewUtils.detachView(mKeyguardSecurityContainer);
}
@Test
@@ -146,7 +145,7 @@
SurfaceView v = verifySurfaceReady();
mTestController.hide();
- verify(mParent).removeView(v);
+ verify(mKeyguardSecurityContainer).removeView(v);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
@@ -154,7 +153,7 @@
public void testHide_notShown() throws Exception {
mTestController.hide();
// Nothing should happen if trying to hide when the view isn't attached yet.
- verify(mParent, never()).removeView(any(SurfaceView.class));
+ verify(mKeyguardSecurityContainer, never()).removeView(any(SurfaceView.class));
}
@Test
@@ -182,7 +181,7 @@
private SurfaceView verifySurfaceReady() throws Exception {
mTestableLooper.processAllMessages();
ArgumentCaptor<SurfaceView> captor = ArgumentCaptor.forClass(SurfaceView.class);
- verify(mParent).addView(captor.capture());
+ verify(mKeyguardSecurityContainer).addView(captor.capture());
mTestableLooper.processAllMessages();
verify(mKeyguardClient).onCreateKeyguardSurface(any(), any(IKeyguardCallback.class));
@@ -190,7 +189,7 @@
}
private void verifyViewDismissed(SurfaceView v) throws Exception {
- verify(mParent).removeView(v);
+ verify(mKeyguardSecurityContainer).removeView(v);
verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
new file mode 100644
index 0000000..c2ade81
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.KeyEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ private KeyguardAbsKeyInputView mAbsKeyInputView;
+ @Mock
+ private PasswordTextView mPasswordEntry;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private SecurityMode mSecurityMode;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+ @Mock
+ private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
+ @Mock
+ private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ @Mock
+ private LatencyTracker mLatencyTracker;
+
+ private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
+ .thenReturn(mKeyguardMessageAreaController);
+ when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1);
+ when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry);
+ when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true);
+ when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea);
+ mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
+ mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
+ mKeyguardMessageAreaControllerFactory, mLatencyTracker) {
+ @Override
+ void resetState() {
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ }
+ };
+ mKeyguardAbsKeyInputViewController.init();
+ reset(mKeyguardMessageAreaController); // Clear out implicit call to init.
+ }
+
+ @Test
+ public void onKeyDown_clearsSecurityMessage() {
+ ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor =
+ ArgumentCaptor.forClass(KeyDownListener.class);
+ verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
+ onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
+ KeyEvent.KEYCODE_0, mock(KeyEvent.class));
+ verify(mKeyguardSecurityCallback).userActivity();
+ verify(mKeyguardMessageAreaController).setMessage(eq(""));
+ }
+
+ @Test
+ public void onKeyDown_noSecurityMessageInteraction() {
+ ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor =
+ ArgumentCaptor.forClass(KeyDownListener.class);
+ verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
+ onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
+ KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
+ verifyZeroInteractions(mKeyguardSecurityCallback);
+ verifyZeroInteractions(mKeyguardMessageAreaController);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 5999e2c..e793079 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -41,11 +41,9 @@
import android.widget.TextClock;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
@@ -78,12 +76,7 @@
when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
.thenReturn(mMockKeyguardSliceView);
- InjectionInflationController inflationController = new InjectionInflationController(
- SystemUIFactory.getInstance()
- .getSysUIComponent()
- .createViewInstanceCreatorFactory());
- LayoutInflater layoutInflater = inflationController
- .injectable(LayoutInflater.from(getContext()));
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
index 54e879e..64632af 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
@@ -16,8 +16,10 @@
package com.android.keyguard;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.media.AudioManager;
import android.telephony.TelephonyManager;
@@ -47,13 +49,15 @@
@Mock
private KeyguardHostView mKeyguardHostView;
@Mock
- private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
- @Mock
private AudioManager mAudioManager;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private ViewMediatorCallback mViewMediatorCallback;
+ @Mock
+ KeyguardSecurityContainerController.Factory mKeyguardSecurityContainerControllerFactory;
+ @Mock
+ private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -62,9 +66,12 @@
@Before
public void setup() {
+ when(mKeyguardSecurityContainerControllerFactory.create(any(
+ KeyguardSecurityContainer.SecurityCallback.class)))
+ .thenReturn(mKeyguardSecurityContainerController);
mKeyguardHostViewController = new KeyguardHostViewController(
- mKeyguardHostView, mKeyguardUpdateMonitor, mKeyguardSecurityContainerController,
- mAudioManager, mTelephonyManager, mViewMediatorCallback);
+ mKeyguardHostView, mKeyguardUpdateMonitor, mAudioManager, mTelephonyManager,
+ mViewMediatorCallback, mKeyguardSecurityContainerControllerFactory);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
new file mode 100644
index 0000000..a7197cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
+ @Mock
+ private ConfigurationController mConfigurationController;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+
+ private KeyguardMessageAreaController mMessageAreaController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mMessageAreaController = new KeyguardMessageAreaController.Factory(
+ mKeyguardUpdateMonitor, mConfigurationController).create(mKeyguardMessageArea);
+ }
+
+ @Test
+ public void onAttachedToWindow_registersConfigurationCallback() {
+ ArgumentCaptor<ConfigurationListener> configurationListenerArgumentCaptor =
+ ArgumentCaptor.forClass(ConfigurationListener.class);
+
+ mMessageAreaController.onViewAttached();
+ verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+
+ mMessageAreaController.onViewDetached();
+ verify(mConfigurationController).removeCallback(
+ eq(configurationListenerArgumentCaptor.getValue()));
+ }
+
+ @Test
+ public void onAttachedToWindow_registersKeyguardUpdateMontiorCallback() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+
+ mMessageAreaController.onViewAttached();
+ verify(mKeyguardUpdateMonitor).registerCallback(
+ keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+
+ mMessageAreaController.onViewDetached();
+ verify(mKeyguardUpdateMonitor).removeCallback(
+ eq(keyguardUpdateMonitorCallbackArgumentCaptor.getValue()));
+ }
+
+ @Test
+ public void testClearsTextField() {
+ mMessageAreaController.setMessage("");
+ verify(mKeyguardMessageArea).setMessage("");
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
index fc7b9a4..31fb25a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,65 +11,60 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.keyguard;
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
+import static com.google.common.truth.Truth.assertThat;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ConfigurationController;
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)
@RunWithLooper
public class KeyguardMessageAreaTest extends SysuiTestCase {
- @Mock
- private ConfigurationController mConfigurationController;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private KeyguardMessageArea mMessageArea;
+ private KeyguardMessageArea mKeyguardMessageArea;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mMessageArea = new KeyguardMessageArea(mContext, null, mKeyguardUpdateMonitor,
- mConfigurationController);
- waitForIdleSync();
+ mKeyguardMessageArea = new KeyguardMessageArea(mContext, null);
+ mKeyguardMessageArea.setBouncerVisible(true);
}
@Test
- public void onAttachedToWindow_registersConfigurationCallback() {
- mMessageArea.onAttachedToWindow();
- verify(mConfigurationController).addCallback(eq(mMessageArea));
-
- mMessageArea.onDetachedFromWindow();
- verify(mConfigurationController).removeCallback(eq(mMessageArea));
+ public void testShowsTextField() {
+ mKeyguardMessageArea.setVisibility(View.INVISIBLE);
+ mKeyguardMessageArea.setMessage("oobleck");
+ assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck");
}
@Test
- public void clearFollowedByMessage_keepsMessage() {
- mMessageArea.setMessage("");
- mMessageArea.setMessage("test");
-
- CharSequence[] messageText = new CharSequence[1];
- messageText[0] = mMessageArea.getText();
-
- assertEquals("test", messageText[0]);
+ public void testHiddenWhenBouncerHidden() {
+ mKeyguardMessageArea.setBouncerVisible(false);
+ mKeyguardMessageArea.setVisibility(View.INVISIBLE);
+ mKeyguardMessageArea.setMessage("oobleck");
+ assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
+ assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck");
}
+ @Test
+ public void testClearsTextField() {
+ mKeyguardMessageArea.setVisibility(View.VISIBLE);
+ mKeyguardMessageArea.setMessage("");
+ assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
+ assertThat(mKeyguardMessageArea.getText()).isEqualTo("");
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
new file mode 100644
index 0000000..c69ec1a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.util.LatencyTracker
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternView
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardPatternViewControllerTest : SysuiTestCase() {
+ @Mock
+ private lateinit var mKeyguardPatternView: KeyguardPatternView
+ @Mock
+ private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock
+ private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode
+ @Mock
+ private lateinit var mLockPatternUtils: LockPatternUtils
+ @Mock
+ private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
+ @Mock
+ private lateinit var mLatencyTracker: LatencyTracker
+ @Mock
+ private lateinit
+ var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
+ @Mock
+ private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+ @Mock
+ private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+ @Mock
+ private lateinit var mLockPatternView: LockPatternView
+
+ private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
+ `when`(mKeyguardPatternView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea)
+ `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
+ .thenReturn(mLockPatternView)
+ `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
+ .thenReturn(mKeyguardMessageAreaController)
+ mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView,
+ mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
+ mLatencyTracker, mKeyguardMessageAreaControllerFactory)
+ }
+
+ @Test
+ fun onPause_clearsTextField() {
+ mKeyguardPatternViewController.init()
+ mKeyguardPatternViewController.onPause()
+ verify(mKeyguardMessageAreaController).setMessage("")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt
deleted file mode 100644
index b4363cf..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.keyguard
-
-import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.LayoutInflater
-
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.google.common.truth.Truth.assertThat
-
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class KeyguardPatternViewTest : SysuiTestCase() {
-
- private lateinit var mKeyguardPatternView: KeyguardPatternView
- private lateinit var mSecurityMessage: KeyguardMessageArea
-
- @Before
- fun setup() {
- val inflater = LayoutInflater.from(context)
- mDependency.injectMockDependency(KeyguardUpdateMonitor::class.java)
- mKeyguardPatternView = inflater.inflate(R.layout.keyguard_pattern_view, null)
- as KeyguardPatternView
- mSecurityMessage = KeyguardMessageArea(mContext, null,
- mock(KeyguardUpdateMonitor::class.java), mock(ConfigurationController::class.java))
- mKeyguardPatternView.mSecurityMessageDisplay = mSecurityMessage
- }
-
- @Test
- fun onPause_clearsTextField() {
- mSecurityMessage.setMessage("an old message")
- mKeyguardPatternView.onPause()
- assertThat(mSecurityMessage.text).isEqualTo("")
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
new file mode 100644
index 0000000..4944284
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ private KeyguardPinBasedInputView mPinBasedInputView;
+ @Mock
+ private PasswordTextView mPasswordEntry;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private SecurityMode mSecurityMode;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+ @Mock
+ private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
+ @Mock
+ private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ @Mock
+ private LatencyTracker mLatencyTracker;
+ @Mock
+ private LiftToActivateListener mLiftToactivateListener;
+ @Mock
+ private View mDeleteButton;
+ @Mock
+ private View mOkButton;
+
+ private KeyguardPinBasedInputViewController mKeyguardPinViewController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
+ .thenReturn(mKeyguardMessageAreaController);
+ when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1);
+ when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
+ when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
+ when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea);
+ when(mPinBasedInputView.findViewById(R.id.delete_button))
+ .thenReturn(mDeleteButton);
+ when(mPinBasedInputView.findViewById(R.id.key_enter))
+ .thenReturn(mOkButton);
+ mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
+ mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
+ mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener) {
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ }
+ };
+ mKeyguardPinViewController.init();
+ }
+
+ @Test
+ public void onResume_requestsFocus() {
+ mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON);
+ verify(mPasswordEntry).requestFocus();
+ }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
deleted file mode 100644
index 6666a92..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.keyguard;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-
-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 org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class KeyguardPinBasedInputViewTest extends SysuiTestCase {
-
- @Mock
- private PasswordTextView mPasswordEntry;
- @Mock
- private SecurityMessageDisplay mSecurityMessageDisplay;
- @InjectMocks
- private KeyguardPinBasedInputView mKeyguardPinView;
-
- @Before
- public void setup() {
- LayoutInflater inflater = LayoutInflater.from(getContext());
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
- mKeyguardPinView =
- (KeyguardPinBasedInputView) inflater.inflate(R.layout.keyguard_pin_view, null);
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void onResume_requestsFocus() {
- mKeyguardPinView.onResume(KeyguardSecurityView.SCREEN_ON);
- verify(mPasswordEntry).requestFocus();
- }
-
- @Test
- public void onKeyDown_clearsSecurityMessage() {
- mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_0, mock(KeyEvent.class));
- verify(mSecurityMessageDisplay).setMessage(eq(""));
- }
-
- @Test
- public void onKeyDown_noSecurityMessageInteraction() {
- mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
- verifyZeroInteractions(mSecurityMessageDisplay);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index 559284a..ae159c7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -31,9 +31,7 @@
import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.InjectionInflationController;
import org.junit.After;
import org.junit.Before;
@@ -65,7 +63,6 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
when(mMockKeyguardClockSwitch.getContext()).thenReturn(mContext);
when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
when(mMockKeyguardStatusView.getContext()).thenReturn(mContext);
@@ -77,11 +74,7 @@
allowTestableLooperAsMainThread();
- InjectionInflationController inflationController = new InjectionInflationController(
- SystemUIFactory.getInstance()
- .getSysUIComponent()
- .createViewInstanceCreatorFactory());
- mLayoutInflater = inflationController.injectable(LayoutInflater.from(mContext));
+ mLayoutInflater = LayoutInflater.from(mContext);
mLayoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
new file mode 100644
index 0000000..eef38d3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+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.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.WindowInsetsController;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper()
+public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
+
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
+ @Mock
+ private KeyguardSecurityContainer mView;
+ @Mock
+ private AdminSecondaryLockScreenController.Factory mAdminSecondaryLockScreenControllerFactory;
+ @Mock
+ private AdminSecondaryLockScreenController mAdminSecondaryLockScreenController;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private KeyguardSecurityModel mKeyguardSecurityModel;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private UiEventLogger mUiEventLogger;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private KeyguardInputViewController mInputViewController;
+ @Mock
+ private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
+ @Mock
+ private WindowInsetsController mWindowInsetsController;
+ @Mock
+ private KeyguardSecurityViewFlipper mSecurityViewFlipper;
+ @Mock
+ private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
+
+ private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
+
+ @Before
+ public void setup() {
+ when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
+ .thenReturn(mAdminSecondaryLockScreenController);
+ when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+
+ mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory(
+ mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
+ mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
+ mKeyguardStateController, mKeyguardSecurityViewFlipperController)
+ .create(mSecurityCallback);
+ }
+
+ @Test
+ public void showSecurityScreen_canInflateAllModes() {
+ SecurityMode[] modes = SecurityMode.values();
+ for (SecurityMode mode : modes) {
+ when(mInputViewController.getSecurityMode()).thenReturn(mode);
+ mKeyguardSecurityContainerController.showSecurityScreen(mode);
+ if (mode == SecurityMode.Invalid) {
+ verify(mKeyguardSecurityViewFlipperController, never()).getSecurityView(
+ any(SecurityMode.class), any(KeyguardSecurityCallback.class));
+ } else {
+ verify(mKeyguardSecurityViewFlipperController).getSecurityView(
+ eq(mode), any(KeyguardSecurityCallback.class));
+ }
+ }
+ }
+
+ @Test
+ public void startDisappearAnimation_animatesKeyboard() {
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ SecurityMode.Password);
+ when(mInputViewController.getSecurityMode()).thenReturn(
+ SecurityMode.Password);
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
+ .thenReturn(mInputViewController);
+ mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */);
+
+ mKeyguardSecurityContainerController.startDisappearAnimation(null);
+ verify(mInputViewController).startDisappearAnimation(eq(null));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index a867825..854be1f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -19,23 +19,19 @@
import static android.view.WindowInsets.Type.ime;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.LayoutInflater;
import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Rule;
@@ -50,68 +46,26 @@
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerTest extends SysuiTestCase {
- @Mock
- private KeyguardSecurityModel mKeyguardSecurityModel;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
- @Mock
- private KeyguardSecurityView mSecurityView;
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
@Mock
private WindowInsetsController mWindowInsetsController;
@Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
- @Rule
- public MockitoRule mRule = MockitoJUnit.rule();
+
private KeyguardSecurityContainer mKeyguardSecurityContainer;
@Before
public void setup() {
- mDependency.injectTestDependency(KeyguardStateController.class, mKeyguardStateController);
- mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel);
- mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
- mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext()) {
- @Override
- protected KeyguardSecurityView getSecurityView(
- KeyguardSecurityModel.SecurityMode securityMode) {
- return mSecurityView;
- }
- };
- mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
- mKeyguardSecurityContainer.setSecurityCallback(mSecurityCallback);
- }
-
- @Test
- public void showSecurityScreen_canInflateAllModes() {
- Context context = getContext();
-
- for (int theme : new int[] {R.style.Theme_SystemUI, R.style.Theme_SystemUI_Light}) {
- context.setTheme(theme);
- final LayoutInflater inflater = LayoutInflater.from(context);
- KeyguardSecurityModel.SecurityMode[] modes =
- KeyguardSecurityModel.SecurityMode.values();
- for (KeyguardSecurityModel.SecurityMode mode : modes) {
- final int resId = mKeyguardSecurityContainer.getLayoutIdFor(mode);
- if (resId == 0) {
- continue;
- }
- inflater.inflate(resId, null /* root */, false /* attach */);
- }
- }
+ mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
+ mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
}
@Test
public void startDisappearAnimation_animatesKeyboard() {
- when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
- KeyguardSecurityModel.SecurityMode.Password);
- mKeyguardSecurityContainer.showPrimarySecurityScreen(false /* turningOff */);
-
- mKeyguardSecurityContainer.startDisappearAnimation(null);
- verify(mSecurityView).startDisappearAnimation(eq(null));
+ mKeyguardSecurityContainer.startDisappearAnimation(SecurityMode.Password);
verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
any(), any());
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
new file mode 100644
index 0000000..3b7f4b8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.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.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.view.WindowInsetsController;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper()
+public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
+
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
+ @Mock
+ private KeyguardSecurityViewFlipper mView;
+ @Mock
+ private LayoutInflater mLayoutInflater;
+ @Mock
+ private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory;
+ @Mock
+ private KeyguardInputViewController mKeyguardInputViewController;
+ @Mock
+ private KeyguardInputView mInputView;
+ @Mock
+ private WindowInsetsController mWindowInsetsController;
+ @Mock
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+
+ private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
+
+ @Before
+ public void setup() {
+ when(mKeyguardSecurityViewControllerFactory.create(
+ any(KeyguardInputView.class), any(SecurityMode.class),
+ any(KeyguardSecurityCallback.class)))
+ .thenReturn(mKeyguardInputViewController);
+ when(mView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+
+ mKeyguardSecurityViewFlipperController = new KeyguardSecurityViewFlipperController(mView,
+ mLayoutInflater, mKeyguardSecurityViewControllerFactory);
+ }
+
+ @Test
+ public void showSecurityScreen_canInflateAllModes() {
+ SecurityMode[] modes = SecurityMode.values();
+ // Always return an invalid controller so that we're always making a new one.
+ when(mKeyguardInputViewController.getSecurityMode()).thenReturn(SecurityMode.Invalid);
+ for (SecurityMode mode : modes) {
+ reset(mLayoutInflater);
+ when(mLayoutInflater.inflate(anyInt(), eq(mView), eq(false)))
+ .thenReturn(mInputView);
+ mKeyguardSecurityViewFlipperController.getSecurityView(mode, mKeyguardSecurityCallback);
+ if (mode == SecurityMode.Invalid || mode == SecurityMode.None) {
+ verify(mLayoutInflater, never()).inflate(
+ anyInt(), any(ViewGroup.class), anyBoolean());
+ } else {
+ verify(mLayoutInflater).inflate(anyInt(), eq(mView), eq(false));
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
index 0431704..79ec4f2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
@@ -24,9 +24,7 @@
import android.view.LayoutInflater;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
@@ -50,13 +48,7 @@
@Before
public void setUp() {
allowTestableLooperAsMainThread();
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
- InjectionInflationController inflationController = new InjectionInflationController(
- SystemUIFactory.getInstance()
- .getSysUIComponent()
- .createViewInstanceCreatorFactory());
- LayoutInflater layoutInflater = inflationController
- .injectable(LayoutInflater.from(getContext()));
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
mKeyguardStatusView =
(KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null);
org.mockito.MockitoAnnotations.initMocks(this);
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 64be2d9..d206ea0 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -553,7 +553,6 @@
TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
TestNetworkInterface iface = tnm.createTapInterface();
Log.d(TAG, "Created test interface " + iface.getInterfaceName());
- assertNotNull(NetworkInterface.getByName(iface.getInterfaceName()));
return iface;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 80e9703..ae33f0c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -119,8 +119,8 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
-import com.android.server.accessibility.magnification.MagnificationTransitionController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -219,16 +219,12 @@
// Lazily initialized - access through getSystemActionPerfomer()
private SystemActionPerformer mSystemActionPerformer;
- private FullScreenMagnificationController mFullScreenMagnificationController;
-
private InteractionBridge mInteractionBridge;
private AlertDialog mEnableTouchExplorationDialog;
private AccessibilityInputFilter mInputFilter;
- private WindowMagnificationManager mWindowMagnificationMgr;
-
private boolean mHasInputFilter;
private KeyEventDispatcher mKeyEventDispatcher;
@@ -259,7 +255,7 @@
private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
- private MagnificationTransitionController mMagnificationTransitionController;
+ private MagnificationController mMagnificationController;
private AccessibilityUserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
@@ -292,7 +288,7 @@
SystemActionPerformer systemActionPerformer,
AccessibilityWindowManager a11yWindowManager,
AccessibilityDisplayListener a11yDisplayListener,
- WindowMagnificationManager windowMagnificationMgr) {
+ MagnificationController magnificationController) {
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
@@ -303,8 +299,7 @@
mSystemActionPerformer = systemActionPerformer;
mA11yWindowManager = a11yWindowManager;
mA11yDisplayListener = a11yDisplayListener;
- mWindowMagnificationMgr = windowMagnificationMgr;
- mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
+ mMagnificationController = magnificationController;
init();
}
@@ -324,7 +319,7 @@
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
- mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
+ mMagnificationController = new MagnificationController(this, mLock, mContext);
init();
}
@@ -1195,9 +1190,7 @@
// The user changed.
mCurrentUserId = userId;
- if (mWindowMagnificationMgr != null) {
- mWindowMagnificationMgr.setUserId(mCurrentUserId);
- }
+ mMagnificationController.updateUserIdIfNeeded(mCurrentUserId);
AccessibilityUserState userState = getCurrentUserStateLocked();
readConfigurationForUserStateLocked(userState);
@@ -1554,7 +1547,7 @@
if (fallBackMagnificationModeSettingsLocked(userState)) {
return;
}
- mMagnificationTransitionController.transitionMagnificationModeLocked(
+ mMagnificationController.transitionMagnificationModeLocked(
Display.DEFAULT_DISPLAY, userState.getMagnificationModeLocked(),
this::onMagnificationTransitionEndedLocked);
}
@@ -2310,13 +2303,9 @@
return;
}
- if (mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.setUserId(userState.mUserId);
- }
-
if (mUiAutomationManager.suppressingAccessibilityServicesLocked()
- && mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.unregisterAll();
+ && mMagnificationController.isFullScreenMagnificationControllerInitialized()) {
+ getFullScreenMagnificationController().unregisterAll();
return;
}
@@ -2339,8 +2328,8 @@
final int displayId = display.getDisplayId();
if (userHasListeningMagnificationServicesLocked(userState, displayId)) {
getFullScreenMagnificationController().register(displayId);
- } else if (mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.unregister(displayId);
+ } else if (mMagnificationController.isFullScreenMagnificationControllerInitialized()) {
+ getFullScreenMagnificationController().unregister(displayId);
}
}
}
@@ -2990,10 +2979,7 @@
*/
public WindowMagnificationManager getWindowMagnificationMgr() {
synchronized (mLock) {
- if (mWindowMagnificationMgr == null) {
- mWindowMagnificationMgr = new WindowMagnificationManager(mContext, mCurrentUserId);
- }
- return mWindowMagnificationMgr;
+ return mMagnificationController.getWindowMagnificationMgr();
}
}
@@ -3060,12 +3046,7 @@
@Override
public FullScreenMagnificationController getFullScreenMagnificationController() {
synchronized (mLock) {
- if (mFullScreenMagnificationController == null) {
- mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
- this, mLock);
- mFullScreenMagnificationController.setUserId(mCurrentUserId);
- }
- return mFullScreenMagnificationController;
+ return mMagnificationController.getFullScreenMagnificationController();
}
}
@@ -3307,9 +3288,7 @@
}
}
}
- if (mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.onDisplayRemoved(displayId);
- }
+ mMagnificationController.onDisplayRemoved(displayId);
mA11yWindowManager.stopTrackingWindows(displayId);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
similarity index 66%
rename from services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
rename to services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index af4b34f..0dc5267 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
@@ -29,21 +30,26 @@
import android.util.SparseArray;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
/**
- * Handles magnification mode transition.
+ * Handles all magnification controllers initialization, generic interactions
+ * and magnification mode transition.
*/
-public class MagnificationTransitionController {
+public class MagnificationController {
private static final boolean DEBUG = false;
private static final String TAG = "MagnificationController";
private final AccessibilityManagerService mAms;
private final PointF mTempPoint = new PointF();
private final Object mLock;
+ private final Context mContext;
private final SparseArray<DisableMagnificationCallback>
mMagnificationEndRunnableSparseArray = new SparseArray();
+ private FullScreenMagnificationController mFullScreenMagnificationController;
+ private WindowMagnificationManager mWindowMagnificationMgr;
/**
* A callback to inform the magnification transition result.
@@ -56,9 +62,20 @@
void onResult(boolean success);
}
- public MagnificationTransitionController(AccessibilityManagerService ams, Object lock) {
+ public MagnificationController(AccessibilityManagerService ams, Object lock,
+ Context context) {
mAms = ams;
mLock = lock;
+ mContext = context;
+ }
+
+ @VisibleForTesting
+ public MagnificationController(AccessibilityManagerService ams, Object lock,
+ Context context, FullScreenMagnificationController fullScreenMagnificationController,
+ WindowMagnificationManager windowMagnificationManager) {
+ this(ams, lock, context);
+ mFullScreenMagnificationController = fullScreenMagnificationController;
+ mWindowMagnificationMgr = windowMagnificationManager;
}
/**
@@ -97,7 +114,7 @@
}
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
- final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationManager();
+ final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
final float scale = windowMagnificationMgr.getPersistedScale();
final DisableMagnificationCallback animationEndCallback =
new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
@@ -111,6 +128,39 @@
setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
}
+ /**
+ * Updates the active user ID of {@link FullScreenMagnificationController} and {@link
+ * WindowMagnificationManager}.
+ *
+ * @param userId the currently active user ID
+ */
+ public void updateUserIdIfNeeded(int userId) {
+ synchronized (mLock) {
+ if (mFullScreenMagnificationController != null) {
+ mFullScreenMagnificationController.setUserId(userId);
+ }
+ if (mWindowMagnificationMgr != null) {
+ mWindowMagnificationMgr.setUserId(userId);
+ }
+ }
+ }
+
+ /**
+ * Removes the magnification instance with given id.
+ *
+ * @param displayId The logical display id.
+ */
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ if (mFullScreenMagnificationController != null) {
+ mFullScreenMagnificationController.onDisplayRemoved(displayId);
+ }
+ if (mWindowMagnificationMgr != null) {
+ mWindowMagnificationMgr.onDisplayRemoved(displayId);
+ }
+ }
+ }
+
private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
int displayId) {
return mMagnificationEndRunnableSparseArray.get(displayId);
@@ -125,31 +175,63 @@
}
}
- private FullScreenMagnificationController getFullScreenMagnificationController() {
- return mAms.getFullScreenMagnificationController();
+ /**
+ * Getter of {@link FullScreenMagnificationController}.
+ *
+ * @return {@link FullScreenMagnificationController}.
+ */
+ public FullScreenMagnificationController getFullScreenMagnificationController() {
+ synchronized (mLock) {
+ if (mFullScreenMagnificationController == null) {
+ mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
+ mAms, mLock);
+ mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked());
+ }
+ }
+ return mFullScreenMagnificationController;
}
- private WindowMagnificationManager getWindowMagnificationManager() {
- return mAms.getWindowMagnificationMgr();
+ /**
+ * Is {@link #mFullScreenMagnificationController} is initialized.
+ * @return {code true} if {@link #mFullScreenMagnificationController} is initialized.
+ */
+ public boolean isFullScreenMagnificationControllerInitialized() {
+ synchronized (mLock) {
+ return mFullScreenMagnificationController != null;
+ }
+ }
+
+ /**
+ * Getter of {@link WindowMagnificationManager}.
+ *
+ * @return {@link WindowMagnificationManager}.
+ */
+ public WindowMagnificationManager getWindowMagnificationMgr() {
+ synchronized (mLock) {
+ if (mWindowMagnificationMgr == null) {
+ mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
+ mAms.getCurrentUserIdLocked());
+ }
+ return mWindowMagnificationMgr;
+ }
}
private @Nullable
PointF getCurrentMagnificationBoundsCenterLocked(int displayId, int targetMode) {
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
- final WindowMagnificationManager magnificationManager = getWindowMagnificationManager();
- if (!magnificationManager.isWindowMagnifierEnabled(displayId)) {
+ if (mWindowMagnificationMgr == null
+ || !mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId)) {
return null;
}
- mTempPoint.set(magnificationManager.getCenterX(displayId),
- magnificationManager.getCenterY(displayId));
+ mTempPoint.set(mWindowMagnificationMgr.getCenterX(displayId),
+ mWindowMagnificationMgr.getCenterY(displayId));
} else {
- final FullScreenMagnificationController screenMagnificationController =
- getFullScreenMagnificationController();
- if (!screenMagnificationController.isMagnifying(displayId)) {
+ if (mFullScreenMagnificationController == null
+ || !mFullScreenMagnificationController.isMagnifying(displayId)) {
return null;
}
- mTempPoint.set(screenMagnificationController.getCenterX(displayId),
- screenMagnificationController.getCenterY(displayId));
+ mTempPoint.set(mFullScreenMagnificationController.getCenterX(displayId),
+ mFullScreenMagnificationController.getCenterY(displayId));
}
return mTempPoint;
}
@@ -228,7 +310,7 @@
mCurrentCenter.y, true,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
- getWindowMagnificationManager().enableWindowMagnification(mDisplayId,
+ getWindowMagnificationMgr().enableWindowMagnification(mDisplayId,
mCurrentScale, mCurrentCenter.x,
mCurrentCenter.y);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index c8e485f..ed3085f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -452,6 +452,15 @@
return magnifier;
}
+ /**
+ * Removes the window magnifier with given id.
+ *
+ * @param displayId The logical display id.
+ */
+ void onDisplayRemoved(int displayId) {
+ disableWindowMagnification(displayId, true);
+ }
+
private class ConnectionCallback extends IWindowMagnificationConnectionCallback.Stub implements
IBinder.DeathRecipient {
private boolean mExpiredDeathRecipient = false;
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 34e6370..b874684 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -17,13 +17,13 @@
package com.android.server;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.Slog;
@@ -84,6 +84,13 @@
@GuardedBy("mTargetUsers")
private final SparseArray<TargetUser> mTargetUsers = new SparseArray<>();
+ /**
+ * Reference to the current user, it's used to set the {@link TargetUser} on
+ * {@link #switchUser(int, int)} as the previous user might have been removed already.
+ */
+ @GuardedBy("mTargetUsers")
+ private @Nullable TargetUser mCurrentUser;
+
SystemServiceManager(Context context) {
mContext = context;
}
@@ -259,18 +266,22 @@
return targetUser;
}
+ private @NonNull TargetUser newTargetUser(@UserIdInt int userId) {
+ final UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);
+ Preconditions.checkState(userInfo != null, "No UserInfo for " + userId);
+ return new TargetUser(userInfo);
+ }
+
/**
* Starts the given user.
*/
public void startUser(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) {
- // Create cached TargetUser
- final UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);
- Preconditions.checkState(userInfo != null, "No UserInfo for " + userId);
+ final TargetUser targetUser = newTargetUser(userId);
synchronized (mTargetUsers) {
- mTargetUsers.put(userId, new TargetUser(userInfo));
+ mTargetUsers.put(userId, targetUser);
}
- onUser(t, START, userId);
+ onUser(t, START, /* prevUser= */ null, targetUser);
}
/**
@@ -291,7 +302,26 @@
* Switches to the given user.
*/
public void switchUser(@UserIdInt int from, @UserIdInt int to) {
- onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, to, from);
+ final TargetUser curUser, prevUser;
+ synchronized (mTargetUsers) {
+ if (mCurrentUser == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "First user switch: from " + from + " to " + to);
+ }
+ prevUser = newTargetUser(from);
+ } else {
+ if (from != mCurrentUser.getUserIdentifier()) {
+ Slog.wtf(TAG, "switchUser(" + from + "," + to + "): mCurrentUser is "
+ + mCurrentUser + ", it should be " + from);
+ }
+ prevUser = mCurrentUser;
+ }
+ curUser = mCurrentUser = getTargetUser(to);
+ if (DEBUG) {
+ Slog.d(TAG, "Set mCurrentUser to " + mCurrentUser);
+ }
+ }
+ onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, prevUser, curUser);
}
/**
@@ -314,21 +344,16 @@
}
private void onUser(@NonNull String onWhat, @UserIdInt int userId) {
- onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userId);
+ onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, /* prevUser= */ null,
+ getTargetUser(userId));
}
private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
- @UserIdInt int userId) {
- onUser(t, onWhat, userId, UserHandle.USER_NULL);
- }
-
- private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
- @UserIdInt int curUserId, @UserIdInt int prevUserId) {
+ @Nullable TargetUser prevUser, @NonNull TargetUser curUser) {
+ final int curUserId = curUser.getUserIdentifier();
t.traceBegin("ssm." + onWhat + "User-" + curUserId);
- Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId);
- final TargetUser curUser = getTargetUser(curUserId);
- final TargetUser prevUser = prevUserId == UserHandle.USER_NULL ? null
- : getTargetUser(prevUserId);
+ Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId
+ + (prevUser != null ? " (from " + prevUser + ")" : ""));
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -466,14 +491,16 @@
.append(service.getClass().getSimpleName())
.append("\n");
}
-
- builder.append("Target users: ");
- final int targetUsersSize = mTargetUsers.size();
- for (int i = 0; i < targetUsersSize; i++) {
- mTargetUsers.valueAt(i).dump(builder);
- if (i != targetUsersSize - 1) builder.append(',');
+ synchronized (mTargetUsers) {
+ builder.append("Current user: ").append(mCurrentUser).append('\n');
+ builder.append("Target users: ");
+ final int targetUsersSize = mTargetUsers.size();
+ for (int i = 0; i < targetUsersSize; i++) {
+ mTargetUsers.valueAt(i).dump(builder);
+ if (i != targetUsersSize - 1) builder.append(',');
+ }
+ builder.append('\n');
}
- builder.append('\n');
Slog.e(TAG, builder.toString());
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f1561cab..7ad0f21 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -297,6 +297,7 @@
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
private static final int MSG_DISABLE_AUDIO_FOR_UID = 100;
+ private static final int MSG_INIT_STREAMS_VOLUMES = 101;
// end of messages handled under wakelock
// retry delay in case of failure to indicate system ready to AudioFlinger
@@ -822,7 +823,34 @@
updateStreamVolumeAlias(false /*updateVolumes*/, TAG);
readPersistedSettings();
readUserRestrictions();
- mSettingsObserver = new SettingsObserver();
+
+ mPlaybackMonitor =
+ new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
+ mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
+
+ readAndSetLowRamDevice();
+
+ mIsCallScreeningModeSupported = AudioSystem.isCallScreeningModeSupported();
+
+ if (mSystemServer.isPrivileged()) {
+ LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
+
+ mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
+
+ mRecordMonitor.initMonitor();
+ }
+
+ mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
+
+ // done with service initialization, continue additional work in our Handler thread
+ queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_STREAMS_VOLUMES,
+ 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+ }
+
+ /**
+ * Called by handling of MSG_INIT_STREAMS_VOLUMES
+ */
+ private void onInitStreamsAndVolumes() {
createStreamStates();
// must be called after createStreamStates() as it uses MUSIC volume as default if no
@@ -833,53 +861,11 @@
// relies on audio policy having correct ranges for volume indexes.
mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
- mPlaybackMonitor =
- new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
-
- mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
-
- readAndSetLowRamDevice();
-
- mIsCallScreeningModeSupported = AudioSystem.isCallScreeningModeSupported();
-
// Call setRingerModeInt() to apply correct mute
// state on streams affected by ringer mode.
mRingerAndZenModeMutedStreams = 0;
setRingerModeInt(getRingerModeInternal(), false);
- // Register for device connection intent broadcasts.
- IntentFilter intentFilter =
- new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
- intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
- intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
- intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
- intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
- intentFilter.addAction(Intent.ACTION_USER_BACKGROUND);
- intentFilter.addAction(Intent.ACTION_USER_FOREGROUND);
- intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
- intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- intentFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
-
- intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
- if (mMonitorRotation) {
- RotationHelper.init(mContext, mAudioHandler);
- }
-
- intentFilter.addAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
- intentFilter.addAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
-
- context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
-
- if (mSystemServer.isPrivileged()) {
- LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
-
- mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
-
- mRecordMonitor.initMonitor();
- }
-
final float[] preScale = new float[3];
preScale[0] = mContext.getResources().getFraction(
com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
@@ -896,10 +882,47 @@
}
}
+ initExternalEventReceivers();
+
// check on volume initialization
checkVolumeRangeInitialization("AudioService()");
}
+ /**
+ * Initialize intent receives and settings observers for this service.
+ * Must be called after createStreamStates() as the handling of some events
+ * may affect or need volumes, e.g. BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED
+ * (for intent receiver), or Settings.Global.ZEN_MODE (for settings observer)
+ */
+ private void initExternalEventReceivers() {
+ mSettingsObserver = new SettingsObserver();
+
+ // Register for device connection intent broadcasts.
+ IntentFilter intentFilter =
+ new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+ intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
+ intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
+ intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+ intentFilter.addAction(Intent.ACTION_USER_BACKGROUND);
+ intentFilter.addAction(Intent.ACTION_USER_FOREGROUND);
+ intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ intentFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
+
+ intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ if (mMonitorRotation) {
+ RotationHelper.init(mContext, mAudioHandler);
+ }
+
+ intentFilter.addAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
+ intentFilter.addAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
+
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
+
+ }
+
public void systemReady() {
sendMsg(mAudioHandler, MSG_SYSTEM_READY, SENDMSG_QUEUE,
0, 0, null, 0);
@@ -2593,7 +2616,7 @@
// StreamVolumeCommand contains the information needed to defer the process of
// setStreamVolume() in case the user has to acknowledge the safe volume warning message.
- class StreamVolumeCommand {
+ static class StreamVolumeCommand {
public final int mStreamType;
public final int mIndex;
public final int mFlags;
@@ -2612,7 +2635,7 @@
.append(mIndex).append(",flags=").append(mFlags).append(",device=")
.append(mDevice).append('}').toString();
}
- };
+ }
private int getNewRingerMode(int stream, int index, int flags) {
// setRingerMode does nothing if the device is single volume,so the value would be unchanged
@@ -3321,7 +3344,7 @@
}
private int mRmtSbmxFullVolRefCount = 0;
- private ArrayList<RmtSbmxFullVolDeathHandler> mRmtSbmxFullVolDeathHandlers =
+ private final ArrayList<RmtSbmxFullVolDeathHandler> mRmtSbmxFullVolDeathHandlers =
new ArrayList<RmtSbmxFullVolDeathHandler>();
public void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb) {
@@ -5882,7 +5905,6 @@
private final Intent mStreamDevicesChanged;
private VolumeStreamState(String settingName, int streamType) {
-
mVolumeIndexSettingName = settingName;
mStreamType = streamType;
@@ -6658,6 +6680,11 @@
mAudioEventWakeLock.release();
break;
+ case MSG_INIT_STREAMS_VOLUMES:
+ onInitStreamsAndVolumes();
+ mAudioEventWakeLock.release();
+ break;
+
case MSG_CHECK_MUSIC_ACTIVE:
onCheckMusicActive((String) msg.obj);
break;
@@ -9216,7 +9243,7 @@
}
}
- private HashMap<IBinder, AsdProxy> mAudioServerStateListeners =
+ private final HashMap<IBinder, AsdProxy> mAudioServerStateListeners =
new HashMap<IBinder, AsdProxy>();
private void checkMonitorAudioServerStatePermission() {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5d2f512..5076007 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -836,7 +836,7 @@
return;
}
if (Settings.Global.getInt(getContext().getContentResolver(),
- "clipboard_access_toast_enabled", 0) == 0) {
+ "clipboard_access_toast_enabled", 1) == 0) {
return;
}
// Don't notify if the app accessing the clipboard is the same as the current owner.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 99dc58e..14b3478 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -77,6 +77,7 @@
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -142,6 +143,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -2301,7 +2303,7 @@
void onChildTransformCreated(
@NonNull Network network, @NonNull IpSecTransform transform, int direction);
- void onSessionLost(@NonNull Network network);
+ void onSessionLost(@NonNull Network network, @Nullable Exception exception);
}
/**
@@ -2458,7 +2460,7 @@
networkAgent.sendLinkProperties(lp);
} catch (Exception e) {
Log.d(TAG, "Error in ChildOpened for network " + network, e);
- onSessionLost(network);
+ onSessionLost(network, e);
}
}
@@ -2488,7 +2490,7 @@
mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform);
} catch (IOException e) {
Log.d(TAG, "Transform application failed for network " + network, e);
- onSessionLost(network);
+ onSessionLost(network, e);
}
}
@@ -2546,11 +2548,20 @@
Log.d(TAG, "Ike Session started for network " + network);
} catch (Exception e) {
Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
- onSessionLost(network);
+ onSessionLost(network, e);
}
});
}
+ /** Marks the state as FAILED, and disconnects. */
+ private void markFailedAndDisconnect(Exception exception) {
+ synchronized (Vpn.this) {
+ updateState(DetailedState.FAILED, exception.getMessage());
+ }
+
+ disconnectVpnRunner();
+ }
+
/**
* Handles loss of a session
*
@@ -2560,7 +2571,7 @@
* <p>This method MUST always be called on the mExecutor thread in order to ensure
* consistency of the Ikev2VpnRunner fields.
*/
- public void onSessionLost(@NonNull Network network) {
+ public void onSessionLost(@NonNull Network network, @Nullable Exception exception) {
if (!isActiveNetwork(network)) {
Log.d(TAG, "onSessionLost() called for obsolete network " + network);
@@ -2572,6 +2583,27 @@
return;
}
+ if (exception instanceof IkeProtocolException) {
+ final IkeProtocolException ikeException = (IkeProtocolException) exception;
+
+ switch (ikeException.getErrorType()) {
+ case IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN: // Fallthrough
+ case IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD: // Fallthrough
+ case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED: // Fallthrough
+ case IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED: // Fallthrough
+ case IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED: // Fallthrough
+ case IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE:
+ // All the above failures are configuration errors, and are terminal
+ markFailedAndDisconnect(exception);
+ return;
+ // All other cases possibly recoverable.
+ }
+ } else if (exception instanceof IllegalArgumentException) {
+ // Failed to build IKE/ChildSessionParams; fatal profile configuration error
+ markFailedAndDisconnect(exception);
+ return;
+ }
+
mActiveNetwork = null;
// Close all obsolete state, but keep VPN alive incase a usable network comes up.
@@ -2621,12 +2653,18 @@
}
/**
- * Cleans up all Ikev2VpnRunner internal state
+ * Disconnects and shuts down this VPN.
+ *
+ * <p>This method resets all internal Ikev2VpnRunner state, but unless called via
+ * VpnRunner#exit(), this Ikev2VpnRunner will still be listed as the active VPN of record
+ * until the next VPN is started, or the Ikev2VpnRunner is explicitly exited. This is
+ * necessary to ensure that the detailed state is shown in the Settings VPN menus; if the
+ * active VPN is cleared, Settings VPNs will not show the resultant state or errors.
*
* <p>This method MUST always be called on the mExecutor thread in order to ensure
* consistency of the Ikev2VpnRunner fields.
*/
- private void shutdownVpnRunner() {
+ private void disconnectVpnRunner() {
mActiveNetwork = null;
mIsRunning = false;
@@ -2640,9 +2678,13 @@
@Override
public void exitVpnRunner() {
- mExecutor.execute(() -> {
- shutdownVpnRunner();
- });
+ try {
+ mExecutor.execute(() -> {
+ disconnectVpnRunner();
+ });
+ } catch (RejectedExecutionException ignored) {
+ // The Ikev2VpnRunner has already shut down.
+ }
}
}
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index 103f659..62630300 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -270,13 +270,13 @@
@Override
public void onClosed() {
Log.d(mTag, "IkeClosed for network " + mNetwork);
- mCallback.onSessionLost(mNetwork); // Server requested session closure. Retry?
+ mCallback.onSessionLost(mNetwork, null); // Server requested session closure. Retry?
}
@Override
public void onClosedExceptionally(@NonNull IkeException exception) {
Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception);
- mCallback.onSessionLost(mNetwork);
+ mCallback.onSessionLost(mNetwork, exception);
}
@Override
@@ -306,13 +306,13 @@
@Override
public void onClosed() {
Log.d(mTag, "ChildClosed for network " + mNetwork);
- mCallback.onSessionLost(mNetwork);
+ mCallback.onSessionLost(mNetwork, null);
}
@Override
public void onClosedExceptionally(@NonNull IkeException exception) {
Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception);
- mCallback.onSessionLost(mNetwork);
+ mCallback.onSessionLost(mNetwork, exception);
}
@Override
@@ -349,7 +349,7 @@
@Override
public void onLost(@NonNull Network network) {
Log.d(mTag, "Tearing down; lost network: " + network);
- mCallback.onSessionLost(network);
+ mCallback.onSessionLost(network, null);
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
new file mode 100644
index 0000000..f4f77db
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -0,0 +1,149 @@
+/*
+ * 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.display;
+
+import android.annotation.NonNull;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.display.DisplayManagerService.SyncRoot;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Container for all the display devices present in the system. If an object wants to get events
+ * about all the DisplayDevices without needing to listen to all of the DisplayAdapters, they can
+ * listen and interact with the instance of this class.
+ * <p>
+ * The collection of {@link DisplayDevice}s and their usage is protected by the provided
+ * {@link DisplayManagerService.SyncRoot} lock object.
+ */
+class DisplayDeviceRepository implements DisplayAdapter.Listener {
+ private static final String TAG = "DisplayDeviceRepository";
+
+ public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
+ public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
+ public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3;
+
+ /**
+ * List of all currently connected display devices. Indexed by the displayId.
+ * TODO: multi-display - break the notion that this is indexed by displayId.
+ */
+ @GuardedBy("mSyncRoot")
+ private final List<DisplayDevice> mDisplayDevices = new ArrayList<>();
+
+ /** Listener for {link DisplayDevice} events. */
+ private final Listener mListener;
+
+ /** Global lock object from {@link DisplayManagerService}. */
+ private final SyncRoot mSyncRoot;
+
+ DisplayDeviceRepository(@NonNull SyncRoot syncRoot, @NonNull Listener listener) {
+ mSyncRoot = syncRoot;
+ mListener = listener;
+ }
+
+ @Override
+ public void onDisplayDeviceEvent(DisplayDevice device, int event) {
+ switch (event) {
+ case DISPLAY_DEVICE_EVENT_ADDED:
+ handleDisplayDeviceAdded(device);
+ break;
+
+ case DISPLAY_DEVICE_EVENT_CHANGED:
+ handleDisplayDeviceChanged(device);
+ break;
+
+ case DISPLAY_DEVICE_EVENT_REMOVED:
+ handleDisplayDeviceRemoved(device);
+ break;
+ }
+ }
+
+ @Override
+ public void onTraversalRequested() {
+ mListener.onTraversalRequested();
+ }
+
+ public boolean containsLocked(DisplayDevice d) {
+ return mDisplayDevices.contains(d);
+ }
+
+ public int sizeLocked() {
+ return mDisplayDevices.size();
+ }
+
+ public void forEachLocked(Consumer<DisplayDevice> consumer) {
+ final int count = mDisplayDevices.size();
+ for (int i = 0; i < count; i++) {
+ consumer.accept(mDisplayDevices.get(i));
+ }
+ }
+
+ private void handleDisplayDeviceAdded(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ if (mDisplayDevices.contains(device)) {
+ Slog.w(TAG, "Attempted to add already added display device: " + info);
+ return;
+ }
+ Slog.i(TAG, "Display device added: " + info);
+ device.mDebugLastLoggedDeviceInfo = info;
+
+ mDisplayDevices.add(device);
+ mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
+ }
+ }
+
+ private void handleDisplayDeviceChanged(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ if (!mDisplayDevices.contains(device)) {
+ Slog.w(TAG, "Attempted to change non-existent display device: " + info);
+ return;
+ }
+ mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
+ }
+ }
+
+ private void handleDisplayDeviceRemoved(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ if (!mDisplayDevices.remove(device)) {
+ Slog.w(TAG, "Attempted to remove non-existent display device: " + info);
+ return;
+ }
+
+ Slog.i(TAG, "Display device removed: " + info);
+ device.mDebugLastLoggedDeviceInfo = info;
+ mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
+ }
+ }
+
+ /**
+ * Listens to {@link DisplayDevice} events from {@link DisplayDeviceRepository}.
+ */
+ interface Listener {
+ void onDisplayDeviceEventLocked(DisplayDevice device, int event);
+
+ // TODO: multi-display - Try to remove the need for requestTraversal...it feels like
+ // a shoe-horned method for a shoe-horned feature.
+ void onTraversalRequested();
+ };
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 97c4cf5..597f49c6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -131,7 +131,8 @@
* </p><p>
* Display adapters are only weakly coupled to the display manager service.
* Display adapters communicate changes in display device state to the display manager
- * service asynchronously via a {@link DisplayAdapter.Listener} registered
+ * service asynchronously via a {@link DisplayAdapter.Listener}, and through
+ * the {@link DisplayDeviceRepository.Listener}, which is ultimately registered
* by the display manager service. This separation of concerns is important for
* two main reasons. First, it neatly encapsulates the responsibilities of these
* two classes: display adapters handle individual display devices whereas
@@ -180,7 +181,7 @@
private final Context mContext;
private final DisplayManagerHandler mHandler;
private final Handler mUiHandler;
- private final DisplayAdapterListener mDisplayAdapterListener;
+ private final DisplayDeviceListener mDisplayDeviceListener;
private final DisplayModeDirector mDisplayModeDirector;
private WindowManagerInternal mWindowManagerInternal;
private InputManagerInternal mInputManagerInternal;
@@ -215,8 +216,7 @@
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
- // List of all currently connected display devices.
- private final ArrayList<DisplayDevice> mDisplayDevices = new ArrayList<DisplayDevice>();
+ private final DisplayDeviceRepository mDisplayDeviceRepo;
// List of all logical displays indexed by logical display id.
// Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
@@ -331,7 +331,8 @@
mContext = context;
mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
mUiHandler = UiThread.getHandler();
- mDisplayAdapterListener = new DisplayAdapterListener();
+ mDisplayDeviceListener = new DisplayDeviceListener();
+ mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mDisplayDeviceListener);
mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
Resources resources = mContext.getResources();
@@ -469,6 +470,11 @@
return mHandler;
}
+ @VisibleForTesting
+ DisplayDeviceRepository getDisplayDeviceRepository() {
+ return mDisplayDeviceRepo;
+ }
+
private void loadStableDisplayValuesLocked() {
final Point size = mPersistentDataStore.getStableDisplaySize();
if (size.x > 0 && size.y > 0) {
@@ -818,7 +824,17 @@
return -1;
}
- handleDisplayDeviceAddedLocked(device);
+ // DisplayDevice events are handled manually for Virtual Displays.
+ // TODO: multi-display Fix this so that generic add/remove events are not handled in a
+ // different code path for virtual displays. Currently this happens so that we can
+ // return a valid display ID synchronously upon successful Virtual Display creation.
+ // This code can run on any binder thread, while onDisplayDeviceAdded() callbacks are
+ // called on the DisplayThread (which we don't want to wait for?).
+ // One option would be to actually wait here on the binder thread
+ // to be notified when the virtual display is created (or failed).
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device,
+ DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
+
LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
if (display != null) {
return display.getDisplayIdLocked();
@@ -828,7 +844,8 @@
Slog.w(TAG, "Rejecting request to create virtual display "
+ "because the logical display was not created.");
mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder());
- handleDisplayDeviceRemovedLocked(device);
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device,
+ DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
}
return -1;
}
@@ -863,7 +880,9 @@
DisplayDevice device =
mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
if (device != null) {
- handleDisplayDeviceRemovedLocked(device);
+ // TODO - handle virtual displays the same as other display adapters.
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device,
+ DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
}
}
}
@@ -883,7 +902,7 @@
synchronized (mSyncRoot) {
// main display adapter
registerDisplayAdapterLocked(new LocalDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
+ mSyncRoot, mContext, mHandler, mDisplayDeviceRepo));
// Standalone VR devices rely on a virtual display as their primary display for
// 2D UI. We register virtual display adapter along side the main display adapter
@@ -891,7 +910,7 @@
// early apps like SetupWizard/Launcher. In particular, SUW is displayed using
// the virtual display inside VR before any VR-specific apps even run.
mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
- mHandler, mDisplayAdapterListener);
+ mHandler, mDisplayDeviceRepo);
if (mVirtualDisplayAdapter != null) {
registerDisplayAdapterLocked(mVirtualDisplayAdapter);
}
@@ -909,7 +928,7 @@
private void registerOverlayDisplayAdapterLocked() {
registerDisplayAdapterLocked(new OverlayDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler));
+ mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler));
}
private void registerWifiDisplayAdapterLocked() {
@@ -917,7 +936,7 @@
com.android.internal.R.bool.config_enableWifiDisplay)
|| SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) {
mWifiDisplayAdapter = new WifiDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener,
+ mSyncRoot, mContext, mHandler, mDisplayDeviceRepo,
mPersistentDataStore);
registerDisplayAdapterLocked(mWifiDisplayAdapter);
}
@@ -946,15 +965,6 @@
}
private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
- DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
- if (mDisplayDevices.contains(device)) {
- Slog.w(TAG, "Attempted to add already added display device: " + info);
- return;
- }
- Slog.i(TAG, "Display device added: " + info);
- device.mDebugLastLoggedDeviceInfo = info;
-
- mDisplayDevices.add(device);
LogicalDisplay display = addLogicalDisplayLocked(device);
Runnable work = updateDisplayStateLocked(device);
if (work != null) {
@@ -966,45 +976,45 @@
@VisibleForTesting
void handleDisplayDeviceChanged(DisplayDevice device) {
synchronized (mSyncRoot) {
- DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
- if (!mDisplayDevices.contains(device)) {
- Slog.w(TAG, "Attempted to change non-existent display device: " + info);
- return;
- }
+ handleDisplayDeviceChangedLocked(device);
+ }
+ }
- int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
- if (diff == DisplayDeviceInfo.DIFF_STATE) {
- Slog.i(TAG, "Display device changed state: \"" + info.name
- + "\", " + Display.stateToString(info.state));
- final Optional<Integer> viewportType = getViewportType(info);
- if (viewportType.isPresent()) {
- for (DisplayViewport d : mViewports) {
- if (d.type == viewportType.get() && info.uniqueId.equals(d.uniqueId)) {
- // Update display view port power state
- d.isActive = Display.isActiveState(info.state);
- }
- }
- if (mInputManagerInternal != null) {
- mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
+ private void handleDisplayDeviceChangedLocked(DisplayDevice device) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+
+ int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
+ if (diff == DisplayDeviceInfo.DIFF_STATE) {
+ Slog.i(TAG, "Display device changed state: \"" + info.name
+ + "\", " + Display.stateToString(info.state));
+ final Optional<Integer> viewportType = getViewportType(info);
+ if (viewportType.isPresent()) {
+ for (DisplayViewport d : mViewports) {
+ if (d.type == viewportType.get() && info.uniqueId.equals(d.uniqueId)) {
+ // Update display view port power state
+ d.isActive = Display.isActiveState(info.state);
}
}
- } else if (diff != 0) {
- Slog.i(TAG, "Display device changed: " + info);
- }
- if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) {
- try {
- mPersistentDataStore.setColorMode(device, info.colorMode);
- } finally {
- mPersistentDataStore.saveIfNeeded();
+ if (mInputManagerInternal != null) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
}
}
- device.mDebugLastLoggedDeviceInfo = info;
-
- device.applyPendingDisplayDeviceInfoChangesLocked();
- if (updateLogicalDisplaysLocked()) {
- scheduleTraversalLocked(false);
+ } else if (diff != 0) {
+ Slog.i(TAG, "Display device changed: " + info);
+ }
+ if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) {
+ try {
+ mPersistentDataStore.setColorMode(device, info.colorMode);
+ } finally {
+ mPersistentDataStore.saveIfNeeded();
}
}
+ device.mDebugLastLoggedDeviceInfo = info;
+
+ device.applyPendingDisplayDeviceInfoChangesLocked();
+ if (updateLogicalDisplaysLocked()) {
+ scheduleTraversalLocked(false);
+ }
}
private void handleDisplayDeviceRemoved(DisplayDevice device) {
@@ -1014,15 +1024,6 @@
}
private void handleDisplayDeviceRemovedLocked(DisplayDevice device) {
- DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
- if (!mDisplayDevices.remove(device)) {
- Slog.w(TAG, "Attempted to remove non-existent display device: " + info);
- return;
- }
-
- Slog.i(TAG, "Display device removed: " + info);
- device.mDebugLastLoggedDeviceInfo = info;
-
updateLogicalDisplaysLocked();
scheduleTraversalLocked(false);
}
@@ -1043,14 +1044,12 @@
}
private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
- final int count = mDisplayDevices.size();
- for (int i = 0; i < count; i++) {
- DisplayDevice device = mDisplayDevices.get(i);
+ mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
Runnable runnable = updateDisplayStateLocked(device);
if (runnable != null) {
workQueue.add(runnable);
}
- }
+ });
}
private Runnable updateDisplayStateLocked(DisplayDevice device) {
@@ -1058,6 +1057,8 @@
// by the display power controller (if known).
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
+ // TODO - multi-display - The rules regarding what display state to apply to each
+ // display will depend on the configuration/mapping of logical displays.
return device.requestDisplayStateLocked(
mGlobalDisplayState, mGlobalDisplayBrightness);
}
@@ -1085,7 +1086,7 @@
final int layerStack = assignLayerStackLocked(displayId);
LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
- display.updateLocked(mDisplayDevices);
+ display.updateLocked(mDisplayDeviceRepo);
if (!display.isValidLocked()) {
// This should never happen currently.
Slog.w(TAG, "Ignoring display device because the logical display "
@@ -1248,7 +1249,7 @@
mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
- display.updateLocked(mDisplayDevices);
+ display.updateLocked(mDisplayDeviceRepo);
if (!display.isValidLocked()) {
mLogicalDisplays.removeAt(i);
handleLogicalDisplayRemoved(displayId);
@@ -1276,12 +1277,10 @@
clearViewportsLocked();
// Configure each display device.
- final int count = mDisplayDevices.size();
- for (int i = 0; i < count; i++) {
- DisplayDevice device = mDisplayDevices.get(i);
+ mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
configureDisplayLocked(t, device);
device.performTraversalLocked(t);
- }
+ });
// Tell the input system about these new viewports.
if (mInputManagerInternal != null) {
@@ -1714,11 +1713,11 @@
}
pw.println();
- pw.println("Display Devices: size=" + mDisplayDevices.size());
- for (DisplayDevice device : mDisplayDevices) {
+ pw.println("Display Devices: size=" + mDisplayDeviceRepo.sizeLocked());
+ mDisplayDeviceRepo.forEachLocked(device -> {
pw.println(" " + device.getDisplayDeviceInfoLocked());
device.dumpLocked(ipw);
- }
+ });
final int logicalDisplayCount = mLogicalDisplays.size();
pw.println();
@@ -1864,20 +1863,20 @@
}
}
- private final class DisplayAdapterListener implements DisplayAdapter.Listener {
+ private final class DisplayDeviceListener implements DisplayDeviceRepository.Listener {
@Override
- public void onDisplayDeviceEvent(DisplayDevice device, int event) {
+ public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
switch (event) {
- case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED:
- handleDisplayDeviceAdded(device);
+ case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED:
+ handleDisplayDeviceAddedLocked(device);
break;
- case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED:
- handleDisplayDeviceChanged(device);
+ case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_CHANGED:
+ handleDisplayDeviceChangedLocked(device);
break;
- case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED:
- handleDisplayDeviceRemoved(device);
+ case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED:
+ handleDisplayDeviceRemovedLocked(device);
break;
}
}
@@ -2573,9 +2572,10 @@
}
}
};
+ LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+ DisplayDevice defaultDevice = defaultDisplay.getPrimaryDisplayDeviceLocked();
mDisplayPowerController = new DisplayPowerController(
- mContext, callbacks, handler, sensorManager, blanker,
- mDisplayDevices.get(Display.DEFAULT_DISPLAY));
+ mContext, callbacks, handler, sensorManager, blanker, defaultDevice);
mSensorManager = sensorManager;
}
@@ -2689,9 +2689,7 @@
@Override
public void onOverlayChanged() {
synchronized (mSyncRoot) {
- for (int i = 0; i < mDisplayDevices.size(); i++) {
- mDisplayDevices.get(i).onOverlayChangedLocked();
- }
+ mDisplayDeviceRepo.forEachLocked(DisplayDevice::onOverlayChangedLocked);
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 8556f08..bf8b891 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -28,7 +28,6 @@
import java.io.PrintWriter;
import java.util.Arrays;
-import java.util.List;
import java.util.Objects;
/**
@@ -220,16 +219,16 @@
* The logical display might become invalid if it is attached to a display device
* that no longer exists.
*
- * @param devices The list of all connected display devices.
+ * @param deviceRepo Repository of active {@link DisplayDevice}s.
*/
- public void updateLocked(List<DisplayDevice> devices) {
+ public void updateLocked(DisplayDeviceRepository deviceRepo) {
// Nothing to update if already invalid.
if (mPrimaryDisplayDevice == null) {
return;
}
// Check whether logical display has become invalid.
- if (!devices.contains(mPrimaryDisplayDevice)) {
+ if (!deviceRepo.containsLocked(mPrimaryDisplayDevice)) {
mPrimaryDisplayDevice = null;
return;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index bba248c..a44fabbe 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1702,7 +1702,8 @@
Intent intent = new Intent(ACTION_SHOW_INPUT_METHOD_PICKER)
.setPackage(mContext.getPackageName());
- mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_IMMUTABLE);
mShowOngoingImeSwitcherForPhones = false;
@@ -2530,7 +2531,8 @@
mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.input_method_binding_label);
mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
- mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
+ mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
+ PendingIntent.FLAG_IMMUTABLE));
if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
mLastBindTime = SystemClock.uptimeMillis();
@@ -3463,6 +3465,9 @@
final boolean sameWindowFocused = mCurFocusedWindow == windowToken;
final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
+ final boolean startInputByWinGainedFocus =
+ (startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) != 0;
+
if (sameWindowFocused && isTextEditor) {
if (DEBUG) {
Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
@@ -3506,7 +3511,7 @@
InputBindResult res = null;
switch (softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE) {
case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
- if (!isTextEditor || !doAutoShow) {
+ if (!sameWindowFocused && (!isTextEditor || !doAutoShow)) {
if (LayoutParams.mayUseInputMethod(windowFlags)) {
// There is no focus view, and this window will
// be behind any soft input window, so hide the
@@ -3555,7 +3560,7 @@
}
break;
case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
- if (isImeVisible()) {
+ if (!sameWindowFocused) {
if (DEBUG) Slog.v(TAG, "Window asks to hide input");
hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
@@ -3584,7 +3589,7 @@
if (DEBUG) Slog.v(TAG, "Window asks to always show input");
if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
unverifiedTargetSdkVersion, startInputFlags)) {
- if (!isImeVisible()) {
+ if (!sameWindowFocused) {
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext, missingMethods,
attribute, startInputFlags, startInputReason);
@@ -3603,17 +3608,26 @@
if (!didStart) {
if (attribute != null) {
- if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
+ if (sameWindowFocused) {
+ // On previous platforms, when Dialogs re-gained focus, the Activity behind
+ // would briefly gain focus first, and dismiss the IME.
+ // On R that behavior has been fixed, but unfortunately apps have come
+ // to rely on this behavior to hide the IME when the editor no longer has focus
+ // To maintain compatibility, we are now hiding the IME when we don't have
+ // an editor upon refocusing a window.
+ if (startInputByWinGainedFocus) {
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
+ }
+ res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
+ startInputFlags, startInputReason);
+ } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
|| (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
startInputFlags, startInputReason);
} else {
res = InputBindResult.NO_EDITOR;
}
- } else if (sameWindowFocused) {
- return new InputBindResult(
- InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
- null, null, null, -1, null);
} else {
res = InputBindResult.NULL_EDITOR_INFO;
}
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
index 25ef9fa..c09ade9 100644
--- a/services/core/java/com/android/server/inputmethod/OWNERS
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -4,3 +4,4 @@
yukawa@google.com
tarandeep@google.com
lumark@google.com
+roosa@google.com
diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
new file mode 100644
index 0000000..3801877
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
@@ -0,0 +1,174 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.timezone.ILocationTimeZoneProvider;
+import com.android.internal.location.timezone.ILocationTimeZoneProviderManager;
+import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
+import com.android.server.ServiceWatcher;
+
+import java.util.Objects;
+
+/**
+ * System server-side proxy for ILocationTimeZoneProvider implementations, i.e. this provides the
+ * system server object used to communicate with a remote LocationTimeZoneProvider over Binder,
+ * which could be running in a different process. As "remote" LocationTimeZoneProviders are bound /
+ * unbound this proxy will rebind to the "best" available remote process.
+ */
+class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
+
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ static LocationTimeZoneProviderProxy createAndRegister(
+ @NonNull Context context, @NonNull ThreadingDomain threadingDomain,
+ @NonNull String action, int enableOverlayResId, int nonOverlayPackageResId) {
+ RealLocationTimeZoneProviderProxy proxy = new RealLocationTimeZoneProviderProxy(
+ context, threadingDomain, action, enableOverlayResId, nonOverlayPackageResId);
+ if (proxy.register()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ @NonNull private final ServiceWatcher mServiceWatcher;
+
+ @GuardedBy("mProxyLock")
+ @Nullable private ManagerProxy mManagerProxy;
+
+ @GuardedBy("mProxyLock")
+ @NonNull private LocationTimeZoneProviderRequest mRequest;
+
+ private RealLocationTimeZoneProviderProxy(
+ @NonNull Context context, @NonNull ThreadingDomain threadingDomain,
+ @NonNull String action, int enableOverlayResId,
+ int nonOverlayPackageResId) {
+ super(context, threadingDomain);
+ mManagerProxy = null;
+ mRequest = LocationTimeZoneProviderRequest.EMPTY_REQUEST;
+ mServiceWatcher = new ServiceWatcher(context, action, this::onBind, this::onUnbind,
+ enableOverlayResId, nonOverlayPackageResId);
+ }
+
+ private boolean register() {
+ return mServiceWatcher.register();
+ }
+
+ private void onBind(IBinder binder, ComponentName componentName) throws RemoteException {
+ processServiceWatcherCallbackOnThreadingDomainThread(() -> onBindOnHandlerThread(binder));
+ }
+
+ private void onUnbind() {
+ processServiceWatcherCallbackOnThreadingDomainThread(this::onUnbindOnHandlerThread);
+ }
+
+ private void processServiceWatcherCallbackOnThreadingDomainThread(@NonNull Runnable runnable) {
+ // For simplicity, this code just post()s the runnable to the mThreadingDomain Thread in all
+ // cases. This adds a delay if ServiceWatcher and ThreadingDomain happen to be using the
+ // same thread, but nothing here should be performance critical.
+ mThreadingDomain.post(runnable);
+ }
+
+ private void onBindOnHandlerThread(@NonNull IBinder binder) {
+ mThreadingDomain.assertCurrentThread();
+
+ ILocationTimeZoneProvider provider = ILocationTimeZoneProvider.Stub.asInterface(binder);
+
+ synchronized (mSharedLock) {
+ try {
+ mManagerProxy = new ManagerProxy();
+ provider.setLocationTimeZoneProviderManager(mManagerProxy);
+ trySendCurrentRequest();
+ mListener.onProviderBound();
+ } catch (RemoteException e) {
+ // This is not expected to happen.
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private void onUnbindOnHandlerThread() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ mManagerProxy = null;
+ mListener.onProviderUnbound();
+ }
+ }
+
+ @Override
+ final void setRequest(@NonNull LocationTimeZoneProviderRequest request) {
+ mThreadingDomain.assertCurrentThread();
+
+ Objects.requireNonNull(request);
+ synchronized (mSharedLock) {
+ mRequest = request;
+
+ trySendCurrentRequest();
+ }
+ }
+
+ @GuardedBy("mProxyLock")
+ private void trySendCurrentRequest() {
+ LocationTimeZoneProviderRequest request = mRequest;
+ mServiceWatcher.runOnBinder(binder -> {
+ ILocationTimeZoneProvider service =
+ ILocationTimeZoneProvider.Stub.asInterface(binder);
+ service.setRequest(request);
+ });
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("mRequest=" + mRequest);
+ mServiceWatcher.dump(null, ipw, args);
+ }
+ }
+
+ /**
+ * A system Server-side proxy for the ILocationTimeZoneProviderManager, i.e. this is a local
+ * binder stub. Each "remote" LocationTimeZoneProvider is passed a binder instance that it
+ * then uses to communicate back with the system server, invoking the logic here.
+ */
+ private class ManagerProxy extends ILocationTimeZoneProviderManager.Stub {
+
+ // executed on binder thread
+ @Override
+ public void onLocationTimeZoneEvent(LocationTimeZoneEvent locationTimeZoneEvent) {
+ synchronized (mSharedLock) {
+ if (mManagerProxy != this) {
+ return;
+ }
+ }
+ handleLocationTimeZoneEvent(locationTimeZoneEvent);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaResourceMonitorService.java b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
index e5da9b1..fc7dd30 100644
--- a/services/core/java/com/android/server/media/MediaResourceMonitorService.java
+++ b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
@@ -28,6 +28,8 @@
import com.android.server.SystemService;
+import java.util.List;
+
/** This class provides a system service that monitors media resource usage. */
public class MediaResourceMonitorService extends SystemService {
private static final String TAG = "MediaResourceMonitor";
@@ -60,16 +62,18 @@
if (pkgNames == null) {
return;
}
- UserManager manager = getContext().getSystemService(UserManager.class);
- int[] userIds = manager.getEnabledProfileIds(ActivityManager.getCurrentUser());
- if (userIds == null || userIds.length == 0) {
+ UserManager manager = getContext().createContextAsUser(
+ UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0)
+ .getSystemService(UserManager.class);
+ List<UserHandle> enabledProfiles = manager.getEnabledProfiles();
+ if (enabledProfiles.isEmpty()) {
return;
}
Intent intent = new Intent(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
intent.putExtra(Intent.EXTRA_PACKAGES, pkgNames);
intent.putExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE, type);
- for (int userId : userIds) {
- getContext().sendBroadcastAsUser(intent, UserHandle.of(userId),
+ for (UserHandle userHandle : enabledProfiles) {
+ getContext().sendBroadcastAsUser(intent, userHandle,
android.Manifest.permission.RECEIVE_MEDIA_RESOURCE_USAGE);
}
} finally {
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 18da33c..7257f52 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -40,15 +40,12 @@
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.LinkedList;
-import java.util.concurrent.TimeUnit;
+import java.util.Set;
/**
* Provides an interface to write and query for notification history data for a user from a Protocol
@@ -173,8 +170,8 @@
mFileWriteHandler.post(rnr);
}
- public void deleteConversation(String pkg, String conversationId) {
- RemoveConversationRunnable rcr = new RemoveConversationRunnable(pkg, conversationId);
+ public void deleteConversations(String pkg, Set<String> conversationIds) {
+ RemoveConversationRunnable rcr = new RemoveConversationRunnable(pkg, conversationIds);
mFileWriteHandler.post(rcr);
}
@@ -467,12 +464,12 @@
final class RemoveConversationRunnable implements Runnable {
private String mPkg;
- private String mConversationId;
+ private Set<String> mConversationIds;
private NotificationHistory mNotificationHistory;
- public RemoveConversationRunnable(String pkg, String conversationId) {
+ public RemoveConversationRunnable(String pkg, Set<String> conversationIds) {
mPkg = pkg;
- mConversationId = conversationId;
+ mConversationIds = conversationIds;
}
@VisibleForTesting
@@ -482,10 +479,10 @@
@Override
public void run() {
- if (DEBUG) Slog.d(TAG, "RemoveConversationRunnable " + mPkg + " " + mConversationId);
+ if (DEBUG) Slog.d(TAG, "RemoveConversationRunnable " + mPkg + " " + mConversationIds);
synchronized (mLock) {
// Remove from pending history
- mBuffer.removeConversationFromWrite(mPkg, mConversationId);
+ mBuffer.removeConversationsFromWrite(mPkg, mConversationIds);
Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator();
while (historyFileItr.hasNext()) {
@@ -496,7 +493,8 @@
: new NotificationHistory();
readLocked(af, notificationHistory,
new NotificationHistoryFilter.Builder().build());
- if(notificationHistory.removeConversationFromWrite(mPkg, mConversationId)) {
+ if (notificationHistory.removeConversationsFromWrite(
+ mPkg, mConversationIds)) {
writeLocked(af, notificationHistory);
}
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 69a7ce90..cf3530b 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -38,12 +38,12 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FunctionalUtils;
import com.android.server.IoThread;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* Keeps track of per-user notification histories.
@@ -167,7 +167,7 @@
}
}
- public void deleteConversation(String pkg, int uid, String conversationId) {
+ public void deleteConversations(String pkg, int uid, Set<String> conversationIds) {
synchronized (mLock) {
int userId = UserHandle.getUserId(uid);
final NotificationHistoryDatabase userHistory =
@@ -179,7 +179,7 @@
+ userId);
return;
}
- userHistory.deleteConversation(pkg, conversationId);
+ userHistory.deleteConversations(pkg, conversationIds);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index c301cd2..affdcea 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -19,6 +19,8 @@
import android.app.Notification;
import android.app.NotificationChannel;
+import java.util.Set;
+
public interface NotificationManagerInternal {
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
@@ -28,5 +30,5 @@
void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, int userId);
- void onConversationRemoved(String pkg, int uid, String conversationId);
+ void onConversationRemoved(String pkg, int uid, Set<String> shortcuts);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 79882da..b4c98e0 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5605,8 +5605,8 @@
}
@Override
- public void onConversationRemoved(String pkg, int uid, String conversationId) {
- onConversationRemovedInternal(pkg, uid, conversationId);
+ public void onConversationRemoved(String pkg, int uid, Set<String> shortcuts) {
+ onConversationRemovedInternal(pkg, uid, shortcuts);
}
@GuardedBy("mNotificationLock")
@@ -5835,14 +5835,13 @@
mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));
}
- private void onConversationRemovedInternal(String pkg, int uid, String conversationId) {
+ private void onConversationRemovedInternal(String pkg, int uid, Set<String> shortcuts) {
checkCallerIsSystem();
Preconditions.checkStringNotEmpty(pkg);
- Preconditions.checkStringNotEmpty(conversationId);
- mHistoryManager.deleteConversation(pkg, uid, conversationId);
+ mHistoryManager.deleteConversations(pkg, uid, shortcuts);
List<String> deletedChannelIds =
- mPreferencesHelper.deleteConversation(pkg, uid, conversationId);
+ mPreferencesHelper.deleteConversations(pkg, uid, shortcuts);
for (String channelId : deletedChannelIds) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
UserHandle.getUserId(uid), REASON_CHANNEL_BANNED,
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 9cf9545..bdf98f4 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -79,6 +79,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class PreferencesHelper implements RankingConfig {
@@ -1428,7 +1429,8 @@
}
}
- public @NonNull List<String> deleteConversation(String pkg, int uid, String conversationId) {
+ public @NonNull List<String> deleteConversations(String pkg, int uid,
+ Set<String> conversationIds) {
synchronized (mPackagePreferences) {
List<String> deletedChannelIds = new ArrayList<>();
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
@@ -1438,7 +1440,8 @@
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
- if (conversationId.equals(nc.getConversationId())) {
+ if (nc.getConversationId() != null
+ && conversationIds.contains(nc.getConversationId())) {
nc.setDeleted(true);
LogMaker lm = getChannelLog(nc, pkg);
lm.setType(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b451eaf1..3e7304b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11291,6 +11291,8 @@
mSettings.addRenamedPackageLPw(parsedPackage.getRealPackage(),
originalPkgSetting.name);
mTransferredPackages.add(originalPkgSetting.name);
+ } else {
+ mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName());
}
}
if (pkgSetting.sharedUser != null) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bae36b2a..a922d76 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -485,6 +485,10 @@
return mRenamedPackages.put(pkgName, origPkgName);
}
+ void removeRenamedPackageLPw(String pkgName) {
+ mRenamedPackages.remove(pkgName);
+ }
+
public boolean canPropagatePermissionToInstantApp(String permName) {
return mPermissions.canPropagatePermissionToInstantApp(permName);
}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 865b8a1..524f9ac 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -425,8 +425,7 @@
public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg,
UidPermissionState uidState) {
- int index = pkg.getRequestedPermissions().indexOf(name);
- if (!uidState.hasRequestedPermission(name) && index == -1) {
+ if (!uidState.hasPermissionState(name) && !pkg.getRequestedPermissions().contains(name)) {
throw new SecurityException("Package " + pkg.getPackageName()
+ " has not requested permission " + name);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 840b233..474ce7c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -943,7 +943,7 @@
private boolean checkSinglePermissionInternal(int uid,
@NonNull UidPermissionState uidState, @NonNull String permissionName) {
- if (!uidState.hasPermission(permissionName)) {
+ if (!uidState.isPermissionGranted(permissionName)) {
return false;
}
@@ -1659,7 +1659,7 @@
}
// Permission is already revoked, no need to do anything.
- if (!uidState.hasPermission(permName)) {
+ if (!uidState.isPermissionGranted(permName)) {
return;
}
@@ -2482,12 +2482,12 @@
return Collections.emptySet();
}
if (!ps.getInstantApp(userId)) {
- return uidState.getPermissions();
+ return uidState.getGrantedPermissions();
} else {
// Install permission state is shared among all users, but instant app state is
// per-user, so we can only filter it here unless we make install permission state
// per-user as well.
- final Set<String> instantPermissions = new ArraySet<>(uidState.getPermissions());
+ final Set<String> instantPermissions = new ArraySet<>(uidState.getGrantedPermissions());
instantPermissions.removeIf(permissionName -> {
BasePermission permission = mSettings.getPermission(permissionName);
if (permission == null) {
@@ -2660,7 +2660,7 @@
// Cache newImplicitPermissions before modifing permissionsState as for the shared
// uids the original and new state are the same object
- if (!origState.hasRequestedPermission(permName)
+ if (!origState.hasPermissionState(permName)
&& (pkg.getImplicitPermissions().contains(permName)
|| (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
if (pkg.getImplicitPermissions().contains(permName)) {
@@ -2685,7 +2685,7 @@
SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
String splitPermName = sp.getSplitPermission();
if (sp.getNewPermissions().contains(permName)
- && origState.hasPermission(splitPermName)) {
+ && origState.isPermissionGranted(splitPermName)) {
upgradedActivityRecognitionPermission = splitPermName;
newImplicitPermissions.add(permName);
@@ -2741,7 +2741,7 @@
}
}
- if (grant == GRANT_INSTALL && !allowedSig && !origState.hasPermission(perm)) {
+ if (grant == GRANT_INSTALL && !allowedSig && !origState.isPermissionGranted(perm)) {
// If this is an existing, non-system package, then
// we can't add any new permissions to it. Runtime
// permissions can be added any time - they are dynamic.
@@ -2849,7 +2849,7 @@
}
}
- if (!uidState.hasPermission(bp.name)
+ if (!uidState.isPermissionGranted(bp.name)
&& uidState.grantPermission(bp)
!= PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
@@ -2993,7 +2993,7 @@
boolean supportsRuntimePermissions = pkg.getTargetSdkVersion()
>= Build.VERSION_CODES.M;
- for (String permission : ps.getPermissions()) {
+ for (String permission : ps.getGrantedPermissions()) {
if (!pkg.getImplicitPermissions().contains(permission)) {
BasePermission bp = mSettings.getPermissionLocked(permission);
if (bp.isRuntime()) {
@@ -3050,7 +3050,7 @@
int numSourcePerm = sourcePerms.size();
for (int i = 0; i < numSourcePerm; i++) {
String sourcePerm = sourcePerms.valueAt(i);
- if (ps.hasPermission(sourcePerm)) {
+ if (ps.isPermissionGranted(sourcePerm)) {
if (!isGranted) {
flags = 0;
}
@@ -3155,7 +3155,7 @@
}
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
- if (!origPs.hasRequestedPermission(sourcePerms)) {
+ if (!origPs.hasPermissionState(sourcePerms)) {
boolean inheritsFromInstallPerm = false;
for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
sourcePermNum++) {
@@ -3465,7 +3465,7 @@
if (!allowed && bp.isDevelopment()) {
// For development permissions, a development permission
// is granted only if it was already granted.
- allowed = origPermissions.hasPermission(perm);
+ allowed = origPermissions.isPermissionGranted(perm);
}
if (!allowed && bp.isSetup()
&& ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
@@ -3687,7 +3687,7 @@
continue;
}
- if (uidState.hasPermission(permissionName)) {
+ if (uidState.isPermissionGranted(permissionName)) {
if (oldGrantedRestrictedPermissions.get(userId) == null) {
oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
}
@@ -3749,7 +3749,7 @@
// as whitelisting trumps policy i.e. policy cannot grant a non
// grantable permission.
if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- final boolean isGranted = uidState.hasPermission(permissionName);
+ final boolean isGranted = uidState.isPermissionGranted(permissionName);
if (!isWhitelisted && isGranted) {
mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -3791,7 +3791,7 @@
+ " and user " + userId);
continue;
}
- if (!newUidState.hasPermission(permission)) {
+ if (!newUidState.isPermissionGranted(permission)) {
callback.onPermissionRevoked(pkg.getUid(), userId, null);
break;
}
diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
index b45176b..06a7f8d 100644
--- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -54,119 +54,52 @@
@NonNull
private final Object mLock = new Object();
+ private boolean mMissing;
+
@GuardedBy("mLock")
+ @Nullable
private ArrayMap<String, PermissionState> mPermissions;
+ private boolean mPermissionReviewRequired;
+
@NonNull
private int[] mGlobalGids = NO_GIDS;
- private boolean mMissing;
+ public UidPermissionState() {}
- private boolean mPermissionReviewRequired;
-
- public UidPermissionState() {
- /* do nothing */
- }
-
- public UidPermissionState(@NonNull UidPermissionState prototype) {
- copyFrom(prototype);
- }
-
- /**
- * Gets the global gids, applicable to all users.
- */
- @NonNull
- public int[] getGlobalGids() {
- return mGlobalGids;
- }
-
- /**
- * Sets the global gids, applicable to all users.
- *
- * @param globalGids The global gids.
- */
- public void setGlobalGids(@NonNull int[] globalGids) {
- if (!ArrayUtils.isEmpty(globalGids)) {
- mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
- }
- }
-
- static void invalidateCache() {
- PackageManager.invalidatePackageInfoCache();
- }
-
- /**
- * Initialized this instance from another one.
- *
- * @param other The other instance.
- */
- public void copyFrom(@NonNull UidPermissionState other) {
- if (other == this) {
- return;
- }
-
+ public UidPermissionState(@NonNull UidPermissionState other) {
synchronized (mLock) {
- if (mPermissions != null) {
- if (other.mPermissions == null) {
- mPermissions = null;
- } else {
- mPermissions.clear();
- }
- }
+ mMissing = other.mMissing;
+
if (other.mPermissions != null) {
- if (mPermissions == null) {
- mPermissions = new ArrayMap<>();
- }
- final int permissionCount = other.mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String name = other.mPermissions.keyAt(i);
- PermissionState permissionState = other.mPermissions.valueAt(i);
+ mPermissions = new ArrayMap<>();
+ final int permissionsSize = other.mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final String name = other.mPermissions.keyAt(i);
+ final PermissionState permissionState = other.mPermissions.valueAt(i);
mPermissions.put(name, new PermissionState(permissionState));
}
}
- }
- mGlobalGids = NO_GIDS;
- if (other.mGlobalGids != NO_GIDS) {
- mGlobalGids = other.mGlobalGids.clone();
- }
+ mPermissionReviewRequired = other.mPermissionReviewRequired;
- mMissing = other.mMissing;
-
- mPermissionReviewRequired = other.mPermissionReviewRequired;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final UidPermissionState other = (UidPermissionState) obj;
-
- synchronized (mLock) {
- if (mPermissions == null) {
- if (other.mPermissions != null) {
- return false;
- }
- } else if (!mPermissions.equals(other.mPermissions)) {
- return false;
+ if (other.mGlobalGids != NO_GIDS) {
+ mGlobalGids = other.mGlobalGids.clone();
}
}
+ }
- if (mMissing != other.mMissing) {
- return false;
+ /**
+ * Reset the internal state of this object.
+ */
+ public void reset() {
+ synchronized (mLock) {
+ mMissing = false;
+ mPermissions = null;
+ mPermissionReviewRequired = false;
+ mGlobalGids = NO_GIDS;
+ invalidateCache();
}
-
- if (mPermissionReviewRequired != other.mPermissionReviewRequired) {
- return false;
- }
- return Arrays.equals(mGlobalGids, other.mGlobalGids);
}
/**
@@ -186,233 +119,45 @@
mMissing = missing;
}
- public boolean isPermissionReviewRequired() {
- return mPermissionReviewRequired;
- }
-
/**
- * Gets whether the state has a given permission.
+ * Get whether there is a permission state for a permission.
*
- * @param name The permission name.
- * @return Whether the state has the permission.
+ * @deprecated This used to be named hasRequestedPermission() and its usage is confusing
*/
- public boolean hasPermission(@NonNull String name) {
+ @Deprecated
+ public boolean hasPermissionState(@NonNull String name) {
synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- PermissionState permissionState = mPermissions.get(name);
- return permissionState != null && permissionState.isGranted();
+ return mPermissions != null && mPermissions.containsKey(name);
}
}
/**
- * Returns whether the state has any known request for the given permission name,
- * whether or not it has been granted.
+ * Get whether there is a permission state for any of the permissions.
*
- * @deprecated Not all requested permissions may be here.
+ * @deprecated This used to be named hasRequestedPermission() and its usage is confusing
*/
@Deprecated
- public boolean hasRequestedPermission(@NonNull ArraySet<String> names) {
+ public boolean hasPermissionState(@NonNull ArraySet<String> names) {
synchronized (mLock) {
if (mPermissions == null) {
return false;
}
- for (int i = names.size() - 1; i >= 0; i--) {
- if (mPermissions.get(names.valueAt(i)) != null) {
+ final int namesSize = names.size();
+ for (int i = 0; i < namesSize; i++) {
+ final String name = names.valueAt(i);
+ if (mPermissions.containsKey(name)) {
return true;
}
}
- }
-
- return false;
- }
-
- /**
- * Returns whether the state has any known request for the given permission name,
- * whether or not it has been granted.
- *
- * @deprecated Not all requested permissions may be here.
- */
- @Deprecated
- public boolean hasRequestedPermission(@NonNull String name) {
- return mPermissions != null && (mPermissions.get(name) != null);
- }
-
- /**
- * Gets all permissions for a given device user id regardless if they
- * are install time or runtime permissions.
- *
- * @return The permissions or an empty set.
- */
- @NonNull
- public Set<String> getPermissions() {
- synchronized (mLock) {
- if (mPermissions == null) {
- return Collections.emptySet();
- }
-
- Set<String> permissions = new ArraySet<>(mPermissions.size());
-
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String permission = mPermissions.keyAt(i);
-
- if (hasPermission(permission)) {
- permissions.add(permission);
- }
- }
-
- return permissions;
- }
- }
-
- /**
- * Gets the flags for a permission.
- *
- * @param name The permission name.
- * @return The permission state or null if no such.
- */
- public int getPermissionFlags(@NonNull String name) {
- PermissionState permState = getPermissionState(name);
- if (permState != null) {
- return permState.getFlags();
- }
- return 0;
- }
-
- /**
- * Update the flags associated with a given permission.
- * @param permission The permission whose flags to update.
- * @param flagMask Mask for which flags to change.
- * @param flagValues New values for the mask flags.
- * @return Whether the permission flags changed.
- */
- public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask,
- int flagValues) {
- if (flagMask == 0) {
return false;
}
-
- PermissionState permissionState = ensurePermissionState(permission);
-
- final int oldFlags = permissionState.getFlags();
-
- synchronized (mLock) {
- final boolean updated = permissionState.updateFlags(flagMask, flagValues);
- if (updated) {
- final int newFlags = permissionState.getFlags();
- if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
- && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- mPermissionReviewRequired = true;
- } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
- && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
- if (mPermissionReviewRequired && !hasPermissionRequiringReview()) {
- mPermissionReviewRequired = false;
- }
- }
- }
- return updated;
- }
- }
-
- private boolean hasPermissionRequiringReview() {
- synchronized (mLock) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- final PermissionState permission = mPermissions.valueAt(i);
- if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- return true;
- }
- }
- }
- return false;
- }
-
- public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
- synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- boolean changed = false;
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- PermissionState permissionState = mPermissions.valueAt(i);
- changed |= permissionState.updateFlags(flagMask, flagValues);
- }
- return changed;
- }
}
/**
- * Compute the Linux gids for a given device user from the permissions
- * granted to this user. Note that these are computed to avoid additional
- * state as they are rarely accessed.
+ * Gets the state for a permission or null if none.
*
- * @param userId The device user id.
- * @return The gids for the device user.
- */
- @NonNull
- public int[] computeGids(@UserIdInt int userId) {
- int[] gids = mGlobalGids;
-
- synchronized (mLock) {
- if (mPermissions != null) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- PermissionState permissionState = mPermissions.valueAt(i);
- if (!permissionState.isGranted()) {
- continue;
- }
- final int[] permGids = permissionState.computeGids(userId);
- if (permGids != NO_GIDS) {
- gids = appendInts(gids, permGids);
- }
- }
- }
- }
-
- return gids;
- }
-
- /**
- * Compute the Linux gids for all device users from the permissions
- * granted to these users.
- *
- * @return The gids for all device users.
- */
- @NonNull
- public int[] computeGids(@NonNull int[] userIds) {
- int[] gids = mGlobalGids;
-
- for (int userId : userIds) {
- final int[] userGids = computeGids(userId);
- gids = appendInts(gids, userGids);
- }
-
- return gids;
- }
-
- /**
- * Resets the internal state of this object.
- */
- public void reset() {
- mGlobalGids = NO_GIDS;
-
- synchronized (mLock) {
- mPermissions = null;
- invalidateCache();
- }
-
- mMissing = false;
- mPermissionReviewRequired = false;
- }
-
- /**
- * Gets the state for a permission or null if no such.
- *
- * @param name The permission name.
- * @return The permission state.
+ * @param name the permission name.
+ * @return the permission state.
*/
@Nullable
public PermissionState getPermissionState(@NonNull String name) {
@@ -425,9 +170,9 @@
}
/**
- * Gets all permission states.
+ * Get all permission states.
*
- * @return The permission states or an empty set.
+ * @return the permission states
*/
@NonNull
public List<PermissionState> getPermissionStates() {
@@ -458,15 +203,56 @@
}
/**
+ * Get whether a permission is granted.
+ *
+ * @param name the permission name
+ * @return whether the permission is granted
+ */
+ public boolean isPermissionGranted(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ PermissionState permissionState = mPermissions.get(name);
+ return permissionState != null && permissionState.isGranted();
+ }
+ }
+
+ /**
+ * Get all the granted permissions.
+ *
+ * @return the granted permissions
+ */
+ @NonNull
+ public Set<String> getGrantedPermissions() {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return Collections.emptySet();
+ }
+
+ Set<String> permissions = new ArraySet<>(mPermissions.size());
+ final int permissionsSize = mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+
+ if (permissionState.isGranted()) {
+ permissions.add(permissionState.getName());
+ }
+ }
+ return permissions;
+ }
+ }
+
+ /**
* Grant a permission.
*
- * @param permission The permission to grant.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * @param permission the permission to grantt
+ * @return the operation result, which is either {@link #PERMISSION_OPERATION_SUCCESS},
* or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
* #PERMISSION_OPERATION_FAILURE}.
*/
public int grantPermission(@NonNull BasePermission permission) {
- if (hasPermission(permission.getName())) {
+ if (isPermissionGranted(permission.getName())) {
return PERMISSION_OPERATION_SUCCESS;
}
@@ -483,20 +269,20 @@
/**
* Revoke a permission.
*
- * @param permission The permission to revoke.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * @param permission the permission to revoke
+ * @return the operation result, which is either {@link #PERMISSION_OPERATION_SUCCESS},
* or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
* #PERMISSION_OPERATION_FAILURE}.
*/
public int revokePermission(@NonNull BasePermission permission) {
- final String permissionName = permission.getName();
- if (!hasPermission(permissionName)) {
+ final String name = permission.getName();
+ if (!isPermissionGranted(name)) {
return PERMISSION_OPERATION_SUCCESS;
}
PermissionState permissionState;
synchronized (mLock) {
- permissionState = mPermissions.get(permissionName);
+ permissionState = mPermissions.get(name);
}
if (!permissionState.revoke()) {
@@ -504,34 +290,88 @@
}
if (permissionState.isDefault()) {
- ensureNoPermissionState(permissionName);
+ ensureNoPermissionState(name);
}
return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
: PERMISSION_OPERATION_SUCCESS;
}
- // TODO: fix this to use arraycopy and append all ints in one go
- private static int[] appendInts(int[] current, int[] added) {
- if (current != null && added != null) {
- for (int guid : added) {
- current = ArrayUtils.appendInt(current, guid);
- }
+ /**
+ * Get the flags for a permission.
+ *
+ * @param name the permission name.
+ * @return the permission flags
+ */
+ public int getPermissionFlags(@NonNull String name) {
+ final PermissionState permissionState = getPermissionState(name);
+ if (permissionState == null) {
+ return 0;
}
- return current;
+ return permissionState.getFlags();
+ }
+
+ /**
+ * Update the flags for a permission.
+ *
+ * @param permission the permission name
+ * @param flagMask the mask for the flags
+ * @param flagValues the new values for the masked flags
+ * @return whether the permission flags changed
+ */
+ public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask,
+ int flagValues) {
+ if (flagMask == 0) {
+ return false;
+ }
+
+ synchronized (mLock) {
+ final PermissionState permissionState = ensurePermissionState(permission);
+ final int oldFlags = permissionState.getFlags();
+
+ final boolean updated = permissionState.updateFlags(flagMask, flagValues);
+ if (updated) {
+ final int newFlags = permissionState.getFlags();
+ if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
+ && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ mPermissionReviewRequired = true;
+ } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
+ && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+ if (mPermissionReviewRequired && !hasPermissionRequiringReview()) {
+ mPermissionReviewRequired = false;
+ }
+ }
+ }
+ return updated;
+ }
+ }
+
+ public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ boolean changed = false;
+ final int permissionsSize = mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final PermissionState permissionState = mPermissions.valueAt(i);
+ changed |= permissionState.updateFlags(flagMask, flagValues);
+ }
+ return changed;
+ }
}
@NonNull
private PermissionState ensurePermissionState(@NonNull BasePermission permission) {
- final String permissionName = permission.getName();
+ final String name = permission.getName();
synchronized (mLock) {
if (mPermissions == null) {
mPermissions = new ArrayMap<>();
}
- PermissionState permissionState = mPermissions.get(permissionName);
+ PermissionState permissionState = mPermissions.get(name);
if (permissionState == null) {
permissionState = new PermissionState(permission);
- mPermissions.put(permissionName, permissionState);
+ mPermissions.put(name, permissionState);
}
return permissionState;
}
@@ -548,4 +388,103 @@
}
}
}
+
+ public boolean isPermissionReviewRequired() {
+ return mPermissionReviewRequired;
+ }
+
+ private boolean hasPermissionRequiringReview() {
+ synchronized (mLock) {
+ final int permissionsSize = mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final PermissionState permission = mPermissions.valueAt(i);
+ if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Gets the global gids, applicable to all users.
+ */
+ @NonNull
+ public int[] getGlobalGids() {
+ return mGlobalGids;
+ }
+
+ /**
+ * Sets the global gids, applicable to all users.
+ *
+ * @param globalGids The global gids.
+ */
+ public void setGlobalGids(@NonNull int[] globalGids) {
+ if (!ArrayUtils.isEmpty(globalGids)) {
+ mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
+ } else {
+ mGlobalGids = NO_GIDS;
+ }
+ }
+
+ /**
+ * Compute the Linux GIDs from the permissions granted to a user.
+ *
+ * @param userId the user ID
+ * @return the GIDs for the user
+ */
+ @NonNull
+ public int[] computeGids(@UserIdInt int userId) {
+ int[] gids = mGlobalGids;
+
+ synchronized (mLock) {
+ if (mPermissions != null) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+ if (!permissionState.isGranted()) {
+ continue;
+ }
+ final int[] permGids = permissionState.computeGids(userId);
+ if (permGids != NO_GIDS) {
+ gids = appendInts(gids, permGids);
+ }
+ }
+ }
+ }
+
+ return gids;
+ }
+
+ /**
+ * Compute the Linux GIDs from the permissions granted to specified users.
+ *
+ * @param userIds the user IDs
+ * @return the GIDs for the user
+ */
+ @NonNull
+ public int[] computeGids(@NonNull int[] userIds) {
+ int[] gids = mGlobalGids;
+
+ for (final int userId : userIds) {
+ final int[] userGids = computeGids(userId);
+ gids = appendInts(gids, userGids);
+ }
+
+ return gids;
+ }
+
+ // TODO: fix this to use arraycopy and append all ints in one go
+ private static int[] appendInts(int[] current, int[] added) {
+ if (current != null && added != null) {
+ for (int guid : added) {
+ current = ArrayUtils.appendInt(current, guid);
+ }
+ }
+ return current;
+ }
+
+ static void invalidateCache() {
+ PackageManager.invalidatePackageInfoCache();
+ }
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 8ccbbdd..cfceaabf 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -22,7 +22,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -33,6 +35,7 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -51,6 +54,7 @@
import android.media.tv.ITvInputServiceCallback;
import android.media.tv.ITvInputSession;
import android.media.tv.ITvInputSessionCallback;
+import android.media.tv.TvChannelInfo;
import android.media.tv.TvContentRating;
import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvContract;
@@ -78,6 +82,7 @@
import android.view.InputChannel;
import android.view.Surface;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
@@ -108,6 +113,9 @@
private static final boolean DEBUG = false;
private static final String TAG = "TvInputManagerService";
private static final String DVB_DIRECTORY = "/dev/dvb";
+ private static final int APP_TAG_SELF = TvChannelInfo.APP_TAG_SELF;
+ private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS =
+ "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS";
// There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d,
// another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the
@@ -136,6 +144,8 @@
private final WatchLogHandler mWatchLogHandler;
+ private final ActivityManager mActivityManager;
+
public TvInputManagerService(Context context) {
super(context);
@@ -144,6 +154,9 @@
IoThread.get().getLooper());
mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
+ mActivityManager =
+ (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
+
synchronized (mLock) {
getOrCreateUserStateLocked(mCurrentUserId);
}
@@ -686,14 +699,18 @@
SessionState sessionState = null;
try {
sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+ UserState userState = getOrCreateUserStateLocked(userId);
if (sessionState.session != null) {
- UserState userState = getOrCreateUserStateLocked(userId);
if (sessionToken == userState.mainSessionToken) {
setMainLocked(sessionToken, false, callingUid, userId);
}
sessionState.session.asBinder().unlinkToDeath(sessionState, 0);
sessionState.session.release();
}
+ sessionState.isCurrent = false;
+ sessionState.currentChannel = null;
+ notifyCurrentChannelInfosUpdatedLocked(
+ userState, getCurrentTvChannelInfosInternalLocked(userState));
} catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in releaseSession", e);
} finally {
@@ -834,6 +851,22 @@
}
}
+ private void notifyCurrentChannelInfosUpdatedLocked(
+ UserState userState, List<TvChannelInfo> infos) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyCurrentChannelInfosUpdatedLocked");
+ }
+ int n = userState.mCallbacks.beginBroadcast();
+ for (int i = 0; i < n; ++i) {
+ try {
+ userState.mCallbacks.getBroadcastItem(i).onCurrentTvChannelInfosUpdated(infos);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "failed to report updated current channel infos to callback", e);
+ }
+ }
+ userState.mCallbacks.finishBroadcast();
+ }
+
private void updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) {
if (DEBUG) {
Slog.d(TAG, "updateTvInputInfoLocked(inputInfo=" + inputInfo + ")");
@@ -1394,13 +1427,19 @@
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
channelUri, params);
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+ if (sessionState != null) {
+ sessionState.isCurrent = true;
+ sessionState.currentChannel = channelUri;
+ notifyCurrentChannelInfosUpdatedLocked(
+ userState, getCurrentTvChannelInfosInternalLocked(userState));
+ }
if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
// Do not log the watch history for passthrough inputs.
return;
}
- UserState userState = getOrCreateUserStateLocked(resolvedUserId);
- SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (sessionState.isRecordingSession) {
return;
}
@@ -2049,6 +2088,21 @@
return clientPid;
}
+ @Override
+ public List<TvChannelInfo> getCurrentTvChannelInfos(@UserIdInt int userId) {
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, "getTvCurrentChannelInfos");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ return getCurrentTvChannelInfosInternalLocked(userState);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
/**
* Add a hardware device in the TvInputHardwareManager for CTS testing
* purpose.
@@ -2219,6 +2273,69 @@
}
}
+ private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(UserState userState) {
+ List<TvChannelInfo> channelInfos = new ArrayList<>();
+ boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission();
+ for (SessionState state : userState.sessionStateMap.values()) {
+ if (state.isCurrent) {
+ Integer appTag;
+ int appType;
+ if (state.callingUid == Binder.getCallingUid()) {
+ appTag = APP_TAG_SELF;
+ appType = TvChannelInfo.APP_TYPE_SELF;
+ } else {
+ appTag = userState.mAppTagMap.get(state.callingUid);
+ if (appTag == null) {
+ appTag = userState.mNextAppTag++;
+ userState.mAppTagMap.put(state.callingUid, appTag);
+ }
+ appType = isSystemApp(state.componentName.getPackageName())
+ ? TvChannelInfo.APP_TYPE_SYSTEM
+ : TvChannelInfo.APP_TYPE_NON_SYSTEM;
+ }
+ channelInfos.add(new TvChannelInfo(
+ state.inputId,
+ watchedProgramsAccess ? state.currentChannel : null,
+ state.isRecordingSession,
+ isForeground(state.callingPid),
+ appType,
+ appTag));
+ }
+ }
+ return channelInfos;
+ }
+
+ private boolean isForeground(int pid) {
+ if (mActivityManager == null) {
+ return false;
+ }
+ List<RunningAppProcessInfo> appProcesses = mActivityManager.getRunningAppProcesses();
+ if (appProcesses == null) {
+ return false;
+ }
+ for (RunningAppProcessInfo appProcess : appProcesses) {
+ if (appProcess.pid == pid
+ && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasAccessWatchedProgramsPermission() {
+ return mContext.checkCallingPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean isSystemApp(String pkg) {
+ try {
+ return (mContext.getPackageManager().getApplicationInfo(pkg, 0).flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0;
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
private static final class UserState {
// A mapping from the TV input id to its TvInputState.
private Map<String, TvInputState> inputMap = new HashMap<>();
@@ -2250,6 +2367,11 @@
// service.
private final PersistentDataStore persistentDataStore;
+ @GuardedBy("mLock")
+ private final Map<Integer, Integer> mAppTagMap = new HashMap<>();
+ @GuardedBy("mLock")
+ private int mNextAppTag = 1;
+
private UserState(Context context, int userId) {
persistentDataStore = new PersistentDataStore(context, userId);
}
@@ -2336,6 +2458,9 @@
// Not null if this session represents an external device connected to a hardware TV input.
private IBinder hardwareSessionToken;
+ private boolean isCurrent = false;
+ private Uri currentChannel = null;
+
private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
int callingPid, int userId, String sessionId) {
@@ -2584,6 +2709,11 @@
if (mSessionState.session == null || mSessionState.client == null) {
return;
}
+ mSessionState.isCurrent = true;
+ mSessionState.currentChannel = channelUri;
+ UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
+ notifyCurrentChannelInfosUpdatedLocked(
+ userState, getCurrentTvChannelInfosInternalLocked(userState));
try {
// TODO: Consider adding this channel change in the watch log. When we do
// that, how we can protect the watch log from malicious tv inputs should
diff --git a/services/core/java/com/android/server/utils/XmlName.java b/services/core/java/com/android/server/utils/XmlName.java
new file mode 100644
index 0000000..c0e22da
--- /dev/null
+++ b/services/core/java/com/android/server/utils/XmlName.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specify the XML name for the annotated field or persistence class.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target({ ElementType.FIELD, ElementType.TYPE })
+public @interface XmlName {
+ String value();
+}
diff --git a/services/core/java/com/android/server/utils/XmlPersistence.java b/services/core/java/com/android/server/utils/XmlPersistence.java
new file mode 100644
index 0000000..8900a9d
--- /dev/null
+++ b/services/core/java/com/android/server/utils/XmlPersistence.java
@@ -0,0 +1,36 @@
+/*
+ * 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.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Generate XML persistence for the annotated class.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+public @interface XmlPersistence {
+ /**
+ * Name for the generated XML persistence class.
+ * <p>
+ * The name defaults to the target class name appended with {@code Persistence} if unspecified.
+ */
+ String value();
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 44462c3..30f6fa6 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -133,6 +133,7 @@
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
+ "android.hardware.gnss-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 4e53aa2..91645ba 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -30,9 +30,12 @@
#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
#include <android/hardware/gnss/2.1/IGnssMeasurement.h>
#include <android/hardware/gnss/3.0/IGnssPsds.h>
+#include <android/hardware/gnss/BnGnss.h>
+#include <android/hardware/gnss/BnGnssPsdsCallback.h>
#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
#include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h>
#include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h>
+#include <binder/IServiceManager.h>
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
@@ -142,9 +145,10 @@
using android::OK;
using android::sp;
-using android::wp;
using android::status_t;
using android::String16;
+using android::wp;
+using android::binder::Status;
using android::hardware::Return;
using android::hardware::Void;
@@ -152,6 +156,7 @@
using android::hardware::hidl_string;
using android::hardware::hidl_death_recipient;
+using android::hardware::gnss::PsdsType;
using android::hardware::gnss::V1_0::GnssLocationFlags;
using android::hardware::gnss::V1_0::IAGnssRilCallback;
using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
@@ -161,11 +166,10 @@
using android::hardware::gnss::V1_0::IGnssNi;
using android::hardware::gnss::V1_0::IGnssNiCallback;
using android::hardware::gnss::V1_0::IGnssXtra;
+using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
using android::hardware::gnss::V3_0::IGnssPsds;
using android::hardware::gnss::V3_0::IGnssPsdsCallback;
-using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
-
using MeasurementCorrections_V1_0 = android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
using MeasurementCorrections_V1_1 = android::hardware::gnss::measurement_corrections::V1_1::MeasurementCorrections;
@@ -224,6 +228,10 @@
using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControlCallback;
+using IGnssAidl = android::hardware::gnss::IGnss;
+using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
+using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
+
struct GnssDeathRecipient : virtual public hidl_death_recipient
{
// hidl_death_recipient interface
@@ -245,7 +253,9 @@
sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
sp<IGnss_V2_1> gnssHal_V2_1 = nullptr;
sp<IGnss_V3_0> gnssHal_V3_0 = nullptr;
+sp<IGnssAidl> gnssHalAidl = nullptr;
sp<IGnssPsds> gnssPsdsIface = nullptr;
+sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
sp<IAGnssRil_V2_0> agnssRilIface_V2_0 = nullptr;
@@ -445,6 +455,19 @@
}
}
+static jboolean checkAidlStatus(const Status& status, const char* errorMessage,
+ const bool success) {
+ if (!status.isOk()) {
+ ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str());
+ return JNI_FALSE;
+ }
+ if (!success) {
+ ALOGE("AIDL return failure: %s", errorMessage);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
jobject version = env->NewObject(class_gnssConfiguration_halInterfaceVersion,
method_halInterfaceVersionCtor, major, minor);
@@ -928,20 +951,31 @@
* GnssPsdsCallback class implements the callback methods for the IGnssPsds
* interface.
*/
+struct GnssPsdsCallbackAidl : public android::hardware::gnss::BnGnssPsdsCallback {
+ Status downloadRequestCb(PsdsType psdsType) override {
+ ALOGD("%s. psdsType: %d", __func__, static_cast<int32_t>(psdsType));
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+ }
+};
+
+/*
+ * GnssPsdsCallback class implements the callback methods for the IGnssPsds
+ * interface.
+ */
struct GnssPsdsCallback : public IGnssPsdsCallback {
Return<void> downloadRequestCb() override;
Return<void> downloadRequestCb_3_0(int32_t psdsType) override;
};
Return<void> GnssPsdsCallback::downloadRequestCb() {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, /* psdsType= */ 1);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
+ return downloadRequestCb_3_0(/* psdsType= */ 1);
}
Return<void> GnssPsdsCallback::downloadRequestCb_3_0(int32_t psdsType) {
- ALOGD("%s: %d", __func__, psdsType);
+ ALOGD("%s. psdsType: %d", __func__, psdsType);
JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -1917,6 +1951,11 @@
/* Initializes the GNSS service handle. */
static void android_location_GnssLocationProvider_set_gps_service_handle() {
+ gnssHalAidl = waitForVintfService<IGnssAidl>();
+ if (gnssHalAidl != nullptr) {
+ ALOGD("Successfully got GNSS AIDL handle.");
+ }
+
ALOGD("Trying IGnss_V3_0::getService()");
gnssHal_V3_0 = IGnss_V3_0::getService();
if (gnssHal_V3_0 != nullptr) {
@@ -2168,7 +2207,15 @@
ALOGD("Link to death notification successful");
}
- if (gnssHal_V3_0 != nullptr) {
+ if (gnssHalAidl != nullptr) {
+ sp<IGnssPsdsAidl> gnssPsdsAidl;
+ auto status = gnssHalAidl->getExtensionPsds(&gnssPsdsAidl);
+ if (status.isOk()) {
+ gnssPsdsAidlIface = gnssPsdsAidl;
+ } else {
+ ALOGD("Unable to get a handle to PSDS AIDL interface.");
+ }
+ } else if (gnssHal_V3_0 != nullptr) {
auto gnssPsds = gnssHal_V3_0->getExtensionPsds();
if (!gnssPsds.isOk()) {
ALOGD("Unable to get a handle to Psds");
@@ -2470,19 +2517,28 @@
}
// Set IGnssPsds or IGnssXtra callback.
- sp<IGnssPsdsCallback> gnssPsdsCbIface = new GnssPsdsCallback();
- if (gnssPsdsIface != nullptr) {
- result = gnssPsdsIface->setCallback_3_0(gnssPsdsCbIface);
- if (!checkHidlReturn(result, "IGnssPsds setCallback() failed.")) {
- gnssPsdsIface = nullptr;
- }
- } else if (gnssXtraIface != nullptr) {
- result = gnssXtraIface->setCallback(gnssPsdsCbIface);
- if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
- gnssXtraIface = nullptr;
+ if (gnssPsdsAidlIface != nullptr) {
+ sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl();
+ bool success;
+ auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl, &success);
+ if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.", success)) {
+ gnssPsdsAidlIface = nullptr;
}
} else {
- ALOGI("Unable to initialize IGnssPsds/IGnssXtra interface.");
+ sp<IGnssPsdsCallback> gnssPsdsCbIface = new GnssPsdsCallback();
+ if (gnssPsdsIface != nullptr) {
+ result = gnssPsdsIface->setCallback_3_0(gnssPsdsCbIface);
+ if (!checkHidlReturn(result, "IGnssPsds setCallback() failed.")) {
+ gnssPsdsIface = nullptr;
+ }
+ } else if (gnssXtraIface != nullptr) {
+ result = gnssXtraIface->setCallback(gnssPsdsCbIface);
+ if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
+ gnssXtraIface = nullptr;
+ }
+ } else {
+ ALOGI("Unable to initialize IGnssPsds/IGnssXtra interface.");
+ }
}
// Set IAGnss.hal callback.
@@ -2743,19 +2799,29 @@
static jboolean android_location_GnssLocationProvider_supports_psds(
JNIEnv* /* env */, jobject /* obj */) {
- return (gnssPsdsIface != nullptr || gnssXtraIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+ return (gnssPsdsAidlIface != nullptr || gnssPsdsIface != nullptr || gnssXtraIface != nullptr)
+ ? JNI_TRUE
+ : JNI_FALSE;
}
static void android_location_GnssLocationProvider_inject_psds_data(JNIEnv* env, jobject /* obj */,
jbyteArray data, jint length,
jint psdsType) {
- if (gnssPsdsIface == nullptr && gnssXtraIface == nullptr) {
+ if (gnssPsdsAidlIface == nullptr && gnssPsdsIface == nullptr && gnssXtraIface == nullptr) {
ALOGE("%s: IGnssPsds or IGnssXtra interface not available.", __func__);
return;
}
jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0));
- if (gnssPsdsIface != nullptr) {
+ if (gnssPsdsAidlIface != nullptr) {
+ bool success;
+ auto status = gnssPsdsAidlIface->injectPsdsData(static_cast<PsdsType>(psdsType),
+ std::vector<uint8_t>((const uint8_t*)bytes,
+ (const uint8_t*)bytes +
+ length),
+ &success);
+ checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.", success);
+ } else if (gnssPsdsIface != nullptr) {
auto result = gnssPsdsIface->injectPsdsData_3_0(psdsType,
std::string((const char*)bytes, length));
checkHidlReturn(result, "IGnssPsds injectPsdsData() failed.");
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 4d11295..52fec33 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -74,6 +74,7 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -757,14 +758,16 @@
Slog.e(TAG, "Package not found: " + packageName, e);
}
PackageData packageData = getPackage(packageName, user.getIdentifier());
+ Set<String> shortcutIds = new HashSet<>();
for (ShortcutInfo shortcutInfo : shortcuts) {
if (packageData != null) {
packageData.deleteDataForConversation(shortcutInfo.getId());
}
- if (uid != Process.INVALID_UID) {
- mNotificationManagerInternal.onConversationRemoved(
- shortcutInfo.getPackage(), uid, shortcutInfo.getId());
- }
+ shortcutIds.add(shortcutInfo.getId());
+ }
+ if (uid != Process.INVALID_UID) {
+ mNotificationManagerInternal.onConversationRemoved(
+ packageName, uid, shortcutIds);
}
});
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index b7355ce..b9c2e56 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -49,6 +49,7 @@
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
+import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -94,6 +95,7 @@
@Mock private IBinder mMockBinder;
@Mock private IAccessibilityServiceClient mMockServiceClient;
@Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
+ @Mock private MagnificationController mMockMagnificationController;
private AccessibilityUserState mUserState;
private MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
@@ -110,6 +112,8 @@
LocalServices.addService(
ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
+ when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
+ mMockWindowMagnificationMgr);
mA11yms = new AccessibilityManagerService(
InstrumentationRegistry.getContext(),
mMockPackageManager,
@@ -117,7 +121,7 @@
mMockSystemActionPerformer,
mMockA11yWindowManager,
mMockA11yDisplayListener,
- mMockWindowMagnificationMgr);
+ mMockMagnificationController);
final AccessibilityUserState userState = new AccessibilityUserState(
mA11yms.getCurrentUserIdLocked(), mMockContext, mA11yms);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
similarity index 85%
rename from services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
rename to services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index cd8e39c..3e34f8a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -62,7 +62,7 @@
* Tests for MagnificationController.
*/
@RunWith(AndroidJUnit4.class)
-public class MagnificationTransitionControllerTest {
+public class MagnificationControllerTest {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
private static final Region MAGNIFICATION_REGION = new Region(0, 0, 500, 600);
@@ -75,7 +75,7 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock private AccessibilityManagerService mService;
- @Mock private MagnificationTransitionController.TransitionCallBack mTransitionCallBack;
+ @Mock private MagnificationController.TransitionCallBack mTransitionCallBack;
@Mock private Context mContext;
@Mock private FullScreenMagnificationController mScreenMagnificationController;
@Captor private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
@@ -83,7 +83,7 @@
private MockWindowMagnificationConnection mMockConnection;
private WindowMagnificationManager mWindowMagnificationManager;
private MockContentResolver mMockResolver;
- private MagnificationTransitionController mMagnificationTransitionController;
+ private MagnificationController mMagnificationController;
@Before
public void setUp() throws Exception {
@@ -95,14 +95,12 @@
Settings.Secure.putFloatForUser(mMockResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE,
CURRENT_USER_ID);
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID);
- when(mService.getFullScreenMagnificationController()).thenReturn(
- mScreenMagnificationController);
- when(mService.getWindowMagnificationMgr()).thenReturn(mWindowMagnificationManager);
+ mWindowMagnificationManager = Mockito.spy(
+ new WindowMagnificationManager(mContext, CURRENT_USER_ID));
mMockConnection = new MockWindowMagnificationConnection(true);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mMagnificationTransitionController = new MagnificationTransitionController(mService,
- new Object());
+ mMagnificationController = new MagnificationController(mService, new Object(), mContext,
+ mScreenMagnificationController, mWindowMagnificationManager);
}
@After
@@ -113,7 +111,7 @@
@Test
public void transitionToWindowMode_notMagnifying_doNothing() throws RemoteException {
setMagnificationModeSettings(MODE_FULLSCREEN);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
@@ -130,7 +128,7 @@
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
@@ -147,11 +145,11 @@
public void transitionToWindowMode_disablingWindowMode_enablingWindowWithFormerCenter()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
@@ -166,7 +164,7 @@
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
mMockConnection.invokeCallbacks();
@@ -186,7 +184,7 @@
magnificationBounds.bottom + 100);
setMagnificationEnabled(MODE_WINDOW, magnifiedCenter.x, magnifiedCenter.y);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
mMockConnection.invokeCallbacks();
@@ -202,11 +200,11 @@
public void transitionToFullScreenMode_disablingFullScreen_enableFullScreenWithFormerCenter()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
@@ -221,7 +219,7 @@
public void interruptDuringTransitionToFullScreenMode_windowMagnifying_notifyTransitionFailed()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
@@ -237,7 +235,23 @@
verify(mTransitionCallBack).onResult(false);
}
+ @Test
+ public void onDisplayRemoved_notifyAllModules() {
+ mMagnificationController.onDisplayRemoved(TEST_DISPLAY);
+
+ verify(mScreenMagnificationController).onDisplayRemoved(TEST_DISPLAY);
+ verify(mWindowMagnificationManager).onDisplayRemoved(TEST_DISPLAY);
+ }
+
+ @Test
+ public void updateUserIdIfNeeded_AllModulesAvailable_setUserId() {
+ mMagnificationController.updateUserIdIfNeeded(CURRENT_USER_ID);
+
+ verify(mScreenMagnificationController).setUserId(CURRENT_USER_ID);
+ verify(mWindowMagnificationManager).setUserId(CURRENT_USER_ID);
+ }
private void setMagnificationEnabled(int mode) throws RemoteException {
+
setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 89b0a03..d5be3ed 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -369,6 +369,16 @@
assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
}
+ @Test
+ public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+ mWindowMagnificationManager.onDisplayRemoved(TEST_DISPLAY);
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
final int len = pointersLocation.length;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 73dda07..da25fd6 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -309,7 +309,8 @@
zeroRect, new Rect(0, 0, 10, 10), zeroRect, zeroRect);
displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY;
displayDevice.setDisplayDeviceInfo(displayDeviceInfo);
- displayManager.handleDisplayDeviceAdded(displayDevice);
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
// Find the display id of the added FakeDisplayDevice
DisplayManagerService.BinderService bs = displayManager.new BinderService();
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index 301a9fe..b312e52 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -28,8 +28,6 @@
import org.junit.Before;
import org.junit.Test;
-import java.util.ArrayList;
-
public class LogicalDisplayTest {
private static final int DISPLAY_ID = 0;
private static final int LAYER_STACK = 0;
@@ -51,9 +49,16 @@
mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(displayDeviceInfo);
- ArrayList<DisplayDevice> displayDevices = new ArrayList<>();
- displayDevices.add(mDisplayDevice);
- mLogicalDisplay.updateLocked(displayDevices);
+ DisplayDeviceRepository repo = new DisplayDeviceRepository(
+ new DisplayManagerService.SyncRoot(), new DisplayDeviceRepository.Listener() {
+ @Override
+ public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {}
+
+ @Override
+ public void onTraversalRequested() {}
+ });
+ repo.onDisplayDeviceEvent(mDisplayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
+ mLogicalDisplay.updateLocked(repo);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index b2f7abb..0a6cd51 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -623,17 +623,19 @@
}
@Test
- public void testShortcutDeleted() {
+ public void testShortcutsDeleted() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut1 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc1",
buildPerson());
ShortcutInfo shortcut2 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc2",
buildPerson());
+ ShortcutInfo shortcut3 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc3",
+ buildPerson());
mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME,
- Arrays.asList(shortcut1, shortcut2), UserHandle.of(USER_ID_PRIMARY));
+ Arrays.asList(shortcut1, shortcut2, shortcut3), UserHandle.of(USER_ID_PRIMARY));
mShortcutChangeCallback.onShortcutsRemoved(TEST_PKG_NAME,
- Collections.singletonList(shortcut1), UserHandle.of(USER_ID_PRIMARY));
+ List.of(shortcut1, shortcut3), UserHandle.of(USER_ID_PRIMARY));
List<ConversationInfo> conversations = getConversationsInPrimary();
@@ -641,7 +643,7 @@
assertEquals("sc2", conversations.get(0).getShortcutId());
verify(mNotificationManagerInternal)
- .onConversationRemoved(TEST_PKG_NAME, TEST_PKG_UID, "sc1");
+ .onConversationRemoved(TEST_PKG_NAME, TEST_PKG_UID, Set.of("sc1", "sc3"));
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index a2d987fb..f6d6624 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -44,15 +44,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.internal.matchers.Not;
import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
@@ -309,22 +307,22 @@
public void testRemoveConversationRunnable() throws Exception {
NotificationHistory nh = mock(NotificationHistory.class);
NotificationHistoryDatabase.RemoveConversationRunnable rcr =
- mDataBase.new RemoveConversationRunnable("pkg", "convo");
+ mDataBase.new RemoveConversationRunnable("pkg", Set.of("convo", "another"));
rcr.setNotificationHistory(nh);
AtomicFile af = mock(AtomicFile.class);
when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
mDataBase.mHistoryFiles.addLast(af);
- when(nh.removeConversationFromWrite("pkg", "convo")).thenReturn(true);
+ when(nh.removeConversationsFromWrite("pkg", Set.of("convo", "another"))).thenReturn(true);
mDataBase.mBuffer = mock(NotificationHistory.class);
rcr.run();
- verify(mDataBase.mBuffer).removeConversationFromWrite("pkg", "convo");
+ verify(mDataBase.mBuffer).removeConversationsFromWrite("pkg",Set.of("convo", "another"));
verify(af).openRead();
- verify(nh).removeConversationFromWrite("pkg", "convo");
+ verify(nh).removeConversationsFromWrite("pkg",Set.of("convo", "another"));
verify(af).startWrite();
}
@@ -332,22 +330,22 @@
public void testRemoveConversationRunnable_noChanges() throws Exception {
NotificationHistory nh = mock(NotificationHistory.class);
NotificationHistoryDatabase.RemoveConversationRunnable rcr =
- mDataBase.new RemoveConversationRunnable("pkg", "convo");
+ mDataBase.new RemoveConversationRunnable("pkg", Set.of("convo"));
rcr.setNotificationHistory(nh);
AtomicFile af = mock(AtomicFile.class);
when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
mDataBase.mHistoryFiles.addLast(af);
- when(nh.removeConversationFromWrite("pkg", "convo")).thenReturn(false);
+ when(nh.removeConversationsFromWrite("pkg", Set.of("convo"))).thenReturn(false);
mDataBase.mBuffer = mock(NotificationHistory.class);
rcr.run();
- verify(mDataBase.mBuffer).removeConversationFromWrite("pkg", "convo");
+ verify(mDataBase.mBuffer).removeConversationsFromWrite("pkg", Set.of("convo"));
verify(af).openRead();
- verify(nh).removeConversationFromWrite("pkg", "convo");
+ verify(nh).removeConversationsFromWrite("pkg", Set.of("convo"));
verify(af, never()).startWrite();
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
index 2341c10..a0293b7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
@@ -47,6 +47,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
@@ -365,15 +366,15 @@
@Test
public void testDeleteConversation_userUnlocked() {
String pkg = "pkg";
- String convo = "convo";
+ Set<String> convos = Set.of("convo", "another");
NotificationHistoryDatabase userHistory = mock(NotificationHistoryDatabase.class);
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistory);
- mHistoryManager.deleteConversation(pkg, 1, convo);
+ mHistoryManager.deleteConversations(pkg, 1, convos);
- verify(userHistory, times(1)).deleteConversation(pkg, convo);
+ verify(userHistory, times(1)).deleteConversations(pkg, convos);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 0be1bf3..3e779a9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -127,6 +127,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
@SmallTest
@@ -3502,8 +3503,9 @@
}
@Test
- public void testDeleteConversation() {
+ public void testDeleteConversations() {
String convoId = "convo";
+ String convoIdC = "convoC";
NotificationChannel messages =
new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
@@ -3526,10 +3528,16 @@
channel2.setConversationId(calls.getId(), convoId);
mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+ NotificationChannel channel3 =
+ new NotificationChannel("C person msgs", "msgs from C", IMPORTANCE_DEFAULT);
+ channel3.setConversationId(messages.getId(), convoIdC);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel3, true, false);
+
assertEquals(channel, mHelper.getNotificationChannel(PKG_O, UID_O, channel.getId(), false));
assertEquals(channel2,
mHelper.getNotificationChannel(PKG_O, UID_O, channel2.getId(), false));
- assertEquals(2, mHelper.deleteConversation(PKG_O, UID_O, convoId).size());
+ List<String> deleted = mHelper.deleteConversations(PKG_O, UID_O, Set.of(convoId, convoIdC));
+ assertEquals(3, deleted.size());
assertEquals(messages,
mHelper.getNotificationChannel(PKG_O, UID_O, messages.getId(), false));
@@ -3542,7 +3550,7 @@
assertEquals(channel2,
mHelper.getNotificationChannel(PKG_O, UID_O, channel2.getId(), true));
- assertEquals(7, mLogger.getCalls().size());
+ assertEquals(9, mLogger.getCalls().size());
assertEquals(
NotificationChannelLogger.NotificationChannelEvent.NOTIFICATION_CHANNEL_CREATED,
mLogger.get(0).event); // Channel messages
@@ -3563,12 +3571,20 @@
mLogger.get(4).event); // Channel channel2 - Conversation A person calls
assertEquals(
NotificationChannelLogger.NotificationChannelEvent
- .NOTIFICATION_CHANNEL_CONVERSATION_DELETED,
- mLogger.get(5).event); // Delete Channel channel - Conversation A person msgs
+ .NOTIFICATION_CHANNEL_CONVERSATION_CREATED,
+ mLogger.get(5).event); // Channel channel3 - Conversation C person msgs
assertEquals(
NotificationChannelLogger.NotificationChannelEvent
.NOTIFICATION_CHANNEL_CONVERSATION_DELETED,
- mLogger.get(6).event); // Delete Channel channel2 - Conversation A person calls
+ mLogger.get(6).event); // Delete Channel channel - Conversation A person msgs
+ assertEquals(
+ NotificationChannelLogger.NotificationChannelEvent
+ .NOTIFICATION_CHANNEL_CONVERSATION_DELETED,
+ mLogger.get(7).event); // Delete Channel channel2 - Conversation A person calls
+ assertEquals(
+ NotificationChannelLogger.NotificationChannelEvent
+ .NOTIFICATION_CHANNEL_CONVERSATION_DELETED,
+ mLogger.get(8).event); // Delete Channel channel3 - Conversation C person msgs
}
@Test
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index f7fe1ba..1472a4a 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,7 +16,6 @@
package android.telecom;
-import android.annotation.SystemApi;
import android.media.ToneGenerator;
import android.os.Parcel;
import android.os.Parcelable;
@@ -97,10 +96,7 @@
*
* This reason code is only used for communication between a {@link ConnectionService} and
* Telecom and should not be surfaced to the user.
- *
- * @hide
*/
- @SystemApi
public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
/**
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index c20e5ad..4a3561b 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -28,6 +28,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import java.util.ArrayList;
@@ -52,8 +53,17 @@
*
* This is an extras key set via {@link Builder#setExtras} which determines the order in which
* {@link PhoneAccount}s from the same {@link ConnectionService} are sorted. The accounts
- * are sorted by this key via standard lexicographical order, and this ordering is used to
+ * are sorted by this key via standard lexicographical order, (as implemented in
+ * {@link String#compareTo}), and this ordering is used to
* determine priority when a call can be placed via multiple accounts.
+ *
+ * When multiple {@link PhoneAccount}s are supplied with the same sort order key, no ordering is
+ * guaranteed between those {@link PhoneAccount}s. Additionally, no ordering is guaranteed
+ * between {@link PhoneAccount}s that do not supply this extra, and all such accounts
+ * will be sorted after the accounts that do supply this extra.
+ *
+ * An example of a sort order key is slot index (see {@link TelephonyManager#getSlotIndex()}),
+ * which is the one used by the cell Telephony stack.
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 47bc566..5b3da61 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3932,6 +3932,16 @@
public static final String KEY_DEFAULT_PREFERRED_APN_NAME_STRING =
"default_preferred_apn_name_string";
+ /**
+ * For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values
+ * for IPv4 and IPv6 if both are sent.
+ * TODO: remove in later release
+ *
+ * @hide
+ */
+ public static final String KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED =
+ "use_lower_mtu_value_if_both_received";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -4473,6 +4483,7 @@
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
+ sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
}
/**
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
new file mode 100644
index 0000000..647da2a
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -0,0 +1,58 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "SurfaceViewBufferTests",
+ srcs: ["**/*.java","**/*.kt"],
+ manifest: "AndroidManifest.xml",
+ test_config: "AndroidTest.xml",
+ platform_apis: true,
+ certificate: "platform",
+ use_embedded_native_libs: true,
+ jni_libs: [
+ "libsurface_jni",
+ ],
+
+ static_libs: [
+ "androidx.appcompat_appcompat",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+ "kotlin-stdlib",
+ "kotlinx-coroutines-android",
+ "flickerlib",
+ "truth-prebuilt",
+ ],
+}
+
+cc_library_shared {
+ name: "libsurface_jni",
+ srcs: [
+ "cpp/SurfaceProxy.cpp",
+ ],
+ shared_libs: [
+ "libutils",
+ "libgui",
+ "liblog",
+ "libandroid",
+ ],
+ include_dirs: [
+ "system/core/include"
+ ],
+ stl: "libc++_static",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
diff --git a/tests/SurfaceViewBufferTests/AndroidManifest.xml b/tests/SurfaceViewBufferTests/AndroidManifest.xml
new file mode 100644
index 0000000..95885c1
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test">
+
+ <uses-sdk android:minSdkVersion="29"
+ android:targetSdkVersion="29"/>
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
+ <!-- Enable / Disable sv blast adapter !-->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+ <application android:allowBackup="false"
+ android:supportsRtl="true">
+ <activity android:name=".MainActivity"
+ android:taskAffinity="com.android.test.MainActivity"
+ android:theme="@style/AppTheme"
+ android:label="SurfaceViewBufferTestApp"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test"
+ android:label="SurfaceViewBufferTests">
+ </instrumentation>
+</manifest>
diff --git a/tests/SurfaceViewBufferTests/AndroidTest.xml b/tests/SurfaceViewBufferTests/AndroidTest.xml
new file mode 100644
index 0000000..b73fe48
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs SurfaceView Buffer Tests">
+ <option name="test-tag" value="SurfaceViewBufferTests" />
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on" />
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="false"/>
+ <option name="test-file-name" value="SurfaceViewBufferTests.apk"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.test"/>
+ <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+ <option name="shell-timeout" value="6600s" />
+ <option name="test-timeout" value="6000s" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
new file mode 100644
index 0000000..0c86524
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+#include <android/window.h>
+#include <gui/Surface.h>
+#include <jni.h>
+#include <system/window.h>
+#include <utils/RefBase.h>
+#include <cassert>
+#include <chrono>
+#include <thread>
+
+#define TAG "SurfaceViewBufferTests"
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
+
+extern "C" {
+int i = 0;
+static ANativeWindow* sAnw;
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_setSurface(JNIEnv* env, jclass,
+ jobject surfaceObject) {
+ sAnw = ANativeWindow_fromSurface(env, surfaceObject);
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ surface->enableFrameTimestamps(true);
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_waitUntilBufferDisplayed(
+ JNIEnv*, jclass, jint jFrameNumber, jint timeoutSec) {
+ using namespace std::chrono_literals;
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+
+ uint64_t frameNumber = static_cast<uint64_t>(jFrameNumber);
+ nsecs_t outRequestedPresentTime, outAcquireTime, outLatchTime, outFirstRefreshStartTime;
+ nsecs_t outLastRefreshStartTime, outGlCompositionDoneTime, outDequeueReadyTime;
+ nsecs_t outDisplayPresentTime = -1;
+ nsecs_t outReleaseTime;
+
+ auto start = std::chrono::steady_clock::now();
+ while (outDisplayPresentTime < 0) {
+ std::this_thread::sleep_for(8ms);
+ surface->getFrameTimestamps(frameNumber, &outRequestedPresentTime, &outAcquireTime,
+ &outLatchTime, &outFirstRefreshStartTime,
+ &outLastRefreshStartTime, &outGlCompositionDoneTime,
+ &outDisplayPresentTime, &outDequeueReadyTime, &outReleaseTime);
+ if (outDisplayPresentTime < 0) {
+ auto end = std::chrono::steady_clock::now();
+ if (std::chrono::duration_cast<std::chrono::seconds>(end - start).count() >
+ timeoutSec) {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_draw(JNIEnv*, jclass) {
+ assert(sAnw);
+ ANativeWindow_Buffer outBuffer;
+ ANativeWindow_lock(sAnw, &outBuffer, nullptr);
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowLock(JNIEnv*, jclass) {
+ assert(sAnw);
+ ANativeWindow_Buffer outBuffer;
+ ANativeWindow_lock(sAnw, &outBuffer, nullptr);
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowUnlockAndPost(JNIEnv*,
+ jclass) {
+ assert(sAnw);
+ ANativeWindow_unlockAndPost(sAnw);
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowSetBuffersGeometry(
+ JNIEnv* /* env */, jclass /* clazz */, jobject /* surfaceObject */, jint w, jint h,
+ jint format) {
+ assert(sAnw);
+ return ANativeWindow_setBuffersGeometry(sAnw, w, h, format);
+}
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/res/values/styles.xml b/tests/SurfaceViewBufferTests/res/values/styles.xml
new file mode 100644
index 0000000..8b50738
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/res/values/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <item name="windowNoTitle">true</item>
+ <item name="windowActionBar">false</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowDisablePreview">true</item>
+</style>
+</resources>
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
new file mode 100644
index 0000000..b1e1336
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.test
+
+import android.app.Activity
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.Gravity
+import android.view.Surface
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import android.widget.FrameLayout
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+
+class MainActivity : Activity() {
+ val mSurfaceProxy = SurfaceProxy()
+ private var mSurfaceHolder: SurfaceHolder? = null
+ private val mDrawLock = ReentrantLock()
+
+ val surface: Surface? get() = mSurfaceHolder?.surface
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ addSurfaceView(Rect(0, 0, 500, 200))
+ }
+
+ fun addSurfaceView(size: Rect): CountDownLatch {
+ val layout = findViewById<FrameLayout>(android.R.id.content)
+ val surfaceReadyLatch = CountDownLatch(1)
+ val surfaceView = createSurfaceView(applicationContext, size, surfaceReadyLatch)
+ layout.addView(surfaceView,
+ FrameLayout.LayoutParams(size.width(), size.height(), Gravity.TOP or Gravity.LEFT)
+ .also { it.setMargins(100, 100, 0, 0) })
+ return surfaceReadyLatch
+ }
+
+ private fun createSurfaceView(
+ context: Context,
+ size: Rect,
+ surfaceReadyLatch: CountDownLatch
+ ): SurfaceView {
+ val surfaceView = SurfaceView(context)
+ surfaceView.setWillNotDraw(false)
+ surfaceView.holder.setFixedSize(size.width(), size.height())
+ surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
+ override fun surfaceCreated(holder: SurfaceHolder) {
+ mDrawLock.withLock {
+ mSurfaceHolder = holder
+ mSurfaceProxy.setSurface(holder.surface)
+ }
+ surfaceReadyLatch.countDown()
+ }
+
+ override fun surfaceChanged(
+ holder: SurfaceHolder,
+ format: Int,
+ width: Int,
+ height: Int
+ ) {
+ }
+
+ override fun surfaceDestroyed(holder: SurfaceHolder) {
+ mDrawLock.withLock {
+ mSurfaceHolder = null
+ }
+ }
+ })
+ return surfaceView
+ }
+
+ fun drawFrame(): Rect {
+ mDrawLock.withLock {
+ val holder = mSurfaceHolder ?: return Rect()
+ val canvas = holder.lockCanvas()
+ val canvasSize = Rect(0, 0, canvas.width, canvas.height)
+ canvas.drawColor(Color.GREEN)
+ val p = Paint()
+ p.color = Color.RED
+ canvas.drawRect(canvasSize, p)
+ holder.unlockCanvasAndPost(canvas)
+ return canvasSize
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
new file mode 100644
index 0000000..884aae41
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test
+
+class SurfaceProxy {
+ init {
+ System.loadLibrary("surface_jni")
+ }
+
+ external fun setSurface(surface: Any)
+ external fun waitUntilBufferDisplayed(frameNumber: Int, timeoutSec: Int)
+ external fun draw()
+
+ // android/native_window.h functions
+ external fun ANativeWindowLock()
+ external fun ANativeWindowUnlockAndPost()
+ external fun ANativeWindowSetBuffersGeometry(surface: Any, width: Int, height: Int, format: Int)
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt
new file mode 100644
index 0000000..b48a91d
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt
@@ -0,0 +1,185 @@
+/*
+ * 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.test
+
+import android.app.Instrumentation
+import android.graphics.Rect
+import android.provider.Settings
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor
+import com.android.server.wm.flicker.monitor.withSFTracing
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.concurrent.CountDownLatch
+import kotlin.properties.Delegates
+
+@RunWith(Parameterized::class)
+class SurfaceViewBufferTest(val useBlastAdapter: Boolean) {
+ private var mInitialUseBlastConfig by Delegates.notNull<Int>()
+
+ @get:Rule
+ var scenarioRule: ActivityScenarioRule<MainActivity> =
+ ActivityScenarioRule<MainActivity>(MainActivity::class.java)
+
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val defaultBufferSize = Rect(0, 0, 640, 480)
+
+ @Before
+ fun setup() {
+ mInitialUseBlastConfig = Settings.Global.getInt(instrumentation.context.contentResolver,
+ "use_blast_adapter_sv", 0)
+ val enable = if (useBlastAdapter) 1 else 0
+ Settings.Global.putInt(instrumentation.context.contentResolver, "use_blast_adapter_sv",
+ enable)
+ val tmpDir = instrumentation.targetContext.dataDir.toPath()
+ LayersTraceMonitor(tmpDir).stop()
+
+ lateinit var surfaceReadyLatch: CountDownLatch
+ scenarioRule.getScenario().onActivity {
+ surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
+ }
+ surfaceReadyLatch.await()
+ }
+
+ @After
+ fun teardown() {
+ scenarioRule.getScenario().close()
+ Settings.Global.putInt(instrumentation.context.contentResolver,
+ "use_blast_adapter_sv", mInitialUseBlastConfig)
+ }
+
+ @Test
+ fun testSetBuffersGeometry_0x0_resetsBufferSize() {
+ val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0,
+ R8G8B8A8_UNORM)
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
+ }
+ }
+
+ // verify buffer size is reset to default buffer size
+ assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ }
+
+ @Test
+ fun testSetBuffersGeometry_0x0_rejectsBuffer() {
+ val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
+ R8G8B8A8_UNORM)
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, R8G8B8A8_UNORM)
+ // Submit buffer one with a different size which should be rejected
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+
+ // submit a buffer with the default buffer size
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(3, 1 /* sec */)
+ }
+ }
+ // Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE
+ assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+
+ // Verify the next buffer is submitted with the correct size
+ assertThat(trace).layer("SurfaceView", 3).also {
+ it.hasBufferSize(defaultBufferSize)
+ it.hasScalingMode(0 /* NATIVE_WINDOW_SCALING_MODE_FREEZE */)
+ }
+ }
+
+ @Test
+ fun testSetBuffersGeometry_smallerThanBuffer() {
+ val bufferSize = Rect(0, 0, 300, 200)
+ val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(),
+ bufferSize.height(), R8G8B8A8_UNORM)
+ it.drawFrame()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
+ }
+ }
+
+ assertThat(trace).layer("SurfaceView", 1).also {
+ it.hasBufferSize(bufferSize)
+ it.hasLayerSize(defaultBufferSize)
+ it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */)
+ }
+ }
+
+ @Test
+ fun testSetBuffersGeometry_largerThanBuffer() {
+ val bufferSize = Rect(0, 0, 3000, 2000)
+ val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(),
+ bufferSize.height(), R8G8B8A8_UNORM)
+ it.drawFrame()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
+ }
+ }
+
+ assertThat(trace).layer("SurfaceView", 1).also {
+ it.hasBufferSize(bufferSize)
+ it.hasLayerSize(defaultBufferSize)
+ it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */)
+ }
+ }
+
+ /** Submit buffers as fast as possible and make sure they are queued */
+ @Test
+ fun testQueueBuffers() {
+ val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
+ R8G8B8A8_UNORM)
+ for (i in 0..100) {
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ }
+ it.mSurfaceProxy.waitUntilBufferDisplayed(100, 1 /* sec */)
+ }
+ }
+ for (frameNumber in 1..100) {
+ assertThat(trace).layer("SurfaceView", frameNumber.toLong())
+ }
+ }
+
+ companion object {
+ private const val TRACE_FLAGS = 0x1 // TRACE_CRITICAL
+ private const val R8G8B8A8_UNORM = 1
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "blast={0}")
+ fun data(): Collection<Array<Any>> {
+ return listOf(
+ arrayOf(false), // First test: submit buffers via bufferqueue
+ arrayOf(true) // Second test: submit buffers via blast adapter
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 124b660..0fe84ab 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -63,6 +63,7 @@
"services.net",
],
libs: [
+ "android.net.ipsec.ike.stubs.module_lib",
"android.test.runner",
"android.test.base",
"android.test.mock",
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index c76b4cd..c3f1549 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -20,6 +20,7 @@
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static android.net.ConnectivityManager.NetworkCallback;
import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
@@ -45,7 +46,9 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -66,6 +69,7 @@
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
+import android.net.IpSecTunnelInterfaceResponse;
import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.Network;
@@ -75,6 +79,8 @@
import android.net.UidRange;
import android.net.VpnManager;
import android.net.VpnService;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.ConditionVariable;
@@ -101,6 +107,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -150,6 +157,11 @@
private static final String TEST_VPN_IDENTITY = "identity";
private static final byte[] TEST_VPN_PSK = "psk".getBytes();
+ private static final Network TEST_NETWORK = new Network(Integer.MAX_VALUE);
+ private static final String TEST_IFACE_NAME = "TEST_IFACE";
+ private static final int TEST_TUNNEL_RESOURCE_ID = 0x2345;
+ private static final long TEST_TIMEOUT_MS = 500L;
+
/**
* Names and UIDs for some fake packages. Important points:
* - UID is ordered increasing.
@@ -227,6 +239,13 @@
// Deny all appops by default.
when(mAppOps.noteOpNoThrow(anyInt(), anyInt(), anyString()))
.thenReturn(AppOpsManager.MODE_IGNORED);
+
+ // Setup IpSecService
+ final IpSecTunnelInterfaceResponse tunnelResp =
+ new IpSecTunnelInterfaceResponse(
+ IpSecManager.Status.OK, TEST_TUNNEL_RESOURCE_ID, TEST_IFACE_NAME);
+ when(mIpSecService.createTunnelInterface(any(), any(), any(), any(), any()))
+ .thenReturn(tunnelResp);
}
@Test
@@ -988,6 +1007,52 @@
eq(AppOpsManager.MODE_IGNORED));
}
+ private NetworkCallback triggerOnAvailableAndGetCallback() {
+ final ArgumentCaptor<NetworkCallback> networkCallbackCaptor =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
+ .requestNetwork(any(), networkCallbackCaptor.capture());
+
+ final NetworkCallback cb = networkCallbackCaptor.getValue();
+ cb.onAvailable(TEST_NETWORK);
+ return cb;
+ }
+
+ @Test
+ public void testStartPlatformVpnAuthenticationFailed() throws Exception {
+ final ArgumentCaptor<IkeSessionCallback> captor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ final IkeProtocolException exception = mock(IkeProtocolException.class);
+ when(exception.getErrorType())
+ .thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED);
+
+ final Vpn vpn = startLegacyVpn(mVpnProfile);
+ final NetworkCallback cb = triggerOnAvailableAndGetCallback();
+
+ // Wait for createIkeSession() to be called before proceeding in order to ensure consistent
+ // state
+ verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS))
+ .createIkeSession(any(), any(), any(), any(), captor.capture(), any());
+ final IkeSessionCallback ikeCb = captor.getValue();
+ ikeCb.onClosedExceptionally(exception);
+
+ verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
+ assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState());
+ }
+
+ @Test
+ public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception {
+ when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any()))
+ .thenThrow(new IllegalArgumentException());
+ final Vpn vpn = startLegacyVpn(mVpnProfile);
+ final NetworkCallback cb = triggerOnAvailableAndGetCallback();
+
+ // Wait for createIkeSession() to be called before proceeding in order to ensure consistent
+ // state
+ verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
+ assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState());
+ }
+
private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null, mKeyStore));
diff --git a/tools/xmlpersistence/Android.bp b/tools/xmlpersistence/Android.bp
new file mode 100644
index 0000000..d58d0dc
--- /dev/null
+++ b/tools/xmlpersistence/Android.bp
@@ -0,0 +1,11 @@
+java_binary_host {
+ name: "xmlpersistence_cli",
+ manifest: "manifest.txt",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "javaparser-symbol-solver",
+ "javapoet",
+ ],
+}
diff --git a/tools/xmlpersistence/OWNERS b/tools/xmlpersistence/OWNERS
new file mode 100644
index 0000000..4f4d06a
--- /dev/null
+++ b/tools/xmlpersistence/OWNERS
@@ -0,0 +1 @@
+zhanghai@google.com
diff --git a/tools/xmlpersistence/manifest.txt b/tools/xmlpersistence/manifest.txt
new file mode 100644
index 0000000..6d97719
--- /dev/null
+++ b/tools/xmlpersistence/manifest.txt
@@ -0,0 +1 @@
+Main-class: MainKt
diff --git a/tools/xmlpersistence/src/main/kotlin/Generator.kt b/tools/xmlpersistence/src/main/kotlin/Generator.kt
new file mode 100644
index 0000000..28467b7
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Generator.kt
@@ -0,0 +1,578 @@
+/*
+ * 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.
+ */
+
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.NameAllocator
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeSpec
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.nio.charset.StandardCharsets
+import java.time.Year
+import java.util.Objects
+import javax.lang.model.element.Modifier
+
+// JavaPoet only supports line comments, and can't add a newline after file level comments.
+val FILE_HEADER = """
+ /*
+ * Copyright (C) ${Year.now().value} The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ // Generated by xmlpersistence. DO NOT MODIFY!
+ // CHECKSTYLE:OFF
+ // @formatter:off
+""".trimIndent() + "\n\n"
+
+private val atomicFileType = ClassName.get("android.util", "AtomicFile")
+
+fun generate(persistence: PersistenceInfo): JavaFile {
+ val distinctClassFields = persistence.root.allClassFields.distinctBy { it.type }
+ val type = TypeSpec.classBuilder(persistence.name)
+ .addJavadoc(
+ """
+ Generated class implementing XML persistence for${'$'}W{@link $1T}.
+ <p>
+ This class provides atomicity for persistence via {@link $2T}, however it does not provide
+ thread safety, so please bring your own synchronization mechanism.
+ """.trimIndent(), persistence.root.type, atomicFileType
+ )
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addField(generateFileField())
+ .addMethod(generateConstructor())
+ .addMethod(generateReadMethod(persistence.root))
+ .addMethod(generateParseMethod(persistence.root))
+ .addMethods(distinctClassFields.map { generateParseClassMethod(it) })
+ .addMethod(generateWriteMethod(persistence.root))
+ .addMethod(generateSerializeMethod(persistence.root))
+ .addMethods(distinctClassFields.map { generateSerializeClassMethod(it) })
+ .addMethod(generateDeleteMethod())
+ .build()
+ return JavaFile.builder(persistence.root.type.packageName(), type)
+ .skipJavaLangImports(true)
+ .indent(" ")
+ .build()
+}
+
+private val nonNullType = ClassName.get("android.annotation", "NonNull")
+
+private fun generateFileField(): FieldSpec =
+ FieldSpec.builder(atomicFileType, "mFile", Modifier.PRIVATE, Modifier.FINAL)
+ .addAnnotation(nonNullType)
+ .build()
+
+private fun generateConstructor(): MethodSpec =
+ MethodSpec.constructorBuilder()
+ .addJavadoc(
+ """
+ Create an instance of this class.
+
+ @param file the XML file for persistence
+ """.trimIndent()
+ )
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(
+ ParameterSpec.builder(File::class.java, "file").addAnnotation(nonNullType).build()
+ )
+ .addStatement("mFile = new \$1T(file)", atomicFileType)
+ .build()
+
+private val nullableType = ClassName.get("android.annotation", "Nullable")
+
+private val xmlPullParserType = ClassName.get("org.xmlpull.v1", "XmlPullParser")
+
+private val xmlType = ClassName.get("android.util", "Xml")
+
+private val xmlPullParserExceptionType = ClassName.get("org.xmlpull.v1", "XmlPullParserException")
+
+private fun generateReadMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("read")
+ .addJavadoc(
+ """
+ Read${'$'}W{@link $1T}${'$'}Wfrom${'$'}Wthe${'$'}WXML${'$'}Wfile.
+
+ @return the persisted${'$'}W{@link $1T},${'$'}Wor${'$'}W{@code null}${'$'}Wif${'$'}Wthe${'$'}WXML${'$'}Wfile${'$'}Wdoesn't${'$'}Wexist
+ @throws IllegalArgumentException if an error occurred while reading
+ """.trimIndent(), rootField.type
+ )
+ .addAnnotation(nullableType)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(rootField.type)
+ .addControlFlow(
+ "try (final \$1T inputStream = mFile.openRead())", FileInputStream::class.java
+ ) {
+ addStatement("final \$1T parser = \$2T.newPullParser()", xmlPullParserType, xmlType)
+ addStatement("parser.setInput(inputStream, null)")
+ addStatement("return parse(parser)")
+ nextControlFlow("catch (\$1T e)", FileNotFoundException::class.java)
+ addStatement("return null")
+ nextControlFlow(
+ "catch (\$1T | \$2T e)", IOException::class.java, xmlPullParserExceptionType
+ )
+ addStatement("throw new IllegalArgumentException(e)")
+ }
+ .build()
+
+private val ClassFieldInfo.allClassFields: List<ClassFieldInfo>
+ get() =
+ mutableListOf<ClassFieldInfo>().apply {
+ this += this@allClassFields
+ for (field in fields) {
+ when (field) {
+ is ClassFieldInfo -> this += field.allClassFields
+ is ListFieldInfo -> this += field.element.allClassFields
+ }
+ }
+ }
+
+private fun generateParseMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("parse")
+ .addAnnotation(nonNullType)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .returns(rootField.type)
+ .addParameter(
+ ParameterSpec.builder(xmlPullParserType, "parser").addAnnotation(nonNullType).build()
+ )
+ .addExceptions(listOf(ClassName.get(IOException::class.java), xmlPullParserExceptionType))
+ .apply {
+ addStatement("int type")
+ addStatement("int depth")
+ addStatement("int innerDepth = parser.getDepth() + 1")
+ addControlFlow(
+ "while ((type = parser.next()) != \$1T.END_DOCUMENT\$W"
+ + "&& ((depth = parser.getDepth()) >= innerDepth || type != \$1T.END_TAG))",
+ xmlPullParserType
+ ) {
+ addControlFlow(
+ "if (depth > innerDepth || type != \$1T.START_TAG)", xmlPullParserType
+ ) {
+ addStatement("continue")
+ }
+ addControlFlow(
+ "if (\$1T.equals(parser.getName(),\$W\$2S))", Objects::class.java,
+ rootField.tagName
+ ) {
+ addStatement("return \$1L(parser)", rootField.parseMethodName)
+ }
+ }
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Missing root tag <${rootField.tagName}>"
+ )
+ }
+ .build()
+
+private fun generateParseClassMethod(classField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder(classField.parseMethodName)
+ .addAnnotation(nonNullType)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .returns(classField.type)
+ .addParameter(
+ ParameterSpec.builder(xmlPullParserType, "parser").addAnnotation(nonNullType).build()
+ )
+ .apply {
+ val (attributeFields, tagFields) = classField.fields
+ .partition { it is PrimitiveFieldInfo || it is StringFieldInfo }
+ if (tagFields.isNotEmpty()) {
+ addExceptions(
+ listOf(ClassName.get(IOException::class.java), xmlPullParserExceptionType)
+ )
+ }
+ val nameAllocator = NameAllocator().apply {
+ newName("parser")
+ newName("type")
+ newName("depth")
+ newName("innerDepth")
+ }
+ for (field in attributeFields) {
+ val variableName = nameAllocator.newName(field.variableName, field)
+ when (field) {
+ is PrimitiveFieldInfo -> {
+ val stringVariableName =
+ nameAllocator.newName("${field.variableName}String")
+ addStatement(
+ "final String \$1L =\$Wparser.getAttributeValue(null,\$W\$2S)",
+ stringVariableName, field.attributeName
+ )
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", stringVariableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Missing attribute \"${field.attributeName}\""
+ )
+ }
+ }
+ val boxedType = field.type.box()
+ val parseTypeMethodName = if (field.type.isPrimitive) {
+ "parse${field.type.toString().capitalize()}"
+ } else {
+ "valueOf"
+ }
+ if (field.isRequired) {
+ addStatement(
+ "final \$1T \$2L =\$W\$3T.\$4L($5L)", field.type, variableName,
+ boxedType, parseTypeMethodName, stringVariableName
+ )
+ } else {
+ addStatement(
+ "final \$1T \$2L =\$W$3L != null ?\$W\$4T.\$5L($3L)\$W: null",
+ field.type, variableName, stringVariableName, boxedType,
+ parseTypeMethodName
+ )
+ }
+ }
+ is StringFieldInfo ->
+ addStatement(
+ "final String \$1L =\$Wparser.getAttributeValue(null,\$W\$2S)",
+ variableName, field.attributeName
+ )
+ else -> error(field)
+ }
+ }
+ if (tagFields.isNotEmpty()) {
+ for (field in tagFields) {
+ val variableName = nameAllocator.newName(field.variableName, field)
+ when (field) {
+ is ClassFieldInfo ->
+ addStatement("\$1T \$2L =\$Wnull", field.type, variableName)
+ is ListFieldInfo ->
+ addStatement(
+ "final \$1T \$2L =\$Wnew \$3T<>()", field.type, variableName,
+ ArrayList::class.java
+ )
+ else -> error(field)
+ }
+ }
+ addStatement("int type")
+ addStatement("int depth")
+ addStatement("int innerDepth = parser.getDepth() + 1")
+ addControlFlow(
+ "while ((type = parser.next()) != \$1T.END_DOCUMENT\$W"
+ + "&& ((depth = parser.getDepth()) >= innerDepth || type != \$1T.END_TAG))",
+ xmlPullParserType
+ ) {
+ addControlFlow(
+ "if (depth > innerDepth || type != \$1T.START_TAG)", xmlPullParserType
+ ) {
+ addStatement("continue")
+ }
+ addControlFlow("switch (parser.getName())") {
+ for (field in tagFields) {
+ addControlFlow("case \$1S:", field.tagName) {
+ val variableName = nameAllocator.get(field)
+ when (field) {
+ is ClassFieldInfo -> {
+ addControlFlow("if (\$1L != null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Duplicate tag \"${field.tagName}\""
+ )
+ }
+ addStatement(
+ "\$1L =\$W\$2L(parser)", variableName,
+ field.parseMethodName
+ )
+ addStatement("break")
+ }
+ is ListFieldInfo -> {
+ val elementNameAllocator = nameAllocator.clone()
+ val elementVariableName = elementNameAllocator.newName(
+ field.element.xmlName!!.toLowerCamelCase()
+ )
+ addStatement(
+ "final \$1T \$2L =\$W\$3L(parser)", field.element.type,
+ elementVariableName, field.element.parseMethodName
+ )
+ addStatement(
+ "\$1L.add(\$2L)", variableName, elementVariableName
+ )
+ addStatement("break")
+ }
+ else -> error(field)
+ }
+ }
+ }
+ }
+ }
+ }
+ for (field in tagFields.filter { it is ClassFieldInfo && it.isRequired }) {
+ addControlFlow("if ($1L == null)", nameAllocator.get(field)) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)", "Missing tag <${field.tagName}>"
+ )
+ }
+ }
+ addStatement(
+ classField.fields.joinToString(",\$W", "return new \$1T(", ")") {
+ nameAllocator.get(it)
+ }, classField.type
+ )
+ }
+ .build()
+
+private val ClassFieldInfo.parseMethodName: String
+ get() = "parse${type.simpleName().toUpperCamelCase()}"
+
+private val xmlSerializerType = ClassName.get("org.xmlpull.v1", "XmlSerializer")
+
+private fun generateWriteMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("write")
+ .apply {
+ val nameAllocator = NameAllocator().apply {
+ newName("outputStream")
+ newName("serializer")
+ }
+ val parameterName = nameAllocator.newName(rootField.variableName)
+ addJavadoc(
+ """
+ Write${'$'}W{@link $1T}${'$'}Wto${'$'}Wthe${'$'}WXML${'$'}Wfile.
+
+ @param $2L the${'$'}W{@link ${'$'}1T}${'$'}Wto${'$'}Wpersist
+ """.trimIndent(), rootField.type, parameterName
+ )
+ addAnnotation(nullableType)
+ addModifiers(Modifier.PUBLIC)
+ addParameter(
+ ParameterSpec.builder(rootField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addStatement("\$1T outputStream = null", FileOutputStream::class.java)
+ addControlFlow("try") {
+ addStatement("outputStream = mFile.startWrite()")
+ addStatement(
+ "final \$1T serializer =\$W\$2T.newSerializer()", xmlSerializerType, xmlType
+ )
+ addStatement(
+ "serializer.setOutput(outputStream, \$1T.UTF_8.name())",
+ StandardCharsets::class.java
+ )
+ addStatement(
+ "serializer.setFeature(\$1S, true)",
+ "http://xmlpull.org/v1/doc/features.html#indent-output"
+ )
+ addStatement("serializer.startDocument(null, true)")
+ addStatement("serialize(serializer,\$W\$1L)", parameterName)
+ addStatement("serializer.endDocument()")
+ addStatement("mFile.finishWrite(outputStream)")
+ nextControlFlow("catch (Exception e)")
+ addStatement("e.printStackTrace()")
+ addStatement("mFile.failWrite(outputStream)")
+ }
+ }
+ .build()
+
+private fun generateSerializeMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("serialize")
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .addParameter(
+ ParameterSpec.builder(xmlSerializerType, "serializer")
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ .apply {
+ val nameAllocator = NameAllocator().apply { newName("serializer") }
+ val parameterName = nameAllocator.newName(rootField.variableName)
+ addParameter(
+ ParameterSpec.builder(rootField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addException(IOException::class.java)
+ addStatement("serializer.startTag(null, \$1S)", rootField.tagName)
+ addStatement("\$1L(serializer, \$2L)", rootField.serializeMethodName, parameterName)
+ addStatement("serializer.endTag(null, \$1S)", rootField.tagName)
+ }
+ .build()
+
+private fun generateSerializeClassMethod(classField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder(classField.serializeMethodName)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .addParameter(
+ ParameterSpec.builder(xmlSerializerType, "serializer")
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ .apply {
+ val nameAllocator = NameAllocator().apply {
+ newName("serializer")
+ newName("i")
+ }
+ val parameterName = nameAllocator.newName(classField.serializeParameterName)
+ addParameter(
+ ParameterSpec.builder(classField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addException(IOException::class.java)
+ val (attributeFields, tagFields) = classField.fields
+ .partition { it is PrimitiveFieldInfo || it is StringFieldInfo }
+ for (field in attributeFields) {
+ val variableName = "$parameterName.${field.name}"
+ if (!field.isRequired) {
+ beginControlFlow("if (\$1L != null)", variableName)
+ }
+ when (field) {
+ is PrimitiveFieldInfo -> {
+ if (field.isRequired && !field.type.isPrimitive) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ val stringVariableName =
+ nameAllocator.newName("${field.variableName}String")
+ addStatement(
+ "final String \$1L =\$WString.valueOf(\$2L)", stringVariableName,
+ variableName
+ )
+ addStatement(
+ "serializer.attribute(null, \$1S, \$2L)", field.attributeName,
+ stringVariableName
+ )
+ }
+ is StringFieldInfo -> {
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ addStatement(
+ "serializer.attribute(null, \$1S, \$2L)", field.attributeName,
+ variableName
+ )
+ }
+ else -> error(field)
+ }
+ if (!field.isRequired) {
+ endControlFlow()
+ }
+ }
+ for (field in tagFields) {
+ val variableName = "$parameterName.${field.name}"
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ when (field) {
+ is ClassFieldInfo -> {
+ addStatement("serializer.startTag(null, \$1S)", field.tagName)
+ addStatement(
+ "\$1L(serializer, \$2L)", field.serializeMethodName, variableName
+ )
+ addStatement("serializer.endTag(null, \$1S)", field.tagName)
+ }
+ is ListFieldInfo -> {
+ val sizeVariableName = nameAllocator.newName("${field.variableName}Size")
+ addStatement(
+ "final int \$1L =\$W\$2L.size()", sizeVariableName, variableName
+ )
+ addControlFlow("for (int i = 0;\$Wi < \$1L;\$Wi++)", sizeVariableName) {
+ val elementNameAllocator = nameAllocator.clone()
+ val elementVariableName = elementNameAllocator.newName(
+ field.element.xmlName!!.toLowerCamelCase()
+ )
+ addStatement(
+ "final \$1T \$2L =\$W\$3L.get(i)", field.element.type,
+ elementVariableName, variableName
+ )
+ addControlFlow("if (\$1L == null)", elementVariableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S\$W+ i\$W+ \$2S)",
+ "Field element \"${field.name}[", "]\" is null"
+ )
+ }
+ addStatement("serializer.startTag(null, \$1S)", field.element.tagName)
+ addStatement(
+ "\$1L(serializer,\$W\$2L)", field.element.serializeMethodName,
+ elementVariableName
+ )
+ addStatement("serializer.endTag(null, \$1S)", field.element.tagName)
+ }
+ }
+ else -> error(field)
+ }
+ }
+ }
+ .build()
+
+private val ClassFieldInfo.serializeMethodName: String
+ get() = "serialize${type.simpleName().toUpperCamelCase()}"
+
+private val ClassFieldInfo.serializeParameterName: String
+ get() = type.simpleName().toLowerCamelCase()
+
+private val FieldInfo.variableName: String
+ get() = name.toLowerCamelCase()
+
+private val FieldInfo.attributeName: String
+ get() {
+ check(this is PrimitiveFieldInfo || this is StringFieldInfo)
+ return xmlNameOrName.toLowerCamelCase()
+ }
+
+private val FieldInfo.tagName: String
+ get() {
+ check(this is ClassFieldInfo || this is ListFieldInfo)
+ return xmlNameOrName.toLowerKebabCase()
+ }
+
+private val FieldInfo.xmlNameOrName: String
+ get() = xmlName ?: name
+
+private fun generateDeleteMethod(): MethodSpec =
+ MethodSpec.methodBuilder("delete")
+ .addJavadoc("Delete the XML file, if any.")
+ .addModifiers(Modifier.PUBLIC)
+ .addStatement("mFile.delete()")
+ .build()
+
+private inline fun MethodSpec.Builder.addControlFlow(
+ controlFlow: String,
+ vararg args: Any,
+ block: MethodSpec.Builder.() -> Unit
+): MethodSpec.Builder {
+ beginControlFlow(controlFlow, *args)
+ block()
+ endControlFlow()
+ return this
+}
diff --git a/tools/xmlpersistence/src/main/kotlin/Main.kt b/tools/xmlpersistence/src/main/kotlin/Main.kt
new file mode 100644
index 0000000..e271f8c
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Main.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File
+import java.nio.file.Files
+
+fun main(args: Array<String>) {
+ val showUsage = args.isEmpty() || when (args.singleOrNull()) {
+ "-h", "--help" -> true
+ else -> false
+ }
+ if (showUsage) {
+ usage()
+ return
+ }
+
+ val files = args.flatMap {
+ File(it).walk().filter { it.isFile && it.extension == "java" }.map { it.toPath() }
+ }
+ val persistences = parse(files)
+ for (persistence in persistences) {
+ val file = generate(persistence)
+ Files.newBufferedWriter(persistence.path).use {
+ it.write(FILE_HEADER)
+ file.writeTo(it)
+ }
+ }
+}
+
+private fun usage() {
+ println("Usage: xmlpersistence <FILES>")
+}
diff --git a/tools/xmlpersistence/src/main/kotlin/Parser.kt b/tools/xmlpersistence/src/main/kotlin/Parser.kt
new file mode 100644
index 0000000..3ea12a9
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Parser.kt
@@ -0,0 +1,248 @@
+/*
+ * 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.
+ */
+
+import com.github.javaparser.JavaParser
+import com.github.javaparser.ParseProblemException
+import com.github.javaparser.ParseResult
+import com.github.javaparser.ParserConfiguration
+import com.github.javaparser.ast.Node
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
+import com.github.javaparser.ast.body.FieldDeclaration
+import com.github.javaparser.ast.body.TypeDeclaration
+import com.github.javaparser.ast.expr.AnnotationExpr
+import com.github.javaparser.ast.expr.Expression
+import com.github.javaparser.ast.expr.NormalAnnotationExpr
+import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
+import com.github.javaparser.ast.expr.StringLiteralExpr
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration
+import com.github.javaparser.resolution.types.ResolvedPrimitiveType
+import com.github.javaparser.resolution.types.ResolvedReferenceType
+import com.github.javaparser.symbolsolver.JavaSymbolSolver
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserClassDeclaration
+import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver
+import com.github.javaparser.symbolsolver.resolution.typesolvers.MemoryTypeSolver
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import java.nio.file.Path
+import java.util.Optional
+
+class PersistenceInfo(
+ val name: String,
+ val root: ClassFieldInfo,
+ val path: Path
+)
+
+sealed class FieldInfo {
+ abstract val name: String
+ abstract val xmlName: String?
+ abstract val type: TypeName
+ abstract val isRequired: Boolean
+}
+
+class PrimitiveFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: TypeName,
+ override val isRequired: Boolean
+) : FieldInfo()
+
+class StringFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val isRequired: Boolean
+) : FieldInfo() {
+ override val type: TypeName = ClassName.get(String::class.java)
+}
+
+class ClassFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: ClassName,
+ override val isRequired: Boolean,
+ val fields: List<FieldInfo>
+) : FieldInfo()
+
+class ListFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: ParameterizedTypeName,
+ val element: ClassFieldInfo
+) : FieldInfo() {
+ override val isRequired: Boolean = true
+}
+
+fun parse(files: List<Path>): List<PersistenceInfo> {
+ val typeSolver = CombinedTypeSolver().apply { add(ReflectionTypeSolver()) }
+ val javaParser = JavaParser(ParserConfiguration()
+ .setSymbolResolver(JavaSymbolSolver(typeSolver)))
+ val compilationUnits = files.map { javaParser.parse(it).getOrThrow() }
+ val memoryTypeSolver = MemoryTypeSolver().apply {
+ for (compilationUnit in compilationUnits) {
+ for (typeDeclaration in compilationUnit.getNodesByClass<TypeDeclaration<*>>()) {
+ val name = typeDeclaration.fullyQualifiedName.getOrNull() ?: continue
+ addDeclaration(name, typeDeclaration.resolve())
+ }
+ }
+ }
+ typeSolver.add(memoryTypeSolver)
+ return mutableListOf<PersistenceInfo>().apply {
+ for (compilationUnit in compilationUnits) {
+ val classDeclarations = compilationUnit
+ .getNodesByClass<ClassOrInterfaceDeclaration>()
+ .filter { !it.isInterface && (!it.isNestedType || it.isStatic) }
+ this += classDeclarations.mapNotNull { parsePersistenceInfo(it) }
+ }
+ }
+}
+
+private fun parsePersistenceInfo(classDeclaration: ClassOrInterfaceDeclaration): PersistenceInfo? {
+ val annotation = classDeclaration.getAnnotationByName("XmlPersistence").getOrNull()
+ ?: return null
+ val rootClassName = classDeclaration.nameAsString
+ val name = annotation.getMemberValue("value")?.stringLiteralValue
+ ?: "${rootClassName}Persistence"
+ val rootXmlName = classDeclaration.getAnnotationByName("XmlName").getOrNull()
+ ?.getMemberValue("value")?.stringLiteralValue
+ val root = parseClassFieldInfo(
+ rootXmlName ?: rootClassName, rootXmlName, true, classDeclaration
+ )
+ val path = classDeclaration.findCompilationUnit().get().storage.get().path
+ .resolveSibling("$name.java")
+ return PersistenceInfo(name, root, path)
+}
+
+private fun parseClassFieldInfo(
+ name: String,
+ xmlName: String?,
+ isRequired: Boolean,
+ classDeclaration: ClassOrInterfaceDeclaration
+): ClassFieldInfo {
+ val fields = classDeclaration.fields.filterNot { it.isStatic }.map { parseFieldInfo(it) }
+ val type = classDeclaration.resolve().typeName
+ return ClassFieldInfo(name, xmlName, type, isRequired, fields)
+}
+
+private fun parseFieldInfo(field: FieldDeclaration): FieldInfo {
+ require(field.isPublic && field.isFinal)
+ val variable = field.variables.single()
+ val name = variable.nameAsString
+ val annotations = field.annotations + variable.type.annotations
+ val annotation = annotations.getByName("XmlName")
+ val xmlName = annotation?.getMemberValue("value")?.stringLiteralValue
+ val isRequired = annotations.getByName("NonNull") != null
+ return when (val type = variable.type.resolve()) {
+ is ResolvedPrimitiveType -> {
+ val primitiveType = type.typeName
+ PrimitiveFieldInfo(name, xmlName, primitiveType, true)
+ }
+ is ResolvedReferenceType -> {
+ when (type.qualifiedName) {
+ Boolean::class.javaObjectType.name, Byte::class.javaObjectType.name,
+ Short::class.javaObjectType.name, Char::class.javaObjectType.name,
+ Integer::class.javaObjectType.name, Long::class.javaObjectType.name,
+ Float::class.javaObjectType.name, Double::class.javaObjectType.name ->
+ PrimitiveFieldInfo(name, xmlName, type.typeName, isRequired)
+ String::class.java.name -> StringFieldInfo(name, xmlName, isRequired)
+ List::class.java.name -> {
+ requireNotNull(xmlName)
+ val elementType = type.typeParametersValues().single()
+ require(elementType is ResolvedReferenceType)
+ val listType = ParameterizedTypeName.get(
+ ClassName.get(List::class.java), elementType.typeName
+ )
+ val element = parseClassFieldInfo(
+ "(element)", xmlName, true, elementType.classDeclaration
+ )
+ ListFieldInfo(name, xmlName, listType, element)
+ }
+ else -> parseClassFieldInfo(name, xmlName, isRequired, type.classDeclaration)
+ }
+ }
+ else -> error(type)
+ }
+}
+
+private fun <T> ParseResult<T>.getOrThrow(): T =
+ if (isSuccessful) {
+ result.get()
+ } else {
+ throw ParseProblemException(problems)
+ }
+
+private inline fun <reified T : Node> Node.getNodesByClass(): List<T> =
+ getNodesByClass(T::class.java)
+
+private fun <T : Node> Node.getNodesByClass(klass: Class<T>): List<T> = mutableListOf<T>().apply {
+ if (klass.isInstance(this@getNodesByClass)) {
+ this += klass.cast(this@getNodesByClass)
+ }
+ for (childNode in childNodes) {
+ this += childNode.getNodesByClass(klass)
+ }
+}
+
+private fun <T> Optional<T>.getOrNull(): T? = orElse(null)
+
+private fun List<AnnotationExpr>.getByName(name: String): AnnotationExpr? =
+ find { it.name.identifier == name }
+
+private fun AnnotationExpr.getMemberValue(name: String): Expression? =
+ when (this) {
+ is NormalAnnotationExpr -> pairs.find { it.nameAsString == name }?.value
+ is SingleMemberAnnotationExpr -> if (name == "value") memberValue else null
+ else -> null
+ }
+
+private val Expression.stringLiteralValue: String
+ get() {
+ require(this is StringLiteralExpr)
+ return value
+ }
+
+private val ResolvedReferenceType.classDeclaration: ClassOrInterfaceDeclaration
+ get() {
+ val resolvedClassDeclaration = typeDeclaration
+ require(resolvedClassDeclaration is JavaParserClassDeclaration)
+ return resolvedClassDeclaration.wrappedNode
+ }
+
+private val ResolvedPrimitiveType.typeName: TypeName
+ get() =
+ when (this) {
+ ResolvedPrimitiveType.BOOLEAN -> TypeName.BOOLEAN
+ ResolvedPrimitiveType.BYTE -> TypeName.BYTE
+ ResolvedPrimitiveType.SHORT -> TypeName.SHORT
+ ResolvedPrimitiveType.CHAR -> TypeName.CHAR
+ ResolvedPrimitiveType.INT -> TypeName.INT
+ ResolvedPrimitiveType.LONG -> TypeName.LONG
+ ResolvedPrimitiveType.FLOAT -> TypeName.FLOAT
+ ResolvedPrimitiveType.DOUBLE -> TypeName.DOUBLE
+ }
+
+// This doesn't support type parameters.
+private val ResolvedReferenceType.typeName: TypeName
+ get() = typeDeclaration.typeName
+
+private val ResolvedReferenceTypeDeclaration.typeName: ClassName
+ get() {
+ val packageName = packageName
+ val classNames = className.split(".")
+ val topLevelClassName = classNames.first()
+ val nestedClassNames = classNames.drop(1)
+ return ClassName.get(packageName, topLevelClassName, *nestedClassNames.toTypedArray())
+ }
diff --git a/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt b/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt
new file mode 100644
index 0000000..b4bdbba
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+import java.util.Locale
+
+private val camelHumpBoundary = Regex(
+ "-"
+ + "|_"
+ + "|(?<=[0-9])(?=[^0-9])"
+ + "|(?<=[A-Z])(?=[^A-Za-z]|[A-Z][a-z])"
+ + "|(?<=[a-z])(?=[^a-z])"
+)
+
+private fun String.toCamelHumps(): List<String> = split(camelHumpBoundary)
+
+fun String.toUpperCamelCase(): String =
+ toCamelHumps().joinToString("") { it.toLowerCase(Locale.ROOT).capitalize(Locale.ROOT) }
+
+fun String.toLowerCamelCase(): String = toUpperCamelCase().decapitalize(Locale.ROOT)
+
+fun String.toUpperKebabCase(): String =
+ toCamelHumps().joinToString("-") { it.toUpperCase(Locale.ROOT) }
+
+fun String.toLowerKebabCase(): String =
+ toCamelHumps().joinToString("-") { it.toLowerCase(Locale.ROOT) }
+
+fun String.toUpperSnakeCase(): String =
+ toCamelHumps().joinToString("_") { it.toUpperCase(Locale.ROOT) }
+
+fun String.toLowerSnakeCase(): String =
+ toCamelHumps().joinToString("_") { it.toLowerCase(Locale.ROOT) }