diff options
338 files changed, 7788 insertions, 3246 deletions
diff --git a/Android.bp b/Android.bp index 8ac7de944fe7..df6fdaa5fdf6 100644 --- a/Android.bp +++ b/Android.bp @@ -65,7 +65,6 @@ filegroup { // Java/AIDL sources under frameworks/base ":framework-annotations", ":framework-blobstore-sources", - ":framework-connectivity-tiramisu-sources", ":framework-core-sources", ":framework-drm-sources", ":framework-graphics-nonupdatable-sources", diff --git a/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java b/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java new file mode 100644 index 000000000000..ea3d172b2e5f --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * What do various kinds of addition cost? + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class AdditionPerfTest { + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public int timeAddConstantToLocalInt() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + int result = 0; + while (state.keepRunning()) { + result += 123; + } + return result; + } + @Test + public int timeAddTwoLocalInts() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + int result = 0; + int constant = 123; + while (state.keepRunning()) { + result += constant; + } + return result; + } + @Test + public long timeAddConstantToLocalLong() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + long result = 0; + while (state.keepRunning()) { + result += 123L; + } + return result; + } + @Test + public long timeAddTwoLocalLongs() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + long result = 0; + long constant = 123L; + while (state.keepRunning()) { + result += constant; + } + return result; + } + @Test + public float timeAddConstantToLocalFloat() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + float result = 0.0f; + while (state.keepRunning()) { + result += 123.0f; + } + return result; + } + @Test + public float timeAddTwoLocalFloats() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + float result = 0.0f; + float constant = 123.0f; + while (state.keepRunning()) { + result += constant; + } + return result; + } + @Test + public double timeAddConstantToLocalDouble() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + double result = 0.0; + while (state.keepRunning()) { + result += 123.0; + } + return result; + } + @Test + public double timeAddTwoLocalDoubles() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + double result = 0.0; + double constant = 123.0; + while (state.keepRunning()) { + result += constant; + } + return result; + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java new file mode 100644 index 000000000000..97ab6c7cd6b4 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ArrayCopyPerfTest { + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void timeManualArrayCopy() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + char[] src = new char[8192]; + while (state.keepRunning()) { + char[] dst = new char[8192]; + for (int i = 0; i < 8192; ++i) { + dst[i] = src[i]; + } + } + } + + @Test + public void time_System_arrayCopy() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + char[] src = new char[8192]; + while (state.keepRunning()) { + char[] dst = new char[8192]; + System.arraycopy(src, 0, dst, 0, 8192); + } + } + + @Test + public void time_Arrays_copyOf() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + char[] src = new char[8192]; + while (state.keepRunning()) { + char[] dst = Arrays.copyOf(src, 8192); + } + } + + @Test + public void time_Arrays_copyOfRange() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + char[] src = new char[8192]; + while (state.keepRunning()) { + char[] dst = Arrays.copyOfRange(src, 0, 8192); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java new file mode 100644 index 000000000000..bb452d394d47 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * How do various ways of iterating through an array compare? + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ArrayIterationPerfTest { + + public class Foo { + int mSplat; + } + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + Foo[] mArray = new Foo[27]; + { + for (int i = 0; i < mArray.length; ++i) mArray[i] = new Foo(); + } + @Test + public void timeArrayIteration() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + int sum = 0; + for (int i = 0; i < mArray.length; i++) { + sum += mArray[i].mSplat; + } + } + } + @Test + public void timeArrayIterationCached() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + int sum = 0; + Foo[] localArray = mArray; + int len = localArray.length; + + for (int i = 0; i < len; i++) { + sum += localArray[i].mSplat; + } + } + } + @Test + public void timeArrayIterationForEach() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + int sum = 0; + for (Foo a: mArray) { + sum += a.mSplat; + } + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java new file mode 100644 index 000000000000..ff6d46f6db7f --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.libcore; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.test.suitebuilder.annotation.LargeTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +/** + * Is a hand-coded counted loop through an ArrayList cheaper than enhanced for? + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ArrayListIterationPerfTest { + + public class Foo { + int mSplat; + } + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + ArrayList<Foo> mList = new ArrayList<Foo>(); + { + for (int i = 0; i < 27; ++i) mList.add(new Foo()); + } + @Test + public void timeArrayListIterationIndexed() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + int sum = 0; + ArrayList<Foo> list = mList; + int len = list.size(); + for (int i = 0; i < len; ++i) { + sum += list.get(i).mSplat; + } + } + } + @Test + public void timeArrayListIterationForEach() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + int sum = 0; + for (Foo a : mList) { + sum += a.mSplat; + } + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/OWNERS b/apct-tests/perftests/core/src/android/libcore/OWNERS new file mode 100644 index 000000000000..2d365747473a --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/OWNERS @@ -0,0 +1,2 @@ +# Bug component: 24949 +include platform/libcore:/OWNERS diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 61424ae0e158..1b9cf2648a3f 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -27,6 +27,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -280,6 +281,18 @@ public class AlarmManager { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) public static final long ENABLE_USE_EXACT_ALARM = 218533173L; + /** + * For apps targeting {@link Build.VERSION_CODES#TIRAMISU} or above, the permission + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} will be denied, unless the user explicitly + * allows it from Settings. + * + * TODO (b/226439802): change to EnabledSince(T) after SDK finalization. + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2) + public static final long SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = 226439802L; + @UnsupportedAppUsage private final IAlarmManager mService; private final Context mContext; diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index f67e8d2baa11..881453fe237d 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -66,6 +66,7 @@ import android.app.IAlarmListener; import android.app.IAlarmManager; import android.app.PendingIntent; import android.app.compat.CompatChanges; +import android.app.role.RoleManager; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; @@ -164,6 +165,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Set; import java.util.TimeZone; @@ -224,6 +226,7 @@ public class AlarmManagerService extends SystemService { private ActivityManagerInternal mActivityManagerInternal; private final EconomyManagerInternal mEconomyManagerInternal; private PackageManagerInternal mPackageManagerInternal; + private RoleManager mRoleManager; private volatile PermissionManagerServiceInternal mLocalPermissionManager; final Object mLock = new Object(); @@ -562,6 +565,9 @@ public class AlarmManagerService extends SystemService { @VisibleForTesting static final String KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = "kill_on_schedule_exact_alarm_revoked"; + @VisibleForTesting + static final String KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = + "schedule_exact_alarm_denied_by_default"; private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; @@ -606,6 +612,9 @@ public class AlarmManagerService extends SystemService { private static final boolean DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = true; + // TODO(b/226439802): Flip to true. + private static final boolean DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = false; + // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; @@ -693,6 +702,14 @@ public class AlarmManagerService extends SystemService { public boolean KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED; + /** + * When this is {@code true}, apps with the change + * {@link AlarmManager#SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT} enabled will not get + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} unless the user grants it to them. + */ + public volatile boolean SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = + DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT; + public boolean USE_TARE_POLICY = Settings.Global.DEFAULT_ENABLE_TARE == 1; private long mLastAllowWhileIdleWhitelistDuration = -1; @@ -876,6 +893,15 @@ public class AlarmManagerService extends SystemService { KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED, DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED); break; + case KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT: + final boolean oldValue = SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT; + + SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = properties.getBoolean( + KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, + DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT); + handleScheduleExactAlarmDeniedByDefaultChange(oldValue, + SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT); + break; default: if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { // The quotas need to be updated in order, so we can't just rely @@ -946,6 +972,15 @@ public class AlarmManagerService extends SystemService { } } + private void handleScheduleExactAlarmDeniedByDefaultChange(boolean oldValue, + boolean newValue) { + if (oldValue == newValue) { + return; + } + mHandler.obtainMessage(AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE, + newValue).sendToTarget(); + } + private void migrateAlarmsToNewStoreLocked() { final AlarmStore newStore = LAZY_BATCHING ? new LazyAlarmStore() : new BatchingAlarmStore(); @@ -1122,6 +1157,9 @@ public class AlarmManagerService extends SystemService { pw.print(KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED, KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED); pw.println(); + pw.print(KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, + SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT); + pw.println(); pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY); pw.println(); @@ -1892,11 +1930,10 @@ public class AlarmManagerService extends SystemService { if (hasUseExactAlarmInternal(packageName, uid)) { return; } - - final boolean requested = mExactAlarmCandidates.contains( - UserHandle.getAppId(uid)); - final boolean denyListed = - mConstants.EXACT_ALARM_DENY_LIST.contains(packageName); + if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) { + // Permission not requested, app op doesn't matter. + return; + } final int newMode = mAppOps.checkOpNoThrow( AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, packageName); @@ -1913,11 +1950,24 @@ public class AlarmManagerService extends SystemService { mLastOpScheduleExactAlarm.setValueAt(index, newMode); } } - - final boolean hadPermission = getScheduleExactAlarmState(requested, - denyListed, oldMode); - final boolean hasPermission = getScheduleExactAlarmState(requested, - denyListed, newMode); + if (oldMode == newMode) { + return; + } + final boolean allowedByDefault = + isScheduleExactAlarmAllowedByDefault(packageName, uid); + + final boolean hadPermission; + if (oldMode != AppOpsManager.MODE_DEFAULT) { + hadPermission = (oldMode == AppOpsManager.MODE_ALLOWED); + } else { + hadPermission = allowedByDefault; + } + final boolean hasPermission; + if (newMode != AppOpsManager.MODE_DEFAULT) { + hasPermission = (newMode == AppOpsManager.MODE_ALLOWED); + } else { + hasPermission = allowedByDefault; + } if (hadPermission && !hasPermission) { mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS, @@ -1939,6 +1989,8 @@ public class AlarmManagerService extends SystemService { LocalServices.getService(AppStandbyInternal.class); appStandbyInternal.addListener(new AppStandbyTracker()); + mRoleManager = getContext().getSystemService(RoleManager.class); + mMetricsHelper.registerPuller(() -> mAlarmStore); } } @@ -2525,19 +2577,6 @@ public class AlarmManagerService extends SystemService { } } - private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed, - int appOpMode) { - // This does not account for the state of the USE_EXACT_ALARM permission. - // The caller should do that separately. - if (!requested) { - return false; - } - if (appOpMode == AppOpsManager.MODE_DEFAULT) { - return !denyListed; - } - return appOpMode == AppOpsManager.MODE_ALLOWED; - } - boolean hasUseExactAlarmInternal(String packageName, int uid) { return isUseExactAlarmEnabled(packageName, UserHandle.getUserId(uid)) && (PermissionChecker.checkPermissionForPreflight(getContext(), @@ -2545,6 +2584,32 @@ public class AlarmManagerService extends SystemService { packageName) == PermissionChecker.PERMISSION_GRANTED); } + /** + * Returns whether SCHEDULE_EXACT_ALARM is allowed by default. + */ + boolean isScheduleExactAlarmAllowedByDefault(String packageName, int uid) { + if (isScheduleExactAlarmDeniedByDefault(packageName, UserHandle.getUserId(uid))) { + + // This is essentially like changing the protection level of the permission to + // (privileged|signature|role|appop), but have to implement this logic to maintain + // compatibility for older apps. + if (mPackageManagerInternal.isPlatformSigned(packageName) + || mPackageManagerInternal.isUidPrivileged(uid)) { + return true; + } + final long token = Binder.clearCallingIdentity(); + try { + final List<String> wellbeingHolders = (mRoleManager != null) + ? mRoleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING) + : Collections.emptyList(); + return wellbeingHolders.contains(packageName); + } finally { + Binder.restoreCallingIdentity(token); + } + } + return !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName); + } + boolean hasScheduleExactAlarmInternal(String packageName, int uid) { final long start = mStatLogger.getTime(); @@ -2560,7 +2625,7 @@ public class AlarmManagerService extends SystemService { final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, packageName); if (mode == AppOpsManager.MODE_DEFAULT) { - hasPermission = !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName); + hasPermission = isScheduleExactAlarmAllowedByDefault(packageName, uid); } else { hasPermission = (mode == AppOpsManager.MODE_ALLOWED); } @@ -2860,6 +2925,13 @@ public class AlarmManagerService extends SystemService { packageName, UserHandle.of(userId)); } + private boolean isScheduleExactAlarmDeniedByDefault(String packageName, int userId) { + return mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT + && CompatChanges.isChangeEnabled( + AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, packageName, + UserHandle.of(userId)); + } + @NeverCompile // Avoid size overhead of debugging code. void dumpImpl(IndentingPrintWriter pw) { synchronized (mLock) { @@ -3769,26 +3841,27 @@ public class AlarmManagerService extends SystemService { if (!isExactAlarmChangeEnabled(changedPackage, userId)) { continue; } + if (isScheduleExactAlarmDeniedByDefault(changedPackage, userId)) { + continue; + } if (hasUseExactAlarmInternal(changedPackage, uid)) { continue; } + if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) { + // Permission isn't requested, deny list doesn't matter. + continue; + } final int appOpMode; synchronized (mLock) { appOpMode = mLastOpScheduleExactAlarm.get(uid, AppOpsManager.opToDefaultMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM)); } - final boolean requested = mExactAlarmCandidates.contains(UserHandle.getAppId(uid)); - - // added: true => package was added to the deny list - // added: false => package was removed from the deny list - final boolean hadPermission = getScheduleExactAlarmState(requested, !added, - appOpMode); - final boolean hasPermission = getScheduleExactAlarmState(requested, added, - appOpMode); - - if (hadPermission == hasPermission) { + if (appOpMode != AppOpsManager.MODE_DEFAULT) { + // Deny list doesn't matter. continue; } + // added: true => package was added to the deny list + // added: false => package was removed from the deny list if (added) { synchronized (mLock) { removeExactAlarmsOnPermissionRevokedLocked(uid, @@ -4634,6 +4707,7 @@ public class AlarmManagerService extends SystemService { public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11; public static final int TARE_AFFORDABILITY_CHANGED = 12; public static final int CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE = 13; + public static final int CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE = 14; AlarmHandler() { super(Looper.myLooper()); @@ -4759,6 +4833,35 @@ public class AlarmManagerService extends SystemService { } } break; + case CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE: + final boolean defaultDenied = (Boolean) msg.obj; + + final int[] startedUserIds = mActivityManagerInternal.getStartedUserIds(); + for (int appId : mExactAlarmCandidates) { + for (int userId : startedUserIds) { + uid = UserHandle.getUid(userId, appId); + + final AndroidPackage packageForUid = + mPackageManagerInternal.getPackage(uid); + if (packageForUid == null) { + continue; + } + final String pkg = packageForUid.getPackageName(); + if (defaultDenied) { + if (!hasScheduleExactAlarmInternal(pkg, uid) + && !hasUseExactAlarmInternal(pkg, uid)) { + synchronized (mLock) { + removeExactAlarmsOnPermissionRevokedLocked(uid, pkg, + true); + } + } + } else if (hasScheduleExactAlarmInternal(pkg, uid)) { + sendScheduleExactAlarmPermissionStateChangedBroadcast(pkg, + UserHandle.getUserId(uid)); + } + } + } + break; default: // nope, just ignore it break; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index f26e051581f3..c6ba1eac56c0 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -307,7 +307,7 @@ public final class QuotaController extends StateController { private final SparseBooleanArray mTempAllowlistCache = new SparseBooleanArray(); /** - * Mapping of UIDs to the when their temp allowlist grace period ends (in the elapsed + * Mapping of UIDs to when their temp allowlist grace period ends (in the elapsed * realtime timebase). */ private final SparseLongArray mTempAllowlistGraceCache = new SparseLongArray(); @@ -815,6 +815,19 @@ public final class QuotaController extends StateController { jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); } + private boolean hasTempAllowlistExemptionLocked(int sourceUid, int standbyBucket, + long nowElapsed) { + if (standbyBucket == RESTRICTED_INDEX || standbyBucket == NEVER_INDEX) { + // Don't let RESTRICTED apps get free quota from the temp allowlist. + // TODO: consider granting the exemption to RESTRICTED apps if the temp allowlist allows + // them to start FGS + return false; + } + final long tempAllowlistGracePeriodEndElapsed = mTempAllowlistGraceCache.get(sourceUid); + return mTempAllowlistCache.get(sourceUid) + || nowElapsed < tempAllowlistGracePeriodEndElapsed; + } + /** @return true if the job is within expedited job quota. */ @GuardedBy("mLock") public boolean isWithinEJQuotaLocked(@NonNull final JobStatus jobStatus) { @@ -833,11 +846,8 @@ public final class QuotaController extends StateController { } final long nowElapsed = sElapsedRealtimeClock.millis(); - final long tempAllowlistGracePeriodEndElapsed = - mTempAllowlistGraceCache.get(jobStatus.getSourceUid()); - final boolean hasTempAllowlistExemption = mTempAllowlistCache.get(jobStatus.getSourceUid()) - || nowElapsed < tempAllowlistGracePeriodEndElapsed; - if (hasTempAllowlistExemption) { + if (hasTempAllowlistExemptionLocked(jobStatus.getSourceUid(), + jobStatus.getEffectiveStandbyBucket(), nowElapsed)) { return true; } @@ -2127,10 +2137,8 @@ public final class QuotaController extends StateController { final long nowElapsed = sElapsedRealtimeClock.millis(); final int standbyBucket = JobSchedulerService.standbyBucketForPackage(mPkg.packageName, mPkg.userId, nowElapsed); - final long tempAllowlistGracePeriodEndElapsed = mTempAllowlistGraceCache.get(mUid); final boolean hasTempAllowlistExemption = !mRegularJobTimer - && (mTempAllowlistCache.get(mUid) - || nowElapsed < tempAllowlistGracePeriodEndElapsed); + && hasTempAllowlistExemptionLocked(mUid, standbyBucket, nowElapsed); final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(mUid); final boolean hasTopAppExemption = !mRegularJobTimer && (mTopAppCache.get(mUid) || nowElapsed < topAppGracePeriodEndElapsed); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/README.md b/apex/jobscheduler/service/java/com/android/server/tare/README.md index a4933a127f13..33eadffb8592 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/README.md +++ b/apex/jobscheduler/service/java/com/android/server/tare/README.md @@ -1,10 +1,12 @@ +# Overview + Welcome to The Android Resource Economy (TARE for short). If you're reading this, you may be wondering what all of this code is for and what it means. TARE is an attempt to apply economic principles to resource (principally battery) management. It acknowledges that battery is a limited resource on mobile devices and that the system must allocate and apportion those resources -accordingly. Every action (running a job, firing an alarm, using the network, using the CPU, -etc.) has a cost. Once that action has been performed and that bit of battery has been drained, it's -no longer available for someone else (another app) to use until the user charges the device again. +accordingly. Every action (running a job, firing an alarm, using the network, using the CPU, etc.) +has a cost. Once that action has been performed and that bit of battery has been drained, it's no +longer available for someone else (another app) to use until the user charges the device again. The key tenets of TARE are: @@ -13,10 +15,22 @@ The key tenets of TARE are: 1. Reward for good actions --- reward and encourage behavior that provides value to the user 1. Fine bad actions --- fine and discourage behavior that is bad for the user -# Details +In an ideal world, the system could be said to most efficiently allocate resources by maximizing its +profits — by maximizing the aggregate sum of the difference between an action's price (that +the app ends up paying) and the cost to produce by the system. This assumes that more important +actions have a higher price than less important actions. With this assumption, maximizing profits +implies that the system runs the most important work first and proceeds in decreasing order of +importance. Of course, that also means the system will not run anything where an app would pay less +for the action than the system's cost to produce that action. Some of this breaks down when we throw +TOP apps into the mix — TOP apps pay 0 for all actions, even though the CTP may be greater +than 0. This is to ensure ideal user experience for the app the user is actively interacting with. +Similar caveats exist for system-critical processes (such as the OS itself) and apps running +foreground services (since those could be critical to user experience, as is the case for media and +navigation apps). Excluding those caveats/special situations, maximizing profits of actions +performed by apps in the background should be the target. -To achieve the goal laid out by TARE, we introduce the concept of Android Resource Credits -(ARCs for short). +To achieve the goal laid out by TARE, we use Android Resource Credits (ARCs for short) as the +internal/representative currency of the system. ## How do ARCs work? @@ -36,6 +50,57 @@ all of its ARCs for jobs if it doesn't want to schedule any alarms. With the ARC system, we can limit the total number of ARCs in circulation, thus limiting how much total work can be done, regardless of how many apps the user has installed. +## EconomicPolicy + +An EconomicPolicy defines the actions and rewards a specific subsystem makes use of. Each subsystem +will likely have a unique set of actions that apps can perform, and may choose to reward apps for +certain behaviors. Generally, the app should be rewarded with ARCs for behaviors that indicate that +the app provided value to the user. The current set of behaviors that apps may be rewarded for +include 1) a user seeing a notification, 2) a user interacting with a notification, 3) the user +opening the app and/or staying in the app for some period of time, 4) the user interacting with a +widget, and 5) the user explicitly interacting with the app in some other way. These behaviors may +change as we determine better ways of identifying providing value to the user and/or user desire for +the app to perform the actions it's requesting. + +### Consumption Limit + +The consumption limit represents the maximum amount of resources available to be consumed. When the +battery is satiated (at 100%), then the amount of resources available to be consumed is equal to the +consumption limit. Each action has a cost to produce that action. When the action is performed, +those resources are consumed. Thus, when an action is performed, the action's CTP is deducted from +the remaining amount of resources available. In keeping with the tenet that resources are limited +and ARCs are a proxy for battery consumption, the amount of resources available to be consumed are +adjusted as the battery level changes. That is, the consumption limit is scaled based on the current +battery level, and if the amount currently available to be consumed is greater than the scaled +consumption limit, then the available resources are decreased to match the scaled limit. + +### Regulation + +Regulations are unique events invoked by the ~~government~~ system in order to get the whole economy +moving smoothly. + +# Previous Implementations + +## V0 + +The initial implementation/proposal combined the supply of resources with the allocation in a single +mechanism. It defined the maximum number of resources (ARCs) available at a time, and then divided +(allocated) that number among the installed apps, intending to have some left over that could be +allocated as part of the rewards. There were several problems with that mechanism: + +1. Not all apps used their credits, which meant that allocating credits to those packages + effectively permanently reduced the number of usable/re-allocatable ARCs. +1. Having a global maximum circulation spread across multiple apps meant that as more apps were + installed, the allocation to each app decreased. Eventually (with enough apps installed), no app + would be given enough credits to perform any actions. + +These problems effectively meant that misallocation was a big problem, demand wasn't well reflected, +and some apps may not have been able to perform work even though they otherwise should have been. + +Tare Improvement Proposal #1 (TIP1) separated allocation (to apps) from supply (by the system) and +allowed apps to accrue credits as appropriate while still limiting the total number of credits +consumed. + # Definitions * ARC: Android Resource Credits are the "currency" units used as an abstraction layer over the real diff --git a/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java b/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java index 6a4a4beaa763..7d9260a77158 100644 --- a/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java +++ b/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java @@ -28,43 +28,13 @@ import java.io.PrintStream; public final class LockSettingsCmd extends BaseCommand { - private static final String USAGE = - "usage: locksettings set-pattern [--old OLD_CREDENTIAL] NEW_PATTERN\n" + - " locksettings set-pin [--old OLD_CREDENTIAL] NEW_PIN\n" + - " locksettings set-password [--old OLD_CREDENTIAL] NEW_PASSWORD\n" + - " locksettings clear [--old OLD_CREDENTIAL]\n" + - " locksettings verify [--old OLD_CREDENTIAL]\n" + - " locksettings set-disabled DISABLED\n" + - " locksettings get-disabled\n" + - "\n" + - "flags: \n" + - " --user USER_ID: specify the user, default value is current user\n" + - "\n" + - "locksettings set-pattern: sets a pattern\n" + - " A pattern is specified by a non-separated list of numbers that index the cell\n" + - " on the pattern in a 1-based manner in left to right and top to bottom order,\n" + - " i.e. the top-left cell is indexed with 1, whereas the bottom-right cell\n" + - " is indexed with 9. Example: 1234\n" + - "\n" + - "locksettings set-pin: sets a PIN\n" + - "\n" + - "locksettings set-password: sets a password\n" + - "\n" + - "locksettings clear: clears the unlock credential\n" + - "\n" + - "locksettings verify: verifies the credential and unlocks the user\n" + - "\n" + - "locksettings set-disabled: sets whether the lock screen should be disabled\n" + - "\n" + - "locksettings get-disabled: retrieves whether the lock screen is disabled\n"; - public static void main(String[] args) { (new LockSettingsCmd()).run(args); } @Override public void onShowUsage(PrintStream out) { - out.println(USAGE); + main(new String[] { "help" }); } @Override diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3b843a9c622a..852dd974ae42 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -536,6 +536,9 @@ public final class ActivityThread extends ClientTransactionHandler // A reusable token for other purposes, e.g. content capture, translation. It shouldn't be // used without security checks public IBinder shareableActivityToken; + // The token of the initial TaskFragment that embedded this activity. Do not rely on it + // after creation because the activity could be reparented. + @Nullable public IBinder mInitialTaskFragmentToken; int ident; @UnsupportedAppUsage Intent intent; @@ -618,7 +621,8 @@ public final class ActivityThread extends ClientTransactionHandler PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client, - IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble) { + IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble, + IBinder initialTaskFragmentToken) { this.token = token; this.assistToken = assistToken; this.shareableActivityToken = shareableActivityToken; @@ -639,6 +643,7 @@ public final class ActivityThread extends ClientTransactionHandler compatInfo); mActivityOptions = activityOptions; mLaunchedFromBubble = launchedFromBubble; + mInitialTaskFragmentToken = initialTaskFragmentToken; init(); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index e9df50f1d987..961135f47143 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1207,11 +1207,6 @@ public class Instrumentation { */ public void sendTrackballEventSync(MotionEvent event) { validateNotAppThread(); - if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { - throw new IllegalArgumentException( - "Cannot inject pointer events from sendTrackballEventSync()." - + " Use sendPointerSync() to inject pointer events."); - } if (!event.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) { event.setSource(InputDevice.SOURCE_TRACKBALL); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 88ffdecfb537..d375a9e1ba26 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1682,7 +1682,7 @@ public class DevicePolicyManager { public @interface ProvisioningConfiguration {} /** - * A String extra holding the provisioning trigger. It could be one of + * An int extra holding the provisioning trigger. It could be one of * {@link #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT}, {@link #PROVISIONING_TRIGGER_QR_CODE}, * {@link #PROVISIONING_TRIGGER_MANAGED_ACCOUNT} or {@link * #PROVISIONING_TRIGGER_UNSPECIFIED}. @@ -3298,9 +3298,9 @@ public class DevicePolicyManager { * Activity action: Starts the device policy management role holder updater. * * <p>The activity must handle the device policy management role holder update and set the - * intent result to either {@link Activity#RESULT_OK} if the update was successful, {@link - * #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR} if it encounters a - * problem that may be solved by relaunching it again, {@link + * intent result to either {@link Activity#RESULT_OK} if the update was successful or not + * necessary, {@link #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR} if + * it encounters a problem that may be solved by relaunching it again, {@link * #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_PROVISIONING_DISABLED} if role holder * provisioning is disabled, or {@link * #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR} if it encounters diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index d7e09519bfb7..076dbef9ebc4 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -72,6 +72,7 @@ public class LaunchActivityItem extends ClientTransactionItem { private IBinder mAssistToken; private IBinder mShareableActivityToken; private boolean mLaunchedFromBubble; + private IBinder mTaskFragmentToken; /** * It is only non-null if the process is the first time to launch activity. It is only an * optimization for quick look up of the interface so the field is ignored for comparison. @@ -95,7 +96,8 @@ public class LaunchActivityItem extends ClientTransactionItem { ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, - client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble); + client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble, + mTaskFragmentToken); client.handleLaunchActivity(r, pendingActions, null /* customIntent */); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @@ -119,7 +121,7 @@ public class LaunchActivityItem extends ClientTransactionItem { List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken, IActivityClientController activityClientController, IBinder shareableActivityToken, - boolean launchedFromBubble) { + boolean launchedFromBubble, IBinder taskFragmentToken) { LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class); if (instance == null) { instance = new LaunchActivityItem(); @@ -128,7 +130,7 @@ public class LaunchActivityItem extends ClientTransactionItem { voiceInteractor, procState, state, persistentState, pendingResults, pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken, activityClientController, shareableActivityToken, - launchedFromBubble); + launchedFromBubble, taskFragmentToken); return instance; } @@ -136,7 +138,7 @@ public class LaunchActivityItem extends ClientTransactionItem { @Override public void recycle() { setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null, - null, false, null, null, null, null, false); + null, false, null, null, null, null, false, null); ObjectPool.recycle(this); } @@ -166,6 +168,7 @@ public class LaunchActivityItem extends ClientTransactionItem { dest.writeStrongInterface(mActivityClientController); dest.writeStrongBinder(mShareableActivityToken); dest.writeBoolean(mLaunchedFromBubble); + dest.writeStrongBinder(mTaskFragmentToken); } /** Read from Parcel. */ @@ -184,7 +187,8 @@ public class LaunchActivityItem extends ClientTransactionItem { in.readStrongBinder(), IActivityClientController.Stub.asInterface(in.readStrongBinder()), in.readStrongBinder(), - in.readBoolean()); + in.readBoolean(), + in.readStrongBinder()); } public static final @NonNull Creator<LaunchActivityItem> CREATOR = @@ -222,7 +226,8 @@ public class LaunchActivityItem extends ClientTransactionItem { && mIsForward == other.mIsForward && Objects.equals(mProfilerInfo, other.mProfilerInfo) && Objects.equals(mAssistToken, other.mAssistToken) - && Objects.equals(mShareableActivityToken, other.mShareableActivityToken); + && Objects.equals(mShareableActivityToken, other.mShareableActivityToken) + && Objects.equals(mTaskFragmentToken, other.mTaskFragmentToken); } @Override @@ -244,6 +249,7 @@ public class LaunchActivityItem extends ClientTransactionItem { result = 31 * result + Objects.hashCode(mProfilerInfo); result = 31 * result + Objects.hashCode(mAssistToken); result = 31 * result + Objects.hashCode(mShareableActivityToken); + result = 31 * result + Objects.hashCode(mTaskFragmentToken); return result; } @@ -291,7 +297,7 @@ public class LaunchActivityItem extends ClientTransactionItem { List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken, IActivityClientController activityClientController, - IBinder shareableActivityToken, boolean launchedFromBubble) { + IBinder shareableActivityToken, boolean launchedFromBubble, IBinder taskFragmentToken) { instance.mIntent = intent; instance.mIdent = ident; instance.mInfo = info; @@ -312,5 +318,6 @@ public class LaunchActivityItem extends ClientTransactionItem { instance.mActivityClientController = activityClientController; instance.mShareableActivityToken = shareableActivityToken; instance.mLaunchedFromBubble = launchedFromBubble; + instance.mTaskFragmentToken = taskFragmentToken; } } diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 5df64e3cca9e..d94ad3aa1732 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -623,6 +623,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> @Override public void writeToParcel(Parcel dest, int flags) { + if (!mPhysicalCameraSettings.containsKey(mLogicalCameraId)) { + throw new IllegalStateException("Physical camera settings map must contain a key for " + + "the logical camera id."); + } + int physicalCameraCount = mPhysicalCameraSettings.size(); dest.writeInt(physicalCameraCount); //Logical camera id and settings always come first. diff --git a/core/java/android/hardware/input/InputDeviceSensorManager.java b/core/java/android/hardware/input/InputDeviceSensorManager.java index 89db857b860b..8a40d00327f1 100644 --- a/core/java/android/hardware/input/InputDeviceSensorManager.java +++ b/core/java/android/hardware/input/InputDeviceSensorManager.java @@ -98,7 +98,7 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene */ private void updateInputDeviceSensorInfoLocked(int deviceId) { final InputDevice inputDevice = InputDevice.getDevice(deviceId); - if (inputDevice.hasSensor()) { + if (inputDevice != null && inputDevice.hasSensor()) { final InputSensorInfo[] sensorInfos = mInputManager.getSensorList(deviceId); populateSensorsForInputDeviceLocked(deviceId, sensorInfos); diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index 03d11515c0a8..dc38db2134f4 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -151,6 +151,8 @@ final class NavigationBarController { private boolean mDrawLegacyNavigationBarBackground; + private final Rect mTempRect = new Rect(); + Impl(@NonNull InputMethodService inputMethodService) { mService = inputMethodService; } @@ -257,23 +259,22 @@ final class NavigationBarController { switch (originalInsets.touchableInsets) { case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME: if (inputFrame.getVisibility() == View.VISIBLE) { - touchableRegion = new Region(inputFrame.getLeft(), - inputFrame.getTop(), inputFrame.getRight(), - inputFrame.getBottom()); + inputFrame.getBoundsOnScreen(mTempRect); + touchableRegion = new Region(mTempRect); } break; case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: if (inputFrame.getVisibility() == View.VISIBLE) { - touchableRegion = new Region(inputFrame.getLeft(), - originalInsets.contentTopInsets, inputFrame.getRight(), - inputFrame.getBottom()); + inputFrame.getBoundsOnScreen(mTempRect); + mTempRect.top = originalInsets.contentTopInsets; + touchableRegion = new Region(mTempRect); } break; case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: if (inputFrame.getVisibility() == View.VISIBLE) { - touchableRegion = new Region(inputFrame.getLeft(), - originalInsets.visibleTopInsets, inputFrame.getRight(), - inputFrame.getBottom()); + inputFrame.getBoundsOnScreen(mTempRect); + mTempRect.top = originalInsets.visibleTopInsets; + touchableRegion = new Region(mTempRect); } break; case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: @@ -281,13 +282,13 @@ final class NavigationBarController { touchableRegion.set(originalInsets.touchableRegion); break; } - final Rect navBarRect = new Rect(decor.getLeft(), - decor.getBottom() - systemInsets.bottom, + // Hereafter "mTempRect" means a navigation bar rect. + mTempRect.set(decor.getLeft(), decor.getBottom() - systemInsets.bottom, decor.getRight(), decor.getBottom()); if (touchableRegion == null) { - touchableRegion = new Region(navBarRect); + touchableRegion = new Region(mTempRect); } else { - touchableRegion.union(navBarRect); + touchableRegion.union(mTempRect); } dest.touchableRegion.set(touchableRegion); diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java index 3abe83bd3373..1b503b11816f 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -25,12 +25,6 @@ import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384; import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512; import static android.net.IpSecAlgorithm.CRYPT_AES_CBC; import static android.net.IpSecAlgorithm.CRYPT_AES_CTR; -import static android.net.eap.EapSessionConfig.EapMsChapV2Config; -import static android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig; -import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig; -import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig; -import static android.net.ipsec.ike.IkeSessionParams.IkeAuthEapConfig; -import static android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.internal.util.Preconditions.checkStringNotEmpty; @@ -40,6 +34,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.content.pm.PackageManager; +import android.net.ipsec.ike.IkeDerAsn1DnIdentification; import android.net.ipsec.ike.IkeFqdnIdentification; import android.net.ipsec.ike.IkeIdentification; import android.net.ipsec.ike.IkeIpv4AddrIdentification; @@ -119,8 +114,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { DEFAULT_ALGORITHMS = Collections.unmodifiableList(algorithms); } - @NonNull private final String mServerAddr; - @NonNull private final String mUserIdentity; + @Nullable private final String mServerAddr; + @Nullable private final String mUserIdentity; // PSK authentication @Nullable private final byte[] mPresharedKey; @@ -146,8 +141,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { private Ikev2VpnProfile( int type, - @NonNull String serverAddr, - @NonNull String userIdentity, + @Nullable String serverAddr, + @Nullable String userIdentity, @Nullable byte[] presharedKey, @Nullable X509Certificate serverRootCaCert, @Nullable String username, @@ -165,8 +160,6 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @Nullable IkeTunnelConnectionParams ikeTunConnParams) { super(type, excludeLocalRoutes, requiresInternetValidation); - checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address"); - checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity"); checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms"); mServerAddr = serverAddr; @@ -191,18 +184,12 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { mIsMetered = isMetered; mMaxMtu = maxMtu; mIsRestrictedToTestNetworks = restrictToTestNetworks; - mIkeTunConnParams = ikeTunConnParams; validate(); } private void validate() { - // Server Address not validated except to check an address was provided. This allows for - // dual-stack servers and hostname based addresses. - checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address"); - checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity"); - // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6 // networks, the VPN must provide a link fulfilling the stricter of the two conditions // (at least that of the IPv6 MTU). @@ -210,6 +197,15 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { throw new IllegalArgumentException("Max MTU must be at least" + IPV6_MIN_MTU); } + // Skip validating the other fields if mIkeTunConnParams is set because the required + // information should all come from the mIkeTunConnParams. + if (mIkeTunConnParams != null) return; + + // Server Address not validated except to check an address was provided. This allows for + // dual-stack servers and hostname based addresses. + checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address"); + checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity"); + switch (mType) { case TYPE_IKEV2_IPSEC_USER_PASS: checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username"); @@ -286,22 +282,31 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { /** Retrieves the server address string. */ @NonNull public String getServerAddr() { - return mServerAddr; + if (mIkeTunConnParams == null) return mServerAddr; + + final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams(); + return ikeSessionParams.getServerHostname(); } /** Retrieves the user identity. */ @NonNull public String getUserIdentity() { - return mUserIdentity; + if (mIkeTunConnParams == null) return mUserIdentity; + + final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams(); + return getUserIdentityFromIkeSession(ikeSessionParams); } /** * Retrieves the pre-shared key. * - * <p>May be null if the profile is not using Pre-shared key authentication. + * <p>May be null if the profile is not using Pre-shared key authentication, or the profile is + * built from an {@link IkeTunnelConnectionParams}. */ @Nullable public byte[] getPresharedKey() { + if (mIkeTunConnParams != null) return null; + return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length); } @@ -309,46 +314,62 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { * Retrieves the certificate for the server's root CA. * * <p>May be null if the profile is not using RSA Digital Signature Authentication or - * Username/Password authentication + * Username/Password authentication, or the profile is built from an + * {@link IkeTunnelConnectionParams}. */ @Nullable public X509Certificate getServerRootCaCert() { + if (mIkeTunConnParams != null) return null; + return mServerRootCaCert; } - /** * Retrieves the username. * - * <p>May be null if the profile is not using Username/Password authentication + * <p>May be null if the profile is not using Username/Password authentication, or the profile + * is built from an {@link IkeTunnelConnectionParams}. */ @Nullable public String getUsername() { + if (mIkeTunConnParams != null) return null; + return mUsername; } /** * Retrieves the password. * - * <p>May be null if the profile is not using Username/Password authentication + * <p>May be null if the profile is not using Username/Password authentication, or the profile + * is built from an {@link IkeTunnelConnectionParams}. */ @Nullable public String getPassword() { + if (mIkeTunConnParams != null) return null; + return mPassword; } /** * Retrieves the RSA private key. * - * <p>May be null if the profile is not using RSA Digital Signature authentication + * <p>May be null if the profile is not using RSA Digital Signature authentication, or the + * profile is built from an {@link IkeTunnelConnectionParams}. */ @Nullable public PrivateKey getRsaPrivateKey() { + if (mIkeTunConnParams != null) return null; + return mRsaPrivateKey; } - /** Retrieves the user certificate, if any was set. */ + /** Retrieves the user certificate, if any was set. + * + * <p>May be null if the profile is built from an {@link IkeTunnelConnectionParams}. + */ @Nullable public X509Certificate getUserCert() { + if (mIkeTunConnParams != null) return null; + return mUserCert; } @@ -358,9 +379,14 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { return mProxyInfo; } - /** Returns all the algorithms allowed by this VPN profile. */ + /** Returns all the algorithms allowed by this VPN profile. + * + * <p>May be an empty list if the profile is built from an {@link IkeTunnelConnectionParams}. + */ @NonNull public List<String> getAllowedAlgorithms() { + if (mIkeTunConnParams != null) return new ArrayList<>(); + return mAllowedAlgorithms; } @@ -455,18 +481,25 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @NonNull public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException { final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */, - mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation); - profile.type = mType; - profile.server = mServerAddr; - profile.ipsecIdentifier = mUserIdentity; + mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation, + mIkeTunConnParams); + + profile.server = getServerAddr(); + profile.ipsecIdentifier = getUserIdentity(); profile.proxy = mProxyInfo; - profile.setAllowedAlgorithms(mAllowedAlgorithms); profile.isBypassable = mIsBypassable; profile.isMetered = mIsMetered; profile.maxMtu = mMaxMtu; profile.areAuthParamsInline = true; profile.saveLogin = true; + // The other fields should come from mIkeTunConnParams if it's available. + if (mIkeTunConnParams != null) { + profile.type = VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS; + return profile; + } + profile.type = mType; + profile.setAllowedAlgorithms(mAllowedAlgorithms); switch (mType) { case TYPE_IKEV2_IPSEC_USER_PASS: profile.username = mUsername; @@ -516,10 +549,47 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @NonNull public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile) throws GeneralSecurityException { - // TODO: Build the VpnProfile from mIkeTunConnParams if it exists. - final Builder builder = new Builder(profile.server, profile.ipsecIdentifier); + final Builder builder; + if (profile.ikeTunConnParams == null) { + builder = new Builder(profile.server, profile.ipsecIdentifier); + builder.setAllowedAlgorithms(profile.getAllowedAlgorithms()); + + switch (profile.type) { + case TYPE_IKEV2_IPSEC_USER_PASS: + builder.setAuthUsernamePassword( + profile.username, + profile.password, + certificateFromPemString(profile.ipsecCaCert)); + break; + case TYPE_IKEV2_IPSEC_PSK: + builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret)); + break; + case TYPE_IKEV2_IPSEC_RSA: + final PrivateKey key; + if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) { + final String alias = + profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length()); + key = getPrivateKeyFromAndroidKeystore(alias); + } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) { + key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length())); + } else { + throw new IllegalArgumentException("Invalid RSA private key prefix"); + } + + final X509Certificate userCert = + certificateFromPemString(profile.ipsecUserCert); + final X509Certificate serverRootCa = + certificateFromPemString(profile.ipsecCaCert); + builder.setAuthDigitalSignature(userCert, key, serverRootCa); + break; + default: + throw new IllegalArgumentException("Invalid auth method set"); + } + } else { + builder = new Builder(profile.ikeTunConnParams); + } + builder.setProxy(profile.proxy); - builder.setAllowedAlgorithms(profile.getAllowedAlgorithms()); builder.setBypassable(profile.isBypassable); builder.setMetered(profile.isMetered); builder.setMaxMtu(profile.maxMtu); @@ -527,36 +597,6 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { builder.restrictToTestNetworks(); } - switch (profile.type) { - case TYPE_IKEV2_IPSEC_USER_PASS: - builder.setAuthUsernamePassword( - profile.username, - profile.password, - certificateFromPemString(profile.ipsecCaCert)); - break; - case TYPE_IKEV2_IPSEC_PSK: - builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret)); - break; - case TYPE_IKEV2_IPSEC_RSA: - final PrivateKey key; - if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) { - final String alias = - profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length()); - key = getPrivateKeyFromAndroidKeystore(alias); - } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) { - key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length())); - } else { - throw new IllegalArgumentException("Invalid RSA private key prefix"); - } - - final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert); - final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert); - builder.setAuthDigitalSignature(userCert, key, serverRootCa); - break; - default: - throw new IllegalArgumentException("Invalid auth method set"); - } - if (profile.excludeLocalRoutes && !profile.isBypassable) { Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN"); } @@ -678,82 +718,13 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { } private static void checkBuilderSetter(boolean constructedFromIkeTunConParams, - @NonNull String message) { + @NonNull String field) { if (constructedFromIkeTunConParams) { - throw new IllegalArgumentException("Constructed using IkeTunnelConnectionParams " - + "should not set " + message); - } - } - - private static int getTypeFromIkeSession(@NonNull IkeSessionParams params) { - final IkeAuthConfig config = params.getLocalAuthConfig(); - if (config instanceof IkeAuthDigitalSignLocalConfig) { - return TYPE_IKEV2_IPSEC_RSA; - } else if (config instanceof IkeAuthEapConfig) { - return TYPE_IKEV2_IPSEC_USER_PASS; - } else if (config instanceof IkeAuthPskConfig) { - return TYPE_IKEV2_IPSEC_PSK; - } else { - throw new IllegalStateException("Invalid local IkeAuthConfig"); + throw new IllegalArgumentException( + field + " can't be set with IkeTunnelConnectionParams builder"); } } - @Nullable - private static String getPasswordFromIkeSession(@NonNull IkeSessionParams params) { - if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null; - - final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig(); - final EapMsChapV2Config eapMsChapV2Config = - ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config(); - return (eapMsChapV2Config != null) ? eapMsChapV2Config.getPassword() : null; - } - - @Nullable - private static String getUsernameFromIkeSession(@NonNull IkeSessionParams params) { - if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null; - - final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig(); - final EapMsChapV2Config eapMsChapV2Config = - ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config(); - return (eapMsChapV2Config != null) ? eapMsChapV2Config.getUsername() : null; - } - - @Nullable - private static X509Certificate getUserCertFromIkeSession(@NonNull IkeSessionParams params) { - if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null; - - final IkeAuthDigitalSignLocalConfig config = - (IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig(); - return config.getClientEndCertificate(); - } - - @Nullable - private static X509Certificate getServerRootCaCertFromIkeSession( - @NonNull IkeSessionParams params) { - if (!(params.getRemoteAuthConfig() instanceof IkeAuthDigitalSignRemoteConfig)) return null; - - final IkeAuthDigitalSignRemoteConfig config = - (IkeAuthDigitalSignRemoteConfig) params.getRemoteAuthConfig(); - return config.getRemoteCaCert(); - } - - @Nullable - private static PrivateKey getRsaPrivateKeyFromIkeSession(@NonNull IkeSessionParams params) { - if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null; - - final IkeAuthDigitalSignLocalConfig config = - (IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig(); - return config.getPrivateKey(); - } - - @Nullable - private static byte[] getPresharedKeyFromIkeSession(@NonNull IkeSessionParams params) { - if (!(params.getLocalAuthConfig() instanceof IkeAuthPskConfig)) return null; - - final IkeAuthPskConfig config = (IkeAuthPskConfig) params.getLocalAuthConfig(); - return config.getPsk(); - } - @NonNull private static String getUserIdentityFromIkeSession(@NonNull IkeSessionParams params) { final IkeIdentification ident = params.getLocalIdentification(); @@ -768,6 +739,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { return ((IkeIpv4AddrIdentification) ident).ipv4Address.getHostAddress(); } else if (ident instanceof IkeIpv6AddrIdentification) { return ((IkeIpv6AddrIdentification) ident).ipv6Address.getHostAddress(); + } else if (ident instanceof IkeDerAsn1DnIdentification) { + throw new IllegalArgumentException("Unspported ASN.1 encoded identities"); } else { throw new IllegalArgumentException("Unknown IkeIdentification to get user identity"); } @@ -776,8 +749,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { /** A incremental builder for IKEv2 VPN profiles */ public static final class Builder { private int mType = -1; - @NonNull private final String mServerAddr; - @NonNull private final String mUserIdentity; + @Nullable private final String mServerAddr; + @Nullable private final String mUserIdentity; // PSK authentication @Nullable private byte[] mPresharedKey; @@ -831,19 +804,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { checkNotNull(ikeTunConnParams, MISSING_PARAM_MSG_TMPL, "ikeTunConnParams"); mIkeTunConnParams = ikeTunConnParams; - - final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams(); - mServerAddr = ikeSessionParams.getServerHostname(); - - mType = getTypeFromIkeSession(ikeSessionParams); - mUserCert = getUserCertFromIkeSession(ikeSessionParams); - mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams); - mRsaPrivateKey = getRsaPrivateKeyFromIkeSession(ikeSessionParams); - mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams); - mUsername = getUsernameFromIkeSession(ikeSessionParams); - mPassword = getPasswordFromIkeSession(ikeSessionParams); - mPresharedKey = getPresharedKeyFromIkeSession(ikeSessionParams); - mUserIdentity = getUserIdentityFromIkeSession(ikeSessionParams); + mServerAddr = null; + mUserIdentity = null; } private void resetAuthParams() { @@ -862,6 +824,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { * authentication method may be set. This method will overwrite any previously set * authentication method. * + * <p>It's not allowed to set this if this {@link Builder} is constructed from an + * {@link IkeTunnelConnectionParams}. This information should be retrieved from + * {@link IkeTunnelConnectionParams} + * * @param user the username to be used for EAP-MSCHAPv2 authentication * @param pass the password to be used for EAP-MSCHAPv2 authentication * @param serverRootCa the root certificate to be used for verifying the identity of the @@ -898,6 +864,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { * Only one authentication method may be set. This method will overwrite any previously set * authentication method. * + * <p>It's not allowed to set this if this {@link Builder} is constructed from an + * {@link IkeTunnelConnectionParams}. This information should be retrieved from + * {@link IkeTunnelConnectionParams} + * * @param userCert the username to be used for RSA Digital signiture authentication * @param key the PrivateKey instance associated with the user ceritificate, used for * constructing the signature @@ -936,6 +906,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { * authentication method may be set. This method will overwrite any previously set * authentication method. * + * <p>It's not allowed to set this if this {@link Builder} is constructed from an + * {@link IkeTunnelConnectionParams}. This information should be retrieved from + * {@link IkeTunnelConnectionParams} + * * @param psk the key to be used for Pre-Shared Key authentication * @return this {@link Builder} object to facilitate chaining of method calls */ @@ -1068,6 +1042,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { * Authentication, and one that provides Encryption. Authenticated Encryption with * Associated Data (AEAD) algorithms provide both Authentication and Encryption. * + * <p>It's not allowed to set this if this {@link Builder} is constructed from an + * {@link IkeTunnelConnectionParams}. This information should be retrieved from + * {@link IkeTunnelConnectionParams} + * * <p>By default, this profile will use any algorithm defined in {@link IpSecAlgorithm}, * with the exception of those considered insecure (as described above). * @@ -1079,6 +1057,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS) public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) { checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames"); + checkBuilderSetter(mIkeTunConnParams != null, "algorithmNames"); validateAllowedAlgorithms(algorithmNames); mAllowedAlgorithms = algorithmNames; diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING index a379c33316f0..3df56162bd2c 100644 --- a/core/java/android/net/TEST_MAPPING +++ b/core/java/android/net/TEST_MAPPING @@ -17,7 +17,7 @@ "path": "frameworks/opt/net/wifi" } ], - "postsubmit": [ + "presubmit": [ { "name": "FrameworksCoreTests", "options": [ diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index fe6ae78ccf2d..3be4c3edc10e 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -98,7 +98,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_search_always_expand", "true"); DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "true"); DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true"); - DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "false"); + DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true"); DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true"); } diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 01a037ae3495..4e7b3a51d758 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -193,6 +193,16 @@ public class NtpTrustedTime implements TrustedTime { } final Network network = connectivityManager.getActiveNetwork(); final NetworkInfo ni = connectivityManager.getNetworkInfo(network); + + // This connectivity check is to avoid performing a DNS lookup for the time server on a + // unconnected network. There are races to obtain time in Android when connectivity + // changes, which means that forceRefresh() can be called by various components before + // the network is actually available. This led in the past to DNS lookup failures being + // cached (~2 seconds) thereby preventing the device successfully making an NTP request + // when connectivity had actually been established. + // A side effect of check is that tests that run a fake NTP server on the device itself + // will only be able to use it if the active network is connected, even though loopback + // addresses are actually reachable. if (ni == null || !ni.isConnected()) { if (LOGD) Log.d(TAG, "forceRefresh: no connectivity"); return false; diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java index f5025f7a9e99..f85280f0264b 100644 --- a/core/java/android/util/SparseSetArray.java +++ b/core/java/android/util/SparseSetArray.java @@ -15,15 +15,34 @@ */ package android.util; +import android.annotation.NonNull; + /** * A sparse array of ArraySets, which is suitable to hold userid->packages association. * * @hide */ public class SparseSetArray<T> { - private final SparseArray<ArraySet<T>> mData = new SparseArray<>(); + private final SparseArray<ArraySet<T>> mData; public SparseSetArray() { + mData = new SparseArray<>(); + } + + /** + * Copy constructor + */ + public SparseSetArray(@NonNull SparseSetArray<T> src) { + final int arraySize = src.size(); + mData = new SparseArray<>(arraySize); + for (int i = 0; i < arraySize; i++) { + final int key = src.keyAt(i); + final ArraySet<T> set = src.get(key); + final int setSize = set.size(); + for (int j = 0; j < setSize; j++) { + add(key, set.valueAt(j)); + } + } } /** diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 5f0098c25e55..0c4d9bf08583 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -1584,10 +1584,10 @@ public final class Display { return false; } final Configuration config = mResources.getConfiguration(); - // TODO(b/179308296) Temporarily - never report max bounds to only Launcher if the feature - // is disabled. + // TODO(b/179308296) Temporarily exclude Launcher from being given max bounds, by checking + // if the caller is the recents component. return config != null && !config.windowConfiguration.getMaxBounds().isEmpty() - && (mDisplayInfo.shouldConstrainMetricsForLauncher || !isRecentsComponent()); + && !isRecentsComponent(); } /** diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 6917d664327f..9264d2ed42a3 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -306,13 +306,6 @@ public final class DisplayInfo implements Parcelable { public float brightnessDefault; /** - * @hide - * True if Display#getRealSize and getRealMetrics should be constrained for Launcher, false - * otherwise. - */ - public boolean shouldConstrainMetricsForLauncher = false; - - /** * The {@link RoundedCorners} if present, otherwise {@code null}. */ @Nullable @@ -395,7 +388,6 @@ public final class DisplayInfo implements Parcelable { && brightnessMaximum == other.brightnessMaximum && brightnessDefault == other.brightnessDefault && Objects.equals(roundedCorners, other.roundedCorners) - && shouldConstrainMetricsForLauncher == other.shouldConstrainMetricsForLauncher && installOrientation == other.installOrientation; } @@ -447,7 +439,6 @@ public final class DisplayInfo implements Parcelable { brightnessMaximum = other.brightnessMaximum; brightnessDefault = other.brightnessDefault; roundedCorners = other.roundedCorners; - shouldConstrainMetricsForLauncher = other.shouldConstrainMetricsForLauncher; installOrientation = other.installOrientation; } @@ -505,7 +496,6 @@ public final class DisplayInfo implements Parcelable { for (int i = 0; i < numUserDisabledFormats; i++) { userDisabledHdrTypes[i] = source.readInt(); } - shouldConstrainMetricsForLauncher = source.readBoolean(); installOrientation = source.readInt(); } @@ -561,7 +551,6 @@ public final class DisplayInfo implements Parcelable { for (int i = 0; i < userDisabledHdrTypes.length; i++) { dest.writeInt(userDisabledHdrTypes[i]); } - dest.writeBoolean(shouldConstrainMetricsForLauncher); dest.writeInt(installOrientation); } @@ -817,8 +806,6 @@ public final class DisplayInfo implements Parcelable { sb.append(brightnessMaximum); sb.append(", brightnessDefault "); sb.append(brightnessDefault); - sb.append(", shouldConstrainMetricsForLauncher "); - sb.append(shouldConstrainMetricsForLauncher); sb.append(", installOrientation "); sb.append(Surface.rotationToString(installOrientation)); sb.append("}"); diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index 2dac81c66d2a..e98d046e8c6c 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -33,6 +33,7 @@ import static android.view.RemoteAnimationTargetProto.TASK_ID; import static android.view.RemoteAnimationTargetProto.WINDOW_CONFIGURATION; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; +import android.annotation.ColorInt; import android.annotation.IntDef; import android.app.ActivityManager; import android.app.TaskInfo; @@ -221,6 +222,12 @@ public class RemoteAnimationTarget implements Parcelable { */ public boolean hasAnimatingParent; + /** + * The background color of animation in case the task info is not available if the transition + * is activity level. + */ + public @ColorInt int backgroundColor; + public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position, Rect localBounds, Rect screenSpaceBounds, @@ -279,6 +286,7 @@ public class RemoteAnimationTarget implements Parcelable { allowEnterPip = in.readBoolean(); windowType = in.readInt(); hasAnimatingParent = in.readBoolean(); + backgroundColor = in.readInt(); } @Override @@ -307,6 +315,7 @@ public class RemoteAnimationTarget implements Parcelable { dest.writeBoolean(allowEnterPip); dest.writeInt(windowType); dest.writeBoolean(hasAnimatingParent); + dest.writeInt(backgroundColor); } public void dump(PrintWriter pw, String prefix) { @@ -327,6 +336,7 @@ public class RemoteAnimationTarget implements Parcelable { pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip); pw.print(prefix); pw.print("windowType="); pw.print(windowType); pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent); + pw.print(prefix); pw.print("backgroundColor="); pw.print(backgroundColor); } public void dumpDebug(ProtoOutputStream proto, long fieldId) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d7940e2de98c..8b9a86b9eec6 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -12033,7 +12033,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @Nullable public Rect getHandwritingArea() { final ListenerInfo info = mListenerInfo; - if (info != null) { + if (info != null && info.mHandwritingArea != null) { return new Rect(info.mHandwritingArea); } return null; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7b3fed74a9be..c45a4c7f6e8d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1742,7 +1742,7 @@ public final class ViewRootImpl implements ViewParent, mForceNextWindowRelayout = forceNextWindowRelayout; mPendingAlwaysConsumeSystemBars = args.argi2 != 0; - mSyncSeqId = args.argi4; + mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId; if (msg == MSG_RESIZED_REPORT) { reportNextDraw(); @@ -2767,17 +2767,16 @@ public final class ViewRootImpl implements ViewParent, dispatchApplyInsets(host); } + if (mFirst) { + // make sure touch mode code executes by setting cached value + // to opposite of the added touch mode. + mAttachInfo.mInTouchMode = !mAddedTouchMode; + ensureTouchModeLocally(mAddedTouchMode); + } + boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw); if (layoutRequested) { - - final Resources res = mView.getContext().getResources(); - - if (mFirst) { - // make sure touch mode code executes by setting cached value - // to opposite of the added touch mode. - mAttachInfo.mInTouchMode = !mAddedTouchMode; - ensureTouchModeLocally(mAddedTouchMode); - } else { + if (!mFirst) { if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { windowSizeMayChange = true; @@ -2797,7 +2796,7 @@ public final class ViewRootImpl implements ViewParent, } // Ask host how big it wants to be - windowSizeMayChange |= measureHierarchy(host, lp, res, + windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(), desiredWindowWidth, desiredWindowHeight); } @@ -6407,6 +6406,24 @@ public final class ViewRootImpl implements ViewParent, return FINISH_HANDLED; } + // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the + // view tree and invoke the appropriate {@link OnBackInvokedCallback}. + if (isBack(event) + && mContext != null + && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { + OnBackInvokedCallback topCallback = + getOnBackInvokedDispatcher().getTopCallback(); + if (event.getAction() == KeyEvent.ACTION_UP) { + if (topCallback != null) { + topCallback.onBackInvoked(); + return FINISH_HANDLED; + } + } else { + // Drop other actions such as {@link KeyEvent.ACTION_DOWN}. + return FINISH_NOT_HANDLED; + } + } + // Deliver the key to the view hierarchy. if (mView.dispatchKeyEvent(event)) { return FINISH_HANDLED; @@ -6416,19 +6433,6 @@ public final class ViewRootImpl implements ViewParent, return FINISH_NOT_HANDLED; } - if (isBack(event) - && mContext != null - && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { - // Invoke the appropriate {@link OnBackInvokedCallback} if the new back - // navigation should be used, and the key event is not handled by anything else. - OnBackInvokedCallback topCallback = - getOnBackInvokedDispatcher().getTopCallback(); - if (topCallback != null) { - topCallback.onBackInvoked(); - return FINISH_HANDLED; - } - } - // This dispatch is for windows that don't have a Window.Callback. Otherwise, // the Window.Callback usually will have already called this (see // DecorView.superDispatchKeyEvent) leaving this call a no-op. @@ -7986,7 +7990,10 @@ public final class ViewRootImpl implements ViewParent, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls, mRelayoutBundle); - mSyncSeqId = mRelayoutBundle.getInt("seqid"); + final int maybeSyncSeqId = mRelayoutBundle.getInt("seqid"); + if (maybeSyncSeqId > 0) { + mSyncSeqId = maybeSyncSeqId; + } if (mTranslator != null) { mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame); @@ -10763,11 +10770,7 @@ public final class ViewRootImpl implements ViewParent, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, InputDevice.SOURCE_KEYBOARD); - - ev.setDisplayId(mContext.getDisplay().getDisplayId()); - if (mView != null) { - mView.dispatchKeyEvent(ev); - } + enqueueInputEvent(ev); } private void registerCompatOnBackInvokedCallback() { diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 29a9926aeb9f..aae930edb729 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -216,10 +216,14 @@ public final class WindowManagerGlobal { public String[] getViewRootNames() { synchronized (mLock) { final int numRoots = mRoots.size(); - String[] mViewRoots = new String[numRoots]; + final int windowlessRoots = mWindowlessRoots.size(); + String[] mViewRoots = new String[numRoots + windowlessRoots]; for (int i = 0; i < numRoots; ++i) { mViewRoots[i] = getWindowName(mRoots.get(i)); } + for (int i = 0; i < windowlessRoots; ++i) { + mViewRoots[i + numRoots] = getWindowName(mWindowlessRoots.get(i)); + } return mViewRoots; } } @@ -288,6 +292,10 @@ public final class WindowManagerGlobal { final ViewRootImpl root = mRoots.get(i); if (name.equals(getWindowName(root))) return root.getView(); } + for (int i = mWindowlessRoots.size() - 1; i >= 0; --i) { + final ViewRootImpl root = mWindowlessRoots.get(i); + if (name.equals(getWindowName(root))) return root.getView(); + } } return null; diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java index 5007df574ec1..4b9a957f541d 100644 --- a/core/java/android/window/WindowContextController.java +++ b/core/java/android/window/WindowContextController.java @@ -41,14 +41,13 @@ import java.lang.annotation.Retention; * @hide */ public class WindowContextController { - // TODO(220049234): Disable attach debug logging before shipping. - private static final boolean DEBUG_ATTACH = true; + private static final boolean DEBUG_ATTACH = false; private static final String TAG = "WindowContextController"; /** - * {@link AttachStatus.STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a + * {@link AttachStatus#STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a * {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a - * WindowToken after this flag sets to {@link AttachStatus.STATUS_ATTACHED}. + * WindowToken after this flag sets to {@link AttachStatus#STATUS_ATTACHED}. */ @VisibleForTesting public int mAttachedToDisplayArea = AttachStatus.STATUS_INITIALIZED; diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index bea2c7885d57..cad8b9b64d0b 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -51,9 +51,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private IWindowSession mWindowSession; private IWindow mWindow; private static final String TAG = "WindowOnBackDispatcher"; - private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability"; - private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties - .getInt(BACK_PREDICTABILITY_PROP, 1) > 0; + private static final boolean ENABLE_PREDICTIVE_BACK = SystemProperties + .getInt("persist.wm.debug.predictive_back", 1) != 0; + private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties + .getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0; /** Convenience hashmap to quickly decide if a callback has been added. */ private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>(); @@ -254,18 +255,18 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { public static boolean isOnBackInvokedCallbackEnabled(@Nullable Context context) { // new back is enabled if the feature flag is enabled AND the app does not explicitly // request legacy back. - boolean featureFlagEnabled = IS_BACK_PREDICTABILITY_ENABLED; + boolean featureFlagEnabled = ENABLE_PREDICTIVE_BACK; // If the context is null, we assume true and fallback on the two other conditions. boolean appRequestsPredictiveBack = context != null && context.getApplicationInfo().isOnBackInvokedCallbackEnabled(); if (DEBUG) { Log.d(TAG, TextUtils.formatSimple("App: %s featureFlagEnabled=%s " - + "appRequestsPredictiveBack=%s", + + "appRequestsPredictiveBack=%s alwaysEnforce=%s", context != null ? context.getApplicationInfo().packageName : "null context", - featureFlagEnabled, appRequestsPredictiveBack)); + featureFlagEnabled, appRequestsPredictiveBack, ALWAYS_ENFORCE_PREDICTIVE_BACK)); } - return featureFlagEnabled && appRequestsPredictiveBack; + return featureFlagEnabled && (appRequestsPredictiveBack || ALWAYS_ENFORCE_PREDICTIVE_BACK); } } diff --git a/core/java/com/android/internal/app/AppLocaleStore.java b/core/java/com/android/internal/app/AppLocaleStore.java index 76e58988eedf..f95838516927 100644 --- a/core/java/com/android/internal/app/AppLocaleStore.java +++ b/core/java/com/android/internal/app/AppLocaleStore.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus; + import android.app.LocaleConfig; import android.content.Context; import android.content.pm.PackageManager; @@ -25,41 +27,43 @@ import android.util.Log; import java.util.ArrayList; import java.util.Locale; -public class AppLocaleStore { +class AppLocaleStore { private static final String TAG = AppLocaleStore.class.getSimpleName(); - public static ArrayList<Locale> getAppSupportedLocales(Context context, String packageName) { + public static AppLocaleResult getAppSupportedLocales( + Context context, String packageName) { + LocaleConfig localeConfig = null; + AppLocaleResult.LocaleStatus localeStatus = LocaleStatus.UNKNOWN_FAILURE; ArrayList<Locale> appSupportedLocales = new ArrayList<>(); - LocaleList packageLocaleList = getPackageLocales(context, packageName); - if (packageLocaleList != null && packageLocaleList.size() > 0) { - for (int i = 0; i < packageLocaleList.size(); i++) { - appSupportedLocales.add(packageLocaleList.get(i)); - } - Log.d(TAG, "getAppSupportedLocales from LocaleConfig. Size: " - + appSupportedLocales.size()); - } else { - String[] languages = getAssetLocales(context, packageName); - for (String language : languages) { - appSupportedLocales.add(Locale.forLanguageTag(language)); - } - Log.d(TAG, "getAppSupportedLocales from asset. Size: " - + appSupportedLocales.size()); + try { + localeConfig = new LocaleConfig(context.createPackageContext(packageName, 0)); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); } - return appSupportedLocales; - } - private static LocaleList getPackageLocales(Context context, String packageName) { - try { - LocaleConfig localeConfig = - new LocaleConfig(context.createPackageContext(packageName, 0)); + if (localeConfig != null) { if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) { - return localeConfig.getSupportedLocales(); + LocaleList packageLocaleList = localeConfig.getSupportedLocales(); + if (packageLocaleList.size() > 0) { + localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG; + for (int i = 0; i < packageLocaleList.size(); i++) { + appSupportedLocales.add(packageLocaleList.get(i)); + } + } else { + localeStatus = LocaleStatus.NO_SUPPORTED_LANGUAGE; + } + } else if (localeConfig.getStatus() == LocaleConfig.STATUS_NOT_SPECIFIED) { + localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET; + String[] languages = getAssetLocales(context, packageName); + for (String language : languages) { + appSupportedLocales.add(Locale.forLanguageTag(language)); + } } - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); } - return null; + Log.d(TAG, "getAppSupportedLocales(). status: " + localeStatus + + ", appSupportedLocales:" + appSupportedLocales.size()); + return new AppLocaleResult(localeStatus, appSupportedLocales); } private static String[] getAssetLocales(Context context, String packageName) { @@ -82,4 +86,20 @@ public class AppLocaleStore { return new String[0]; } + static class AppLocaleResult { + enum LocaleStatus { + UNKNOWN_FAILURE, + NO_SUPPORTED_LANGUAGE, + GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG, + GET_SUPPORTED_LANGUAGE_FROM_ASSET, + } + + LocaleStatus mLocaleStatus; + ArrayList<Locale> mAppSupportedLocales; + + public AppLocaleResult(LocaleStatus localeStatus, ArrayList<Locale> appSupportedLocales) { + this.mLocaleStatus = localeStatus; + this.mAppSupportedLocales = appSupportedLocales; + } + } } diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java index 52c74cf81508..213af26a436f 100644 --- a/core/java/com/android/internal/app/LocalePickerWithRegion.java +++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus; + import android.app.FragmentManager; import android.app.FragmentTransaction; import android.app.ListFragment; @@ -158,30 +160,39 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O if (appCurrentLocale != null && !isForCountryMode) { mLocaleList.add(appCurrentLocale); } - filterTheLanguagesNotSupportedInApp(context, appPackageName); - if (!isForCountryMode) { - mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo()); + AppLocaleStore.AppLocaleResult result = + AppLocaleStore.getAppSupportedLocales(context, appPackageName); + boolean shouldShowList = + result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG + || result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET; + + mLocaleList = filterTheLanguagesNotSupportedInApp( + shouldShowList, result.mAppSupportedLocales); + + // Add "system language" + if (!isForCountryMode && shouldShowList) { + mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo(appCurrentLocale == null)); } } return true; } - private void filterTheLanguagesNotSupportedInApp(Context context, String appPackageName) { - ArrayList<Locale> supportedLocales = - AppLocaleStore.getAppSupportedLocales(context, appPackageName); - + private Set<LocaleStore.LocaleInfo> filterTheLanguagesNotSupportedInApp( + boolean shouldShowList, ArrayList<Locale> supportedLocales) { Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>(); - for(LocaleStore.LocaleInfo li: mLocaleList) { - for(Locale l: supportedLocales) { - if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) { - filteredList.add(li); + if (shouldShowList) { + for(LocaleStore.LocaleInfo li: mLocaleList) { + for(Locale l: supportedLocales) { + if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) { + filteredList.add(li); + } } } + Log.d(TAG, "mLocaleList after app-supported filter: " + filteredList.size()); } - Log.d(TAG, "mLocaleList after app-supported filter: " + filteredList.size()); - mLocaleList = filteredList; + return filteredList; } private void returnToParentFrame() { diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java index cea8eaa3ee7f..eb11b9b8b138 100644 --- a/core/java/com/android/internal/app/LocaleStore.java +++ b/core/java/com/android/internal/app/LocaleStore.java @@ -281,9 +281,12 @@ public class LocaleStore { * The "system default" is special case for per-app picker. Intentionally keep the locale * empty to let activity know "system default" been selected. */ - public static LocaleInfo getSystemDefaultLocaleInfo() { + public static LocaleInfo getSystemDefaultLocaleInfo(boolean hasAppLanguage) { LocaleInfo systemDefaultInfo = new LocaleInfo(""); systemDefaultInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SYSTEM_LANGUAGE; + if (hasAppLanguage) { + systemDefaultInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CURRENT; + } systemDefaultInfo.mIsTranslated = true; return systemDefaultInfo; } diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java index 2eb104ed215a..68b8968fe399 100644 --- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java +++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java @@ -27,6 +27,7 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; +import android.widget.FrameLayout; import android.widget.TextView; import com.android.internal.R; @@ -54,7 +55,11 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { private static final int TYPE_HEADER_ALL_OTHERS = 1; private static final int TYPE_LOCALE = 2; private static final int TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER = 3; + private static final int TYPE_CURRENT_LOCALE = 4; private static final int MIN_REGIONS_FOR_SUGGESTIONS = 6; + private static final int APP_LANGUAGE_PICKER_TYPE_COUNT = 5; + private static final int SYSTEM_LANGUAGE_TYPE_COUNT = 3; + private static final int SYSTEM_LANGUAGE_WITHOUT_HEADER_TYPE_COUNT = 1; private ArrayList<LocaleStore.LocaleInfo> mLocaleOptions; private ArrayList<LocaleStore.LocaleInfo> mOriginalLocaleOptions; @@ -93,7 +98,8 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { @Override public boolean isEnabled(int position) { return getItemViewType(position) == TYPE_LOCALE - || getItemViewType(position) == TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER; + || getItemViewType(position) == TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER + || getItemViewType(position) == TYPE_CURRENT_LOCALE; } @Override @@ -112,6 +118,9 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { if (item.isSystemLocale()) { return TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER; } + if (item.isAppCurrentLocale()) { + return TYPE_CURRENT_LOCALE; + } return TYPE_LOCALE; } } @@ -119,11 +128,13 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { @Override public int getViewTypeCount() { if (!TextUtils.isEmpty(mAppPackageName) && showHeaders()) { - return 4; // Two headers and 1 for "System language" + // Two headers, 1 "System language", 1 current locale + return APP_LANGUAGE_PICKER_TYPE_COUNT; } else if (showHeaders()) { - return 3; // Two headers in addition to the locales + // Two headers in addition to the locales + return SYSTEM_LANGUAGE_TYPE_COUNT; } else { - return 1; // Locales items only + return SYSTEM_LANGUAGE_WITHOUT_HEADER_TYPE_COUNT; // Locales items only } } @@ -204,11 +215,15 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { textView.setTextLocale( mDisplayLocale != null ? mDisplayLocale : Locale.getDefault()); break; - case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER: if (!(convertView instanceof ViewGroup)) { - convertView = mInflater.inflate( - R.layout.app_language_picker_system_default, parent, false); + if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) { + convertView = mInflater.inflate( + R.layout.app_language_picker_system_current, parent, false); + } else { + convertView = mInflater.inflate( + R.layout.app_language_picker_system_default, parent, false); + } } Locale defaultLocale = Locale.getDefault(); @@ -219,25 +234,20 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { subtitle.setText(defaultLocale.getDisplayName()); subtitle.setTextLocale(defaultLocale); break; + case TYPE_CURRENT_LOCALE: + if (!(convertView instanceof ViewGroup)) { + convertView = mInflater.inflate( + R.layout.app_language_picker_current_locale_item, parent, false); + } + updateTextView( + convertView, convertView.findViewById(R.id.language_picker_item), position); + break; default: // Covers both null, and "reusing" a wrong kind of view if (!(convertView instanceof ViewGroup)) { convertView = mInflater.inflate(R.layout.language_picker_item, parent, false); } - - TextView text = (TextView) convertView.findViewById(R.id.locale); - LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position); - text.setText(item.getLabel(mCountryMode)); - text.setTextLocale(item.getLocale()); - text.setContentDescription(item.getContentDescription(mCountryMode)); - if (mCountryMode) { - int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent()); - //noinspection ResourceType - convertView.setLayoutDirection(layoutDir); - text.setTextDirection(layoutDir == View.LAYOUT_DIRECTION_RTL - ? View.TEXT_DIRECTION_RTL - : View.TEXT_DIRECTION_LTR); - } + updateTextView(convertView, convertView.findViewById(R.id.locale), position); } return convertView; } @@ -348,4 +358,19 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { public Filter getFilter() { return new FilterByNativeAndUiNames(); } + + private void updateTextView(View convertView, TextView text, int position) { + LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position); + text.setText(item.getLabel(mCountryMode)); + text.setTextLocale(item.getLocale()); + text.setContentDescription(item.getContentDescription(mCountryMode)); + if (mCountryMode) { + int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent()); + //noinspection ResourceType + convertView.setLayoutDirection(layoutDir); + text.setTextDirection(layoutDir == View.LAYOUT_DIRECTION_RTL + ? View.TEXT_DIRECTION_RTL + : View.TEXT_DIRECTION_LTR); + } + } } diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java index 576860d6a50f..b334e9172729 100644 --- a/core/java/com/android/internal/net/VpnProfile.java +++ b/core/java/com/android/internal/net/VpnProfile.java @@ -22,12 +22,17 @@ import android.net.Ikev2VpnProfile; import android.net.PlatformVpnProfile; import android.net.ProxyInfo; import android.net.Uri; +import android.net.ipsec.ike.IkeTunnelConnectionParams; +import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.PersistableBundle; import android.text.TextUtils; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.HexDump; import com.android.net.module.util.ProxyUtils; import java.io.UnsupportedEncodingException; @@ -69,7 +74,8 @@ public final class VpnProfile implements Cloneable, Parcelable { public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; public static final int TYPE_IKEV2_IPSEC_PSK = 7; public static final int TYPE_IKEV2_IPSEC_RSA = 8; - public static final int TYPE_MAX = 8; + public static final int TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS = 9; + public static final int TYPE_MAX = 9; // Match these constants with R.array.vpn_proxy_settings. public static final int PROXY_NONE = 0; @@ -145,25 +151,27 @@ public final class VpnProfile implements Cloneable, Parcelable { public final boolean excludeLocalRoutes; // 25 public final boolean requiresInternetValidation; // 26 + public final IkeTunnelConnectionParams ikeTunConnParams; // 27 // Helper fields. @UnsupportedAppUsage public transient boolean saveLogin = false; public VpnProfile(String key) { - this(key, false, false, false); + this(key, false, false, false, null); } public VpnProfile(String key, boolean isRestrictedToTestNetworks) { - this(key, isRestrictedToTestNetworks, false, false); + this(key, isRestrictedToTestNetworks, false, false, null); } public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes, - boolean requiresInternetValidation) { + boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams) { this.key = key; this.isRestrictedToTestNetworks = isRestrictedToTestNetworks; this.excludeLocalRoutes = excludeLocalRoutes; this.requiresInternetValidation = requiresInternetValidation; + this.ikeTunConnParams = ikeTunConnParams; } @UnsupportedAppUsage @@ -195,6 +203,10 @@ public final class VpnProfile implements Cloneable, Parcelable { isRestrictedToTestNetworks = in.readBoolean(); excludeLocalRoutes = in.readBoolean(); requiresInternetValidation = in.readBoolean(); + final PersistableBundle bundle = + in.readParcelable(PersistableBundle.class.getClassLoader()); + ikeTunConnParams = (bundle == null) ? null + : TunnelConnectionParamsUtils.fromPersistableBundle(bundle); } /** @@ -244,6 +256,8 @@ public final class VpnProfile implements Cloneable, Parcelable { out.writeBoolean(isRestrictedToTestNetworks); out.writeBoolean(excludeLocalRoutes); out.writeBoolean(requiresInternetValidation); + out.writeParcelable(ikeTunConnParams == null ? null + : TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams), flags); } /** @@ -259,15 +273,17 @@ public final class VpnProfile implements Cloneable, Parcelable { } String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1); + // Acceptable numbers of values are: // 14-19: Standard profile, with option for serverCert, proxy // 24: Standard profile with serverCert, proxy and platform-VPN parameters // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks // 26: ...and excludeLocalRoutes - // (26 can only be found on dogfood devices) // 27: ...and requiresInternetValidation + // (26,27 can only be found on dogfood devices) + // 28: ...and ikeTunConnParams if ((values.length < 14 || (values.length > 19 && values.length < 24) - || values.length > 27)) { + || values.length > 28)) { return null; } @@ -292,8 +308,22 @@ public final class VpnProfile implements Cloneable, Parcelable { requiresInternetValidation = false; } + final IkeTunnelConnectionParams tempIkeTunConnParams; + // Assign null directly if the ikeTunConParams field is empty. + if (values.length >= 28 && values[27].length() != 0) { + final Parcel parcel = Parcel.obtain(); + final byte[] bytes = HexDump.hexStringToByteArray(values[27]); + parcel.unmarshall(bytes, 0, bytes.length); + parcel.setDataPosition(0); + final PersistableBundle bundle = (PersistableBundle) parcel.readValue( + PersistableBundle.class.getClassLoader()); + tempIkeTunConnParams = TunnelConnectionParamsUtils.fromPersistableBundle(bundle); + } else { + tempIkeTunConnParams = null; + } + VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks, - excludeLocalRoutes, requiresInternetValidation); + excludeLocalRoutes, requiresInternetValidation, tempIkeTunConnParams); profile.name = values[0]; profile.type = Integer.parseInt(values[1]); if (profile.type < 0 || profile.type > TYPE_MAX) { @@ -345,6 +375,7 @@ public final class VpnProfile implements Cloneable, Parcelable { profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty(); return profile; } catch (Exception e) { + Log.d(TAG, "Got exception in decode.", e); // ignore } return null; @@ -406,6 +437,17 @@ public final class VpnProfile implements Cloneable, Parcelable { builder.append(VALUE_DELIMITER).append(excludeLocalRoutes); builder.append(VALUE_DELIMITER).append(requiresInternetValidation); + if (ikeTunConnParams != null) { + final PersistableBundle bundle = + TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams); + final Parcel parcel = Parcel.obtain(); + parcel.writeValue(bundle); + final byte[] bytes = parcel.marshall(); + builder.append(VALUE_DELIMITER).append(HexDump.toHexString(bytes)); + } else { + builder.append(VALUE_DELIMITER).append(""); + } + return builder.toString().getBytes(StandardCharsets.UTF_8); } @@ -486,7 +528,8 @@ public final class VpnProfile implements Cloneable, Parcelable { key, type, server, username, password, dnsServers, searchDomains, routes, mppe, l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert, proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline, - isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation); + isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation, + ikeTunConnParams); } /** Checks VPN profiles for interior equality. */ @@ -521,7 +564,8 @@ public final class VpnProfile implements Cloneable, Parcelable { && areAuthParamsInline == other.areAuthParamsInline && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks && excludeLocalRoutes == other.excludeLocalRoutes - && requiresInternetValidation == other.requiresInternetValidation; + && requiresInternetValidation == other.requiresInternetValidation + && Objects.equals(ikeTunConnParams, other.ikeTunConnParams); } @NonNull diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 1feb5d415e16..06d12b5195ab 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -46,6 +46,7 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.XmlUtils; +import com.android.modules.utils.build.UnboundedSdkLevel; import libcore.io.IoUtils; import libcore.util.EmptyArray; @@ -58,7 +59,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -126,7 +126,7 @@ public class SystemConfig { * * <p>0 means not specified. */ - public final int onBootclasspathSince; + public final String onBootclasspathSince; /** * SDK version this library was removed from the BOOTCLASSPATH. @@ -138,7 +138,7 @@ public class SystemConfig { * * <p>0 means not specified. */ - public final int onBootclasspathBefore; + public final String onBootclasspathBefore; /** * Declares whether this library can be safely ignored from <uses-library> tags. @@ -155,19 +155,19 @@ public class SystemConfig { @VisibleForTesting public SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) { - this(name, filename, dependencies, 0 /* onBootclasspathSince */, - 0 /* onBootclasspathBefore */, isNative); + this(name, filename, dependencies, null /* onBootclasspathSince */, + null /* onBootclasspathBefore */, isNative); } @VisibleForTesting public SharedLibraryEntry(String name, String filename, String[] dependencies, - int onBootclasspathSince, int onBootclassPathBefore) { - this(name, filename, dependencies, onBootclasspathSince, onBootclassPathBefore, + String onBootclasspathSince, String onBootclasspathBefore) { + this(name, filename, dependencies, onBootclasspathSince, onBootclasspathBefore, false /* isNative */); } SharedLibraryEntry(String name, String filename, String[] dependencies, - int onBootclasspathSince, int onBootclasspathBefore, boolean isNative) { + String onBootclasspathSince, String onBootclasspathBefore, boolean isNative) { this.name = name; this.filename = filename; this.dependencies = dependencies; @@ -175,16 +175,14 @@ public class SystemConfig { this.onBootclasspathBefore = onBootclasspathBefore; this.isNative = isNative; - canBeSafelyIgnored = this.onBootclasspathSince != 0 - && isSdkAtLeast(this.onBootclasspathSince); - } - - private static boolean isSdkAtLeast(int level) { - if ("REL".equals(Build.VERSION.CODENAME)) { - return Build.VERSION.SDK_INT >= level; - } - return level == Build.VERSION_CODES.CUR_DEVELOPMENT - || Build.VERSION.SDK_INT >= level; + // this entry can be ignored if either: + // - onBootclasspathSince is set and we are at or past that SDK + // - onBootclasspathBefore is set and we are before that SDK + canBeSafelyIgnored = + (this.onBootclasspathSince != null + && UnboundedSdkLevel.isAtLeast(this.onBootclasspathSince)) + || (this.onBootclasspathBefore != null + && !UnboundedSdkLevel.isAtLeast(this.onBootclasspathBefore)); } } @@ -878,10 +876,8 @@ public class SystemConfig { String lname = parser.getAttributeValue(null, "name"); String lfile = parser.getAttributeValue(null, "file"); String ldependency = parser.getAttributeValue(null, "dependency"); - int minDeviceSdk = XmlUtils.readIntAttribute(parser, "min-device-sdk", - 0); - int maxDeviceSdk = XmlUtils.readIntAttribute(parser, "max-device-sdk", - 0); + String minDeviceSdk = parser.getAttributeValue(null, "min-device-sdk"); + String maxDeviceSdk = parser.getAttributeValue(null, "max-device-sdk"); if (lname == null) { Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " + parser.getPositionDescription()); @@ -889,15 +885,18 @@ public class SystemConfig { Slog.w(TAG, "<" + name + "> without file in " + permFile + " at " + parser.getPositionDescription()); } else { - boolean allowedMinSdk = minDeviceSdk <= Build.VERSION.SDK_INT; + boolean allowedMinSdk = + minDeviceSdk == null || UnboundedSdkLevel.isAtLeast( + minDeviceSdk); boolean allowedMaxSdk = - maxDeviceSdk == 0 || maxDeviceSdk >= Build.VERSION.SDK_INT; + maxDeviceSdk == null || UnboundedSdkLevel.isAtMost( + maxDeviceSdk); final boolean exists = new File(lfile).exists(); if (allowedMinSdk && allowedMaxSdk && exists) { - int bcpSince = XmlUtils.readIntAttribute(parser, - "on-bootclasspath-since", 0); - int bcpBefore = XmlUtils.readIntAttribute(parser, - "on-bootclasspath-before", 0); + String bcpSince = parser.getAttributeValue(null, + "on-bootclasspath-since"); + String bcpBefore = parser.getAttributeValue(null, + "on-bootclasspath-before"); SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile, ldependency == null ? new String[0] : ldependency.split(":"), @@ -1487,7 +1486,7 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0); } - if (isFilesystemSupported("erofs")) { + if (isErofsSupported()) { if (isKernelVersionAtLeast(5, 10)) { addFeature(PackageManager.FEATURE_EROFS, 0); } else if (isKernelVersionAtLeast(4, 19)) { @@ -1865,11 +1864,10 @@ public class SystemConfig { return Process.myUid() == Process.SYSTEM_UID; } - private static boolean isFilesystemSupported(String fs) { + private static boolean isErofsSupported() { try { - final byte[] fsTableData = Files.readAllBytes(Paths.get("/proc/filesystems")); - final String fsTable = new String(fsTableData, StandardCharsets.UTF_8); - return fsTable.contains("\t" + fs + "\n"); + final Path path = Paths.get("/sys/fs/erofs"); + return Files.exists(path); } catch (Exception e) { return false; } diff --git a/core/proto/android/os/appbackgroundrestrictioninfo.proto b/core/proto/android/os/appbackgroundrestrictioninfo.proto new file mode 100644 index 000000000000..8445641694dc --- /dev/null +++ b/core/proto/android/os/appbackgroundrestrictioninfo.proto @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.os; + +option java_multiple_files = true; + +// This message is used for statsd logging and should be kept in sync with +// frameworks/proto_logging/stats/atoms.proto +/** + * Logs information about app background restrictions. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/AppRestrictionController.java + */ +message AppBackgroundRestrictionsInfo { + // the uid of the app. + optional int32 uid = 1; + + enum RestrictionLevel { + LEVEL_UNKNOWN = 0; + LEVEL_UNRESTRICTED = 1; + LEVEL_EXEMPTED = 2; + LEVEL_ADAPTIVE_BUCKET = 3; + LEVEL_RESTRICTED_BUCKET = 4; + LEVEL_BACKGROUND_RESTRICTED = 5; + LEVEL_HIBERNATION = 6; + } + // indicates the app background restriction level. + optional RestrictionLevel restriction_level = 2; + + enum Threshold { + THRESHOLD_UNKNOWN = 0; + THRESHOLD_RESTRICTED = 1; // app was background restricted by the system. + THRESHOLD_USER = 2; // app was background restricted by user action. + } + // indicates which threshold caused the app to be put into bg restriction. + optional Threshold threshold = 3; + + enum StateTracker { + UNKNOWN_TRACKER = 0; + BATTERY_TRACKER = 1; + BATTERY_EXEMPTION_TRACKER = 2; + FGS_TRACKER = 3; + MEDIA_SESSION_TRACKER = 4; + PERMISSION_TRACKER = 5; + BROADCAST_EVENTS_TRACKER = 6; + BIND_SERVICE_EVENTS_TRACKER = 7; + } + // indicates the reason/tracker which caused the app to hit the threshold. + optional StateTracker tracker = 4; + + message FgsTrackerInfo { + // indicates whether an fgs notification was visible for this app or not. + optional bool fgs_notification_visible = 1; + // total FGS duration for this app. + optional int64 fgs_duration = 2; + } + optional FgsTrackerInfo fgs_tracker_info = 5; + + message BatteryTrackerInfo { + // total battery usage within last 24h (percentage) + optional int32 battery_24h = 1; + // background battery usage (percentage) + optional int32 battery_usage_background = 2; + // FGS battery usage (percentage) + optional int32 battery_usage_fgs = 3; + } + optional BatteryTrackerInfo battery_tracker_info = 6; + + message BroadcastEventsTrackerInfo { + // the number of broadcasts sent by this app. + optional int32 broadcasts_sent = 1; + } + optional BroadcastEventsTrackerInfo broadcast_events_tracker_info = 7; + + message BindServiceEventsTrackerInfo { + // the number of bind service requests by this app. + optional int32 bind_service_requests = 1; + } + optional BindServiceEventsTrackerInfo bind_service_events_tracker_info = + 8; + + // The reasons listed below are defined in PowerExemptionManager.java + enum ExemptionReason { + // range 0-9 is reserved for default reasons + REASON_UNKNOWN = 0; + REASON_DENIED = 1; + REASON_OTHER = 2; + // range 10-49 is reserved for BG-FGS-launch allowed proc states + REASON_PROC_STATE_PERSISTENT = 10; + REASON_PROC_STATE_PERSISTENT_UI = 11; + REASON_PROC_STATE_TOP = 12; + REASON_PROC_STATE_BTOP = 13; + REASON_PROC_STATE_FGS = 14; + REASON_PROC_STATE_BFGS = 15; + // range 50-99 is reserved for BG-FGS-launch allowed reasons + REASON_UID_VISIBLE = 50; + REASON_SYSTEM_UID = 51; + REASON_ACTIVITY_STARTER = 52; + REASON_START_ACTIVITY_FLAG = 53; + REASON_FGS_BINDING = 54; + REASON_DEVICE_OWNER = 55; + REASON_PROFILE_OWNER = 56; + REASON_COMPANION_DEVICE_MANAGER = 57; + REASON_BACKGROUND_ACTIVITY_PERMISSION = 58; + REASON_BACKGROUND_FGS_PERMISSION = 59; + REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60; + REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61; + REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62; + REASON_DEVICE_DEMO_MODE = 63; + REASON_ALLOWLISTED_PACKAGE = 65; + REASON_APPOP = 66; + REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD = 67; + REASON_OP_ACTIVATE_VPN = 68; + REASON_OP_ACTIVATE_PLATFORM_VPN = 69; + REASON_TEMP_ALLOWED_WHILE_IN_USE = 70; + REASON_CURRENT_INPUT_METHOD = 71; + // range 100-199 is reserved for public reasons + REASON_GEOFENCING = 100; + REASON_PUSH_MESSAGING = 101; + REASON_PUSH_MESSAGING_OVER_QUOTA = 102; + REASON_ACTIVITY_RECOGNITION = 103; + REASON_ACCOUNT_TRANSFER = 104; + // range 200-299 is reserved for broadcast actions + REASON_BOOT_COMPLETED = 200; + REASON_PRE_BOOT_COMPLETED = 201; + REASON_LOCKED_BOOT_COMPLETED = 202; + REASON_BLUETOOTH_BROADCAST = 203; + REASON_TIMEZONE_CHANGED = 204; + REASON_TIME_CHANGED = 205; + REASON_LOCALE_CHANGED = 206; + REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED = 207; + REASON_REFRESH_SAFETY_SOURCES = 208; + // range 300-399 is reserved for other internal reasons + REASON_SYSTEM_ALLOW_LISTED = 300; + REASON_ALARM_MANAGER_ALARM_CLOCK = 301; + REASON_ALARM_MANAGER_WHILE_IDLE = 302; + REASON_SERVICE_LAUNCH = 303; + REASON_KEY_CHAIN = 304; + REASON_PACKAGE_VERIFIER = 305; + REASON_SYNC_MANAGER = 306; + REASON_DOMAIN_VERIFICATION_V1 = 307; + REASON_DOMAIN_VERIFICATION_V2 = 308; + REASON_VPN = 309; + REASON_NOTIFICATION_SERVICE = 310; + REASON_PACKAGE_REPLACED = 311; + REASON_LOCATION_PROVIDER = 312; + REASON_MEDIA_BUTTON = 313; + REASON_EVENT_SMS = 314; + REASON_EVENT_MMS = 315; + REASON_SHELL = 316; + REASON_MEDIA_SESSION_CALLBACK = 317; + REASON_ROLE_DIALER = 318; + REASON_ROLE_EMERGENCY = 319; + REASON_SYSTEM_MODULE = 320; + REASON_CARRIER_PRIVILEGED_APP = 321; + // app requested to be exempt + REASON_OPT_OUT_REQUESTED = 1000; + } + // indicates if the app is exempt from background restrictions and the reason if applicable. + optional ExemptionReason exemption_reason = 9; + + enum OptimizationLevel { + UNKNOWN = 0; + OPTIMIZED = 1; + BACKGROUND_RESTRICTED = 2; + NOT_OPTIMIZED = 3; + } + // the user choice for the optimization level of the app. + optional OptimizationLevel opt_level = 10; + + enum TargetSdk { + SDK_UNKNOWN = 0; + SDK_PRE_S = 1; + SDK_S = 2; + SDK_T = 3; + } + // indicates the target sdk level for this app. + optional TargetSdk target_sdk = 11; + + // indicates if the current device is a low ram device. + optional bool low_mem_device = 12; +} + diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 96fe7e1aa541..4075c5f4d8ae 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2086,7 +2086,7 @@ <!-- @SystemApi @hide Allows applications to register network factory or agent --> <permission android:name="android.permission.NETWORK_FACTORY" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- @SystemApi @hide Allows applications to access network stats provider --> <permission android:name="android.permission.NETWORK_STATS_PROVIDER" @@ -2275,13 +2275,13 @@ @hide --> <permission android:name="android.permission.BLUETOOTH_MAP" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows bluetooth stack to access files @hide This should only be used by Bluetooth apk. --> <permission android:name="android.permission.BLUETOOTH_STACK" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows uhid write access for creating virtual input devices @hide @@ -2552,7 +2552,7 @@ <!-- Allows access to configure network interfaces, configure/use IPSec, etc. @hide --> <permission android:name="android.permission.NET_ADMIN" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows registration for remote audio playback. @hide --> <permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK" @@ -2676,7 +2676,7 @@ <!-- Allows listen permission to always reported system signal strength. @hide Used internally. --> <permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- @SystemApi Protects the ability to register any PhoneAccount with PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount @@ -3911,7 +3911,7 @@ Not for use by third party apps. @hide --> <permission android:name="android.permission.MANAGE_APP_OPS_MODES" - android:protectionLevel="signature|installer|verifier" /> + android:protectionLevel="signature|installer|verifier|role" /> <!-- @SystemApi Allows an application to open windows that are for use by parts of the system user interface. @@ -4792,7 +4792,7 @@ <!-- Allows an application to manage the companion devices. @hide --> <permission android:name="android.permission.MANAGE_COMPANION_DEVICES" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows an application to subscribe to notifications about the presence status change of their associated companion device @@ -5041,7 +5041,7 @@ <!-- @TestApi Allows an application to query audio related state. @hide --> <permission android:name="android.permission.QUERY_AUDIO_STATE" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows an application to modify what effects are applied to all audio (matching certain criteria) from any application. @@ -5114,7 +5114,7 @@ @hide --> <permission android:name="android.permission.DEVICE_POWER" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows toggling battery saver on the system. Superseded by DEVICE_POWER permission. @hide @SystemApi @@ -5140,7 +5140,7 @@ <!-- @hide Allows low-level access to tun tap driver --> <permission android:name="android.permission.NET_TUNNELING" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Run as a manufacturer test application, running as the root user. Only available when the device is running in manufacturer test mode. diff --git a/core/res/res/drawable/ic_check_24dp.xml b/core/res/res/drawable/ic_check_24dp.xml new file mode 100644 index 000000000000..a0e21ff2826e --- /dev/null +++ b/core/res/res/drawable/ic_check_24dp.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" + android:fillColor="@android:color/white"/> +</vector> diff --git a/core/res/res/layout/app_language_picker_current_locale_item.xml b/core/res/res/layout/app_language_picker_current_locale_item.xml new file mode 100644 index 000000000000..bf6d9639791a --- /dev/null +++ b/core/res/res/layout/app_language_picker_current_locale_item.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight=".8"> + <include + android:id="@+id/language_picker_item" + layout="@layout/language_picker_item" /> + </FrameLayout> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight=".2" + android:gravity="center" + android:minHeight="?android:attr/listPreferredItemHeight"> + <ImageView + android:id="@+id/imageView" + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/ic_check_24dp" + app:tint="#0F9D58"/> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/layout/app_language_picker_system_current.xml b/core/res/res/layout/app_language_picker_system_current.xml new file mode 100644 index 000000000000..341ee2528671 --- /dev/null +++ b/core/res/res/layout/app_language_picker_system_current.xml @@ -0,0 +1,45 @@ +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight=".8"> + <include + android:id="@+id/system_language_view" + layout="@layout/app_language_picker_system_default" /> + </FrameLayout> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight=".2" + android:gravity="center" + android:minHeight="?android:attr/listPreferredItemHeight"> + <ImageView + android:id="@+id/imageView" + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/ic_check_24dp" + app:tint="#0F9D58"/> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 783fabe20a6d..f5c82d5019ad 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -35,4 +35,9 @@ <color name="personal_apps_suspension_notification_color">#8AB4F8</color> <color name="overview_background">@color/overview_background_dark</color> + + <color name="user_icon_4">#fff439a0</color><!-- pink --> + <color name="user_icon_6">#ff4ecde6</color><!-- cyan --> + <color name="user_icon_7">#fffbbc04</color><!-- yellow --> + <color name="user_icon_8">#fffa903e</color><!-- orange --> </resources> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 54325e590347..71c98d002984 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -174,14 +174,14 @@ <color name="system_notification_accent_color">#00000000</color> <!-- Default user icon colors --> - <color name="user_icon_1">#ff00bcd4</color><!-- cyan 500 --> - <color name="user_icon_2">#ff3f51b5</color><!-- indigo 500 --> - <color name="user_icon_3">#ff4285f4</color><!-- blue 500 --> - <color name="user_icon_4">#ffe91e63</color><!-- pink 500 --> - <color name="user_icon_5">#ff0f9d58</color><!-- green 500 --> - <color name="user_icon_6">#ff8bc34a</color><!-- light green 500 --> - <color name="user_icon_7">#ffff9800</color><!-- orange 500 --> - <color name="user_icon_8">#ffff5722</color><!-- deep orange 500 --> + <color name="user_icon_1">#ffe46962</color><!-- red --> + <color name="user_icon_2">#ffaf5cf7</color><!-- purple --> + <color name="user_icon_3">#ff4c8df6</color><!-- blue --> + <color name="user_icon_4">#fff439a0</color><!-- pink --> + <color name="user_icon_5">#ff1ea446</color><!-- green --> + <color name="user_icon_6">#ff129eaf</color><!-- cyan --> + <color name="user_icon_7">#ffb26c00</color><!-- yellow --> + <color name="user_icon_8">#ffe8710a</color><!-- orange --> <color name="user_icon_default_gray">#ff9e9e9e</color><!-- gray 500 --> <color name="user_icon_default_white">#ffffffff</color><!-- white --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 60d875c2ca39..42f789bb59fc 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2918,6 +2918,11 @@ com.android.settings.intelligence </string> + <!-- System bluetooth stack package name --> + <string name="config_systemBluetoothStack" translatable="false"> + com.android.bluetooth.services + </string> + <!-- Flag indicating that the media framework should not allow changes or mute on any stream or global volumes. --> <bool name="config_useFixedVolume">false</bool> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index 870f54989674..86bad7f12258 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -184,6 +184,8 @@ <public name="safety_protection_display_text" /> <!-- @hide @SystemApi --> <public name="config_systemSettingsIntelligence" /> + <!-- @hide --> + <public name="config_systemBluetoothStack" /> </staging-public-group> <staging-public-group type="dimen" first-id="0x01db0000"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7069e716087d..776d3dafe8c0 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2303,6 +2303,7 @@ <java-symbol type="drawable" name="scrubber_control_disabled_holo" /> <java-symbol type="drawable" name="scrubber_control_selector_holo" /> <java-symbol type="drawable" name="scrubber_progress_horizontal_holo_dark" /> + <java-symbol type="drawable" name="progress_small_material" /> <java-symbol type="string" name="chooseUsbActivity" /> <java-symbol type="string" name="ext_media_badremoval_notification_message" /> <java-symbol type="string" name="ext_media_badremoval_notification_title" /> @@ -4753,7 +4754,11 @@ <java-symbol type="drawable" name="ic_swap_horiz" /> <java-symbol type="bool" name="config_notificationForceUserSetOnUpgrade" /> + <!-- For app language picker --> <java-symbol type="string" name="system_locale_title" /> <java-symbol type="layout" name="app_language_picker_system_default" /> + <java-symbol type="layout" name="app_language_picker_system_current" /> + <java-symbol type="layout" name="app_language_picker_current_locale_item" /> <java-symbol type="id" name="system_locale_subtitle" /> + <java-symbol type="id" name="language_picker_item" /> </resources> diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 3e261a7113ac..50639be57f22 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -157,7 +157,7 @@ public class ObjectPoolTests { .setPendingResults(resultInfoList()).setPendingNewIntents(referrerIntentList()) .setIsForward(true).setAssistToken(assistToken) .setShareableActivityToken(shareableActivityToken) - .build(); + .setTaskFragmentToken(new Binder()).build(); LaunchActivityItem emptyItem = new LaunchActivityItemBuilder().build(); LaunchActivityItem item = itemSupplier.get(); diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java index 1467fed898c4..26d9628ba55b 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java +++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java @@ -110,6 +110,7 @@ class TestUtils { private IBinder mAssistToken; private IBinder mShareableActivityToken; private boolean mLaunchedFromBubble; + private IBinder mTaskFragmentToken; LaunchActivityItemBuilder setIntent(Intent intent) { mIntent = intent; @@ -206,13 +207,18 @@ class TestUtils { return this; } + LaunchActivityItemBuilder setTaskFragmentToken(IBinder taskFragmentToken) { + mTaskFragmentToken = taskFragmentToken; + return this; + } + LaunchActivityItem build() { return LaunchActivityItem.obtain(mIntent, mIdent, mInfo, mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, mAssistToken, null /* activityClientController */, mShareableActivityToken, - mLaunchedFromBubble); + mLaunchedFromBubble, mTaskFragmentToken); } } } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index beadc4464516..8276d10be4cd 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -203,6 +203,7 @@ public class TransactionParcelTests { .setPendingResults(resultInfoList()).setActivityOptions(ActivityOptions.makeBasic()) .setPendingNewIntents(referrerIntentList()).setIsForward(true) .setAssistToken(new Binder()).setShareableActivityToken(new Binder()) + .setTaskFragmentToken(new Binder()) .build(); writeAndPrepareForReading(item); diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index b006a1681a99..8d3751e6ad6c 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -345,7 +345,7 @@ public class ActivityThreadClientTest { null /* pendingResults */, null /* pendingNewIntents */, null /* activityOptions */, true /* isForward */, null /* profilerInfo */, mThread /* client */, null /* asssitToken */, null /* shareableActivityToken */, - false /* launchedFromBubble */); + false /* launchedFromBubble */, null /* taskfragmentToken */); } @Override diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 58a2073981bf..04ead1bf1e9c 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -33,6 +33,30 @@ applications that come with the platform <permission name="android.permission.WRITE_SECURE_SETTINGS"/> </privapp-permissions> + <privapp-permissions package="com.android.bluetooth.services"> + <permission name="android.permission.DUMP"/> + <permission name="android.permission.MODIFY_AUDIO_ROUTING"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="android.permission.TETHER_PRIVILEGED"/> + <permission name="android.permission.CALL_PRIVILEGED"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/> + <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/> + <permission name="android.permission.UPDATE_DEVICE_STATS"/> + <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.NFC_HANDOVER_STATUS"/> + <permission name="android.permission.CONNECTIVITY_INTERNAL"/> + <permission name="android.permission.BLUETOOTH_PRIVILEGED"/> + <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + <permission name="android.permission.REAL_GET_TASKS"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/> + <permission name="android.permission.WRITE_APN_SETTINGS"/> + <permission name="android.permission.UPDATE_APP_OPS_STATS"/> + </privapp-permissions> + <privapp-permissions package="com.android.backupconfirm"> <permission name="android.permission.BACKUP"/> <permission name="android.permission.CRYPT_KEEPER"/> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index c76fa9658c67..01f5feb9b13e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -615,14 +615,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return null; } + private void updateCallbackIfNecessary() { + updateCallbackIfNecessary(true /* deferCallbackUntilAllActivitiesCreated */); + } + /** * Notifies listeners about changes to split states if necessary. + * + * @param deferCallbackUntilAllActivitiesCreated boolean to indicate whether the split info + * callback should be deferred until all the + * organized activities have been created. */ - private void updateCallbackIfNecessary() { + private void updateCallbackIfNecessary(boolean deferCallbackUntilAllActivitiesCreated) { if (mEmbeddingCallback == null) { return; } - if (!allActivitiesCreated()) { + if (deferCallbackUntilAllActivitiesCreated && !allActivitiesCreated()) { return; } List<SplitInfo> currentSplitStates = getActiveSplitStates(); @@ -838,6 +846,36 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter { @Override + public void onActivityPreCreated(Activity activity, Bundle savedInstanceState) { + final IBinder activityToken = activity.getActivityToken(); + final IBinder initialTaskFragmentToken = ActivityThread.currentActivityThread() + .getActivityClient(activityToken).mInitialTaskFragmentToken; + // If the activity is not embedded, then it will not have an initial task fragment token + // so no further action is needed. + if (initialTaskFragmentToken == null) { + return; + } + for (int i = mTaskContainers.size() - 1; i >= 0; i--) { + final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i) + .mContainers; + for (int j = containers.size() - 1; j >= 0; j--) { + final TaskFragmentContainer container = containers.get(j); + if (!container.hasActivity(activityToken) + && container.getTaskFragmentToken().equals(initialTaskFragmentToken)) { + // The onTaskFragmentInfoChanged callback containing this activity has not + // reached the client yet, so add the activity to the pending appeared + // activities and send a split info callback to the client before + // {@link Activity#onCreate} is called. + container.addPendingAppearedActivity(activity); + updateCallbackIfNecessary( + false /* deferCallbackUntilAllActivitiesCreated */); + return; + } + } + } + } + + @Override public void onActivityPostCreated(Activity activity, Bundle savedInstanceState) { // Calling after Activity#onCreate is complete to allow the app launch something // first. In case of a configured placeholder activity we want to make sure diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index e49af41d4eac..9a12669f078a 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -120,7 +120,7 @@ class TaskFragmentContainer { } ActivityStack toActivityStack() { - return new ActivityStack(collectActivities(), mInfo.getRunningActivityCount() == 0); + return new ActivityStack(collectActivities(), isEmpty()); } void addPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) { diff --git a/libs/WindowManager/Jetpack/tests/OWNERS b/libs/WindowManager/Jetpack/tests/OWNERS index f2c3388023be..ac522b2dde10 100644 --- a/libs/WindowManager/Jetpack/tests/OWNERS +++ b/libs/WindowManager/Jetpack/tests/OWNERS @@ -1,4 +1,4 @@ -# Bug component: 909476 +# Bug component: 1157642 # includes OWNERS from parent directories charlesccchen@google.com diegovela@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index e528df8c89b4..3f0b01bef0ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -131,7 +131,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou mDisplayController.getDisplayContext(mRootTaskInfo.displayId), mRootTaskInfo.configuration, this /* layoutChangeListener */, mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(), - true /* applyDismissingParallax */); + SplitLayout.PARALLAX_DISMISSING); mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout); final WindowContainerToken token1 = task1.token; @@ -327,13 +327,15 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou @Override public void onLayoutPositionChanging(SplitLayout layout) { mSyncQueue.runInSync(t -> - layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2)); + layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2, + true /* applyResizingOffset */)); } @Override public void onLayoutSizeChanging(SplitLayout layout) { mSyncQueue.runInSync(t -> - layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2)); + layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2, + true /* applyResizingOffset */)); } @Override @@ -342,7 +344,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> - layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2)); + layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2, + false /* applyResizingOffset */)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 577ced53f992..42ac19509693 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -48,18 +48,16 @@ import com.android.wm.shell.common.annotations.ShellMainThread; * Controls the window animation run when a user initiates a back gesture. */ public class BackAnimationController implements RemoteCallable<BackAnimationController> { - - private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP = - "persist.debug.back_predictability_progress_threshold"; - // By default, enable new back dispatching without any animations. - private static final int BACK_PREDICTABILITY_PROP = - SystemProperties.getInt("persist.debug.back_predictability", 1); - public static final boolean IS_ENABLED = BACK_PREDICTABILITY_PROP > 0; - private static final int PROGRESS_THRESHOLD = SystemProperties - .getInt(BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP, -1); private static final String TAG = "BackAnimationController"; + private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP = + "persist.wm.debug.predictive_back_progress_threshold"; + public static final boolean IS_ENABLED = + SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0; + private static final int PROGRESS_THRESHOLD = SystemProperties + .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1); @VisibleForTesting - boolean mEnableAnimations = (BACK_PREDICTABILITY_PROP & (1 << 1)) != 0; + boolean mEnableAnimations = SystemProperties.getInt( + "persist.wm.debug.predictive_back_anim", 0) != 0; /** * Location of the initial touch event of the back gesture. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 116d3524e711..ec81d230fae7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -72,6 +72,10 @@ import java.io.PrintWriter; */ public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener { + public static final int PARALLAX_NONE = 0; + public static final int PARALLAX_DISMISSING = 1; + public static final int PARALLAX_ALIGN_CENTER = 2; + private final int mDividerWindowWidth; private final int mDividerInsets; private final int mDividerSize; @@ -87,7 +91,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private final SplitWindowManager mSplitWindowManager; private final DisplayImeController mDisplayImeController; private final ImePositionProcessor mImePositionProcessor; - private final DismissingEffectPolicy mDismissingEffectPolicy; + private final ResizingEffectPolicy mSurfaceEffectPolicy; private final ShellTaskOrganizer mTaskOrganizer; private final InsetsState mInsetsState = new InsetsState(); @@ -105,7 +109,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange SplitLayoutHandler splitLayoutHandler, SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks, DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer, - boolean applyDismissingParallax) { + int parallaxType) { mContext = context.createConfigurationContext(configuration); mOrientation = configuration.orientation; mRotation = configuration.windowConfiguration.getRotation(); @@ -115,7 +119,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange parentContainerCallbacks); mTaskOrganizer = taskOrganizer; mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId()); - mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax); + mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType); final Resources resources = context.getResources(); mDividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width); @@ -281,7 +285,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */); DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */); - mDismissingEffectPolicy.applyDividerPosition(position, isLandscape); + mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape); } /** Inflates {@link DividerView} on the root surface. */ @@ -486,7 +490,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */ public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1, - SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { + SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2, + boolean applyResizingOffset) { final SurfaceControl dividerLeash = getDividerLeash(); if (dividerLeash != null) { mTempRect.set(getRefDividerBounds()); @@ -506,7 +511,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return; } - mDismissingEffectPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2); + mSurfaceEffectPolicy.adjustDimSurface(t, dimLayer1, dimLayer2); + if (applyResizingOffset) { + mSurfaceEffectPolicy.adjustRootSurface(t, leash1, leash2); + } } /** Apply recorded task layout to the {@link WindowContainerTransaction}. */ @@ -590,7 +598,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * Calls when resizing the split bounds. * * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl, - * SurfaceControl, SurfaceControl) + * SurfaceControl, SurfaceControl, boolean) */ void onLayoutSizeChanging(SplitLayout layout); @@ -600,7 +608,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * @see #applyTaskChanges(WindowContainerTransaction, ActivityManager.RunningTaskInfo, * ActivityManager.RunningTaskInfo) * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl, - * SurfaceControl, SurfaceControl) + * SurfaceControl, SurfaceControl, boolean) */ void onLayoutSizeChanged(SplitLayout layout); @@ -609,7 +617,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * panel. * * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl, - * SurfaceControl, SurfaceControl) + * SurfaceControl, SurfaceControl, boolean) */ void onLayoutPositionChanging(SplitLayout layout); @@ -637,21 +645,25 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * Calculates and applies proper dismissing parallax offset and dimming value to hint users * dismissing gesture. */ - private class DismissingEffectPolicy { + private class ResizingEffectPolicy { /** Indicates whether to offset splitting bounds to hint dismissing progress or not. */ - private final boolean mApplyParallax; + private final int mParallaxType; + + int mShrinkSide = DOCKED_INVALID; // The current dismissing side. int mDismissingSide = DOCKED_INVALID; // The parallax offset to hint the dismissing side and progress. - final Point mDismissingParallaxOffset = new Point(); + final Point mParallaxOffset = new Point(); // The dimming value to hint the dismissing side and progress. float mDismissingDimValue = 0.0f; + final Rect mContentBounds = new Rect(); + final Rect mSurfaceBounds = new Rect(); - DismissingEffectPolicy(boolean applyDismissingParallax) { - mApplyParallax = applyDismissingParallax; + ResizingEffectPolicy(int parallaxType) { + mParallaxType = parallaxType; } /** @@ -662,7 +674,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange */ void applyDividerPosition(int position, boolean isLandscape) { mDismissingSide = DOCKED_INVALID; - mDismissingParallaxOffset.set(0, 0); + mParallaxOffset.set(0, 0); mDismissingDimValue = 0; int totalDismissingDistance = 0; @@ -676,15 +688,39 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange - mDividerSnapAlgorithm.getDismissEndTarget().position; } + final boolean topLeftShrink = isLandscape + ? position < mWinBounds1.right : position < mWinBounds1.bottom; + if (topLeftShrink) { + mShrinkSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP; + mContentBounds.set(mWinBounds1); + mSurfaceBounds.set(mBounds1); + } else { + mShrinkSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM; + mContentBounds.set(mWinBounds2); + mSurfaceBounds.set(mBounds2); + } + if (mDismissingSide != DOCKED_INVALID) { float fraction = Math.max(0, Math.min(mDividerSnapAlgorithm.calculateDismissingFraction(position), 1f)); mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction); - fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide); + if (mParallaxType == PARALLAX_DISMISSING) { + fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide); + if (isLandscape) { + mParallaxOffset.x = (int) (fraction * totalDismissingDistance); + } else { + mParallaxOffset.y = (int) (fraction * totalDismissingDistance); + } + } + } + + if (mParallaxType == PARALLAX_ALIGN_CENTER) { if (isLandscape) { - mDismissingParallaxOffset.x = (int) (fraction * totalDismissingDistance); + mParallaxOffset.x = + (mSurfaceBounds.width() - mContentBounds.width()) / 2; } else { - mDismissingParallaxOffset.y = (int) (fraction * totalDismissingDistance); + mParallaxOffset.y = + (mSurfaceBounds.height() - mContentBounds.height()) / 2; } } } @@ -704,41 +740,66 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } /** Applies parallax offset and dimming value to the root surface at the dismissing side. */ - boolean adjustDismissingSurface(SurfaceControl.Transaction t, - SurfaceControl leash1, SurfaceControl leash2, + void adjustRootSurface(SurfaceControl.Transaction t, + SurfaceControl leash1, SurfaceControl leash2) { + SurfaceControl targetLeash = null; + + if (mParallaxType == PARALLAX_DISMISSING) { + switch (mDismissingSide) { + case DOCKED_TOP: + case DOCKED_LEFT: + targetLeash = leash1; + mTempRect.set(mBounds1); + break; + case DOCKED_BOTTOM: + case DOCKED_RIGHT: + targetLeash = leash2; + mTempRect.set(mBounds2); + break; + } + } else if (mParallaxType == PARALLAX_ALIGN_CENTER) { + switch (mShrinkSide) { + case DOCKED_TOP: + case DOCKED_LEFT: + targetLeash = leash1; + mTempRect.set(mBounds1); + break; + case DOCKED_BOTTOM: + case DOCKED_RIGHT: + targetLeash = leash2; + mTempRect.set(mBounds2); + break; + } + } + if (mParallaxType != PARALLAX_NONE && targetLeash != null) { + t.setPosition(targetLeash, + mTempRect.left + mParallaxOffset.x, mTempRect.top + mParallaxOffset.y); + // Transform the screen-based split bounds to surface-based crop bounds. + mTempRect.offsetTo(-mParallaxOffset.x, -mParallaxOffset.y); + t.setWindowCrop(targetLeash, mTempRect); + } + } + + void adjustDimSurface(SurfaceControl.Transaction t, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { - SurfaceControl targetLeash, targetDimLayer; + SurfaceControl targetDimLayer; switch (mDismissingSide) { case DOCKED_TOP: case DOCKED_LEFT: - targetLeash = leash1; targetDimLayer = dimLayer1; - mTempRect.set(mBounds1); break; case DOCKED_BOTTOM: case DOCKED_RIGHT: - targetLeash = leash2; targetDimLayer = dimLayer2; - mTempRect.set(mBounds2); break; case DOCKED_INVALID: default: t.setAlpha(dimLayer1, 0).hide(dimLayer1); t.setAlpha(dimLayer2, 0).hide(dimLayer2); - return false; - } - - if (mApplyParallax) { - t.setPosition(targetLeash, - mTempRect.left + mDismissingParallaxOffset.x, - mTempRect.top + mDismissingParallaxOffset.y); - // Transform the screen-based split bounds to surface-based crop bounds. - mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y); - t.setWindowCrop(targetLeash, mTempRect); + return; } t.setAlpha(targetDimLayer, mDismissingDimValue) .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f); - return true; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 73f393140cbc..7f9ecca20b48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -63,6 +63,7 @@ import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipController; +import com.android.wm.shell.pip.phone.PipKeepClearAlgorithm; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.recents.RecentTasksController; @@ -207,7 +208,8 @@ public class WMShellModule { @Provides static Optional<Pip> providePip(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, - PipBoundsState pipBoundsState, PipMediaController pipMediaController, + PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState, + PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController, WindowManagerShellWrapper windowManagerShellWrapper, @@ -215,9 +217,11 @@ public class WMShellModule { Optional<OneHandedController> oneHandedController, @ShellMainThread ShellExecutor mainExecutor) { return Optional.ofNullable(PipController.create(context, displayController, - pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController, - phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController, - windowManagerShellWrapper, taskStackListener, oneHandedController, mainExecutor)); + pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, + pipMotionHelper, + pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTouchHandler, + pipTransitionController, windowManagerShellWrapper, taskStackListener, + oneHandedController, mainExecutor)); } @WMSingleton @@ -234,6 +238,12 @@ public class WMShellModule { @WMSingleton @Provides + static PipKeepClearAlgorithm providePipKeepClearAlgorithm() { + return new PipKeepClearAlgorithm(); + } + + @WMSingleton + @Provides static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context, PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) { return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 623ef05ec7e2..175a2445f28d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -107,7 +107,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb private PipAppOpsListener mAppOpsListener; private PipMediaController mMediaController; private PipBoundsAlgorithm mPipBoundsAlgorithm; + private PipKeepClearAlgorithm mPipKeepClearAlgorithm; private PipBoundsState mPipBoundsState; + private PipMotionHelper mPipMotionHelper; private PipTouchHandler mTouchHandler; private PipTransitionController mPipTransitionController; private TaskStackListenerImpl mTaskStackListener; @@ -241,6 +243,10 @@ public class PipController implements PipTransitionController.PipTransitionCallb Set<Rect> unrestricted) { if (mPipBoundsState.getDisplayId() == displayId) { mPipBoundsState.setKeepClearAreas(restricted, unrestricted); + mPipMotionHelper.moveToBounds(mPipKeepClearAlgorithm.adjust( + mPipBoundsState.getBounds(), + mPipBoundsState.getRestrictedKeepClearAreas(), + mPipBoundsState.getUnrestrictedKeepClearAreas())); } } }; @@ -293,7 +299,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb @Nullable public static Pip create(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, - PipBoundsState pipBoundsState, PipMediaController pipMediaController, + PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState, + PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController, WindowManagerShellWrapper windowManagerShellWrapper, @@ -307,9 +314,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb } return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, - pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer, - pipTouchHandler, pipTransitionController, windowManagerShellWrapper, - taskStackListener, oneHandedController, mainExecutor) + pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController, + phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController, + windowManagerShellWrapper, taskStackListener, oneHandedController, mainExecutor) .mImpl; } @@ -317,7 +324,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, + PipKeepClearAlgorithm pipKeepClearAlgorithm, @NonNull PipBoundsState pipBoundsState, + PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, @@ -339,7 +348,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb mWindowManagerShellWrapper = windowManagerShellWrapper; mDisplayController = displayController; mPipBoundsAlgorithm = pipBoundsAlgorithm; + mPipKeepClearAlgorithm = pipKeepClearAlgorithm; mPipBoundsState = pipBoundsState; + mPipMotionHelper = pipMotionHelper; mPipTaskOrganizer = pipTaskOrganizer; mMainExecutor = mainExecutor; mMediaController = pipMediaController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java new file mode 100644 index 000000000000..a83258f9063b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip.phone; + +import android.graphics.Rect; + +import java.util.Set; + +/** + * Calculates the adjusted position that does not occlude keep clear areas. + */ +public class PipKeepClearAlgorithm { + + /** Returns a new {@code Rect} that does not occlude the provided keep clear areas. */ + public Rect adjust(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas, + Set<Rect> unrestrictedKeepClearAreas) { + if (restrictedKeepClearAreas.isEmpty()) { + return defaultBounds; + } + // TODO(b/183746978): implement the adjustment algorithm + // naively check if areas intersect, an if so move PiP upwards + Rect outBounds = new Rect(defaultBounds); + for (Rect r : restrictedKeepClearAreas) { + if (r.intersect(outBounds)) { + outBounds.offset(0, r.top - outBounds.bottom); + } + } + return outBounds; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt index 9ede4433a978..239ea38e1e9c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt @@ -363,48 +363,54 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { private fun getNearbyStashedPosition(bounds: Rect, keepClearAreas: Set<Rect>): Rect { val screenBounds = transformedScreenBounds - val stashCandidates = Array(2) { Rect(bounds) } + val stashCandidates = mutableListOf<Rect>() val areasOverlappingPipX = keepClearAreas.filter { it.intersectsX(bounds) } val areasOverlappingPipY = keepClearAreas.filter { it.intersectsY(bounds) } if (screenBounds.bottom - bounds.bottom <= bounds.top - screenBounds.top) { - // bottom is closer than top, stash downwards val fullStashTop = screenBounds.bottom - stashOffset val maxBottom = areasOverlappingPipX.maxByOrNull { it.bottom }!!.bottom val partialStashTop = maxBottom + pipAreaPadding - val downPosition = stashCandidates[0] + val downPosition = Rect(bounds) downPosition.offsetTo(bounds.left, min(fullStashTop, partialStashTop)) - } else { - // top is closer than bottom, stash upwards - val fullStashY = screenBounds.top - bounds.height() + stashOffset + stashCandidates += downPosition + } + if (screenBounds.bottom - bounds.bottom >= bounds.top - screenBounds.top) { + val fullStashBottom = screenBounds.top - bounds.height() + stashOffset val minTop = areasOverlappingPipX.minByOrNull { it.top }!!.top - val partialStashY = minTop - bounds.height() - pipAreaPadding + val partialStashBottom = minTop - bounds.height() - pipAreaPadding - val upPosition = stashCandidates[0] - upPosition.offsetTo(bounds.left, max(fullStashY, partialStashY)) + val upPosition = Rect(bounds) + upPosition.offsetTo(bounds.left, max(fullStashBottom, partialStashBottom)) + stashCandidates += upPosition } if (screenBounds.right - bounds.right <= bounds.left - screenBounds.left) { - // right is closer than left, stash rightwards - val fullStashLeft = screenBounds.right - stashOffset + val fullStashRight = screenBounds.right - stashOffset val maxRight = areasOverlappingPipY.maxByOrNull { it.right }!!.right - val partialStashLeft = maxRight + pipAreaPadding + val partialStashRight = maxRight + pipAreaPadding - val rightPosition = stashCandidates[1] - rightPosition.offsetTo(min(fullStashLeft, partialStashLeft), bounds.top) - } else { - // left is closer than right, stash leftwards + val rightPosition = Rect(bounds) + rightPosition.offsetTo(min(fullStashRight, partialStashRight), bounds.top) + stashCandidates += rightPosition + } + if (screenBounds.right - bounds.right >= bounds.left - screenBounds.left) { val fullStashLeft = screenBounds.left - bounds.width() + stashOffset val minLeft = areasOverlappingPipY.minByOrNull { it.left }!!.left val partialStashLeft = minLeft - bounds.width() - pipAreaPadding - val rightPosition = stashCandidates[1] - rightPosition.offsetTo(max(fullStashLeft, partialStashLeft), bounds.top) + val leftPosition = Rect(bounds) + leftPosition.offsetTo(max(fullStashLeft, partialStashLeft), bounds.top) + stashCandidates += leftPosition + } + + if (stashCandidates.isEmpty()) { + return bounds } return stashCandidates.minByOrNull { 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 f20870ff0b2d..aec51baa4af7 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 @@ -377,7 +377,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return; } - mStageCoordinator.updateSurfaceBounds(null /* layout */, t); + mStageCoordinator.updateSurfaceBounds(null /* layout */, t, + false /* applyResizingOffset */); for (int i = 0; i < apps.length; ++i) { if (apps[i].mode == MODE_OPENING) { t.show(apps[i].leash); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index e150cf9cd112..45931de5e35e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -30,6 +30,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; +import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -495,7 +496,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Using legacy transitions, so we can't use blast sync since it conflicts. mTaskOrganizer.applyTransaction(wct); - mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t)); + mSyncQueue.runInSync(t -> + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */)); } /** @@ -704,9 +706,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainStage.deactivate(wct, !fromEnteringPip && mMainStage == childrenToTop); wct.reorder(mRootTaskInfo.token, false /* onTop */); mTaskOrganizer.applyTransaction(wct); - mSyncQueue.runInSync(t -> t - .setWindowCrop(mMainStage.mRootLeash, null) - .setWindowCrop(mSideStage.mRootLeash, null)); + mSyncQueue.runInSync(t -> { + setResizingSplits(false /* resizing */); + t.setWindowCrop(mMainStage.mRootLeash, null) + .setWindowCrop(mSideStage.mRootLeash, null); + }); // Hide divider and reset its position. mSplitLayout.resetDividerPosition(); @@ -780,7 +784,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, void finishEnterSplitScreen(SurfaceControl.Transaction t) { mSplitLayout.init(); setDividerVisibility(true, t); - updateSurfaceBounds(mSplitLayout, t); + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); setSplitsVisible(true); mShouldUpdateRecents = true; updateRecentTasksSplitPair(); @@ -925,7 +929,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mRootTaskInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */); + mDisplayImeController, mTaskOrganizer, + PARALLAX_ALIGN_CENTER /* parallaxType */); mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); } @@ -1075,7 +1080,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareEnterSplitScreen(wct); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { - updateSurfaceBounds(mSplitLayout, t); + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); setDividerVisibility(true, t); }); } @@ -1094,8 +1099,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onSnappedToDismiss(boolean bottomOrRight) { - setResizingSplits(false /* resizing */); - final boolean mainStageToTop = bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; @@ -1104,6 +1107,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } + setResizingSplits(false /* resizing */); final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(dismissTop, wct); @@ -1121,14 +1125,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onLayoutPositionChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */)); } @Override public void onLayoutSizeChanging(SplitLayout layout) { mSyncQueue.runInSync(t -> { setResizingSplits(true /* resizing */); - updateSurfaceBounds(layout, t); + updateSurfaceBounds(layout, t, true /* applyResizingOffset */); mMainStage.onResizing(getMainStageBounds(), t); mSideStage.onResizing(getSideStageBounds(), t); }); @@ -1142,7 +1146,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { setResizingSplits(false /* resizing */); - updateSurfaceBounds(layout, t); + updateSurfaceBounds(layout, t, false /* applyResizingOffset */); mMainStage.onResized(t); mSideStage.onResized(t); }); @@ -1174,13 +1178,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); } - void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) { + void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, + boolean applyResizingOffset) { final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash, - bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer); + bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer, + applyResizingOffset); } void setResizingSplits(boolean resizing) { @@ -1220,7 +1226,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTaskOrganizer.applyTransaction(wct); } - @Override public void onDisplayAdded(int displayId) { if (displayId != DEFAULT_DISPLAY) { return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java index f1520edf53b1..07174051a344 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java @@ -253,7 +253,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t) { - mStageCoordinator.updateSurfaceBounds(null /* layout */, t); + mStageCoordinator.updateSurfaceBounds(null /* layout */, t, + false /* applyResizingOffset */); if (apps != null) { for (int i = 0; i < apps.length; ++i) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java index 6ef20e37d5bc..ac25c7510931 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java @@ -265,7 +265,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainStage.activate(getMainStageBounds(), wct); mSideStage.addTask(task, getSideStageBounds(), wct); mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> updateSurfaceBounds(null /* layout */, t)); + mSyncQueue.runInSync( + t -> updateSurfaceBounds(null /* layout */, t, false /* applyResizingOffset */)); return true; } @@ -801,12 +802,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onLayoutPositionChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */)); } @Override public void onLayoutSizeChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */)); mSideStage.setOutlineVisibility(false); } @@ -816,7 +817,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, updateWindowBounds(layout, wct); updateUnfoldBounds(); mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */)); mSideStage.setOutlineVisibility(true); mLogger.logResize(mSplitLayout.getDividerPositionAsFraction()); } @@ -840,13 +841,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); } - void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) { + void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, + boolean applyResizingOffset) { final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash, - bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer); + bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer, + applyResizingOffset); } @Override @@ -882,7 +885,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mDisplayAreaInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer, true /* applyDismissingParallax */); + mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_DISMISSING); mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); if (mMainUnfoldController != null && mSideUnfoldController != null) { diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS index a244d14b0e14..f4efc374ecc2 100644 --- a/libs/WindowManager/Shell/tests/OWNERS +++ b/libs/WindowManager/Shell/tests/OWNERS @@ -1,4 +1,4 @@ -# Bug component: 909476 +# Bug component: 1157642 # includes OWNERS from parent directories natanieljr@google.com pablogamito@google.com diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt index fb404b913465..684e5cad0e67 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt @@ -17,6 +17,8 @@ package com.android.wm.shell.flicker.bubble import android.platform.test.annotations.Presubmit +import android.view.WindowInsets +import android.view.WindowManager import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By @@ -59,6 +61,16 @@ class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScr } } transitions { + // Swipe & wait for the notification shade to expand so all can be seen + val wm = context.getSystemService(WindowManager::class.java) + val metricInsets = wm.getCurrentWindowMetrics().windowInsets + val insets = metricInsets.getInsetsIgnoringVisibility( + WindowInsets.Type.statusBars() + or WindowInsets.Type.displayCutout()) + device.swipe(100, insets.top + 100, 100, device.getDisplayHeight() / 2, 4) + device.waitForIdle(2000) + instrumentation.uiAutomation.syncInputTransactions() + val notification = device.wait(Until.findObject( By.text("BubbleChat")), FIND_OBJECT_TIMEOUT) notification?.click() ?: error("Notification not found") diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java index d743dffd3c9e..6cd93eff2803 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java @@ -22,7 +22,6 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Person; -import android.app.RemoteInput; import android.content.Context; import android.content.Intent; import android.graphics.Point; @@ -116,24 +115,20 @@ public class BubbleHelper { private Notification.Builder getNotificationBuilder(int id) { Person chatBot = new Person.Builder() .setBot(true) - .setName("BubbleBot") + .setName("BubbleChat") .setImportant(true) .build(); - - RemoteInput remoteInput = new RemoteInput.Builder("key") - .setLabel("Reply") - .build(); - String shortcutId = "BubbleChat"; return new Notification.Builder(mContext, CHANNEL_ID) .setChannelId(CHANNEL_ID) .setShortcutId(shortcutId) + .setContentTitle("BubbleChat") .setContentIntent(PendingIntent.getActivity(mContext, 0, new Intent(mContext, LaunchBubbleActivity.class), PendingIntent.FLAG_UPDATE_CURRENT)) .setStyle(new Notification.MessagingStyle(chatBot) - .setConversationTitle("Bubble Chat") - .addMessage("Hello? This is bubble: " + id, + .setConversationTitle("BubbleChat") + .addMessage("BubbleChat", SystemClock.currentThreadTimeMillis() - 300000, chatBot) .addMessage("Is it me, " + id + ", you're looking for?", SystemClock.currentThreadTimeMillis(), chatBot) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 8b4e1f8bfdb7..f1e602fcf778 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -73,7 +73,7 @@ public class SplitLayoutTests extends ShellTestCase { mCallbacks, mDisplayImeController, mTaskOrganizer, - false /* applyDismissingParallax */)); + SplitLayout.PARALLAX_NONE)); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index aef298ed478a..deb955b30842 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -74,6 +74,7 @@ public class PipControllerTest extends ShellTestCase { @Mock private PhonePipMenuController mMockPhonePipMenuController; @Mock private PipAppOpsListener mMockPipAppOpsListener; @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; + @Mock private PipKeepClearAlgorithm mMockPipKeepClearAlgorithm; @Mock private PipSnapAlgorithm mMockPipSnapAlgorithm; @Mock private PipMediaController mMockPipMediaController; @Mock private PipTaskOrganizer mMockPipTaskOrganizer; @@ -97,9 +98,10 @@ public class PipControllerTest extends ShellTestCase { return null; }).when(mMockExecutor).execute(any()); mPipController = new PipController(mContext, mMockDisplayController, - mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState, - mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, - mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper, + mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm, + mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController, + mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler, + mMockPipTransitionController, mMockWindowManagerShellWrapper, mMockTaskStackListener, mMockOneHandedController, mMockExecutor); when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm); when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper); @@ -128,9 +130,10 @@ public class PipControllerTest extends ShellTestCase { when(spyContext.getPackageManager()).thenReturn(mockPackageManager); assertNull(PipController.create(spyContext, mMockDisplayController, - mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState, - mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, - mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper, + mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm, + mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController, + mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler, + mMockPipTransitionController, mMockWindowManagerShellWrapper, mMockTaskStackListener, mMockOneHandedController, mMockExecutor)); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java new file mode 100644 index 000000000000..f657b5e62d82 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip.phone; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Set; + +/** + * Unit tests against {@link PipKeepClearAlgorithm}. + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class PipKeepClearAlgorithmTest extends ShellTestCase { + + private PipKeepClearAlgorithm mPipKeepClearAlgorithm; + + + @Before + public void setUp() throws Exception { + mPipKeepClearAlgorithm = new PipKeepClearAlgorithm(); + } + + @Test + public void adjust_withCollidingRestrictedKeepClearAreas_movesBounds() { + final Rect inBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(50, 50, 150, 150); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect), + Set.of()); + + assertFalse(outBounds.contains(keepClearRect)); + } + + @Test + public void adjust_withNonCollidingRestrictedKeepClearAreas_boundsDoNotChange() { + final Rect inBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(100, 100, 150, 150); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect), + Set.of()); + + assertEquals(inBounds, outBounds); + } + + @Test + public void adjust_withCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() { + // TODO(b/183746978): update this test to accommodate for the updated algorithm + final Rect inBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(50, 50, 150, 150); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(), + Set.of(keepClearRect)); + + assertEquals(inBounds, outBounds); + } + + @Test + public void adjust_withNonCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() { + final Rect inBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(100, 100, 150, 150); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(), + Set.of(keepClearRect)); + + assertEquals(inBounds, outBounds); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 061136c65daf..c571d44d3da9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -309,7 +309,7 @@ public class StageCoordinatorTests extends ShellTestCase { public void testFinishEnterSplitScreen_applySurfaceLayout() { mStageCoordinator.finishEnterSplitScreen(new SurfaceControl.Transaction()); - verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any()); + verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any(), eq(false)); } private class UnfoldControllerProvider implements diff --git a/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml index 5b12fc586ef9..0eba119d58ee 100644 --- a/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml +++ b/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="help_feedback_label" msgid="7106780063063027882">"المساعدة والملاحظات والآراء"</string> + <string name="help_feedback_label" msgid="7106780063063027882">"المساعدة والملاحظات"</string> </resources> diff --git a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml index fc3ec4344712..fd45a16f24ba 100644 --- a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml @@ -17,7 +17,8 @@ <resources> <style name="SettingsSpinnerTitleBar"> - <item name="android:textAppearance">?android:attr/textAppearanceButton</item> + <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item> + <item name="android:textSize">16sp</item> <item name="android:textColor">@color/settingslib_spinner_title_color</item> <item name="android:maxLines">1</item> <item name="android:ellipsize">marquee</item> @@ -29,7 +30,8 @@ </style> <style name="SettingsSpinnerDropdown"> - <item name="android:textAppearance">?android:attr/textAppearanceButton</item> + <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item> + <item name="android:textSize">16sp</item> <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> <item name="android:maxLines">1</item> <item name="android:ellipsize">marquee</item> diff --git a/packages/SettingsLib/res/layout/user_preference.xml b/packages/SettingsLib/res/layout/user_preference.xml deleted file mode 100644 index f13447a4737c..000000000000 --- a/packages/SettingsLib/res/layout/user_preference.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@android:id/widget_frame" - android:layout_width="match_parent" - android:layout_height="@dimen/user_spinner_item_height" - android:paddingStart="@dimen/user_spinner_padding_sides" - android:paddingEnd="@dimen/user_spinner_padding_sides" - android:orientation="horizontal" > - - <ImageView - android:id="@android:id/icon" - android:layout_width="@dimen/user_icon_view_height" - android:layout_height="@dimen/user_icon_view_height" - android:layout_gravity="center" - android:scaleType="fitCenter" /> - - <TextView - android:id="@android:id/title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:layout_gravity="center" - android:labelFor="@android:id/icon" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - android:paddingStart="@dimen/user_spinner_padding" - android:paddingEnd="@dimen/user_spinner_padding" - android:textAppearance="?android:attr/textAppearanceMedium" /> - -</LinearLayout> diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 0a3178a9a42a..cb9a7bfd6794 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Kon nie \'n nuwe gebruiker skep nie"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Kon nie ’n nuwe gas skep nie"</string> <string name="user_nickname" msgid="262624187455825083">"Bynaam"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Voeg gebruiker by"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Stel gassessie terug"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Stel gassessie terug?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Verwyder gas?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Stel terug"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Verwyder"</string> <string name="guest_resetting" msgid="7822120170191509566">"Stel tans gassessie terug …"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Kies foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Te veel verkeerde pogings. Hierdie toestel se data sal uitgevee word."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Te veel verkeerde pogings. Hierdie gebruiker sal uitgevee word."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Te veel verkeerde pogings. Hierdie werkprofiel en die data daarin sal uitgevee word."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Maak toe"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Toestelverstek"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Gedeaktiveer"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Skakel skerm aan"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Laat toe dat die skerm aangeskakel word"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Laat ’n program toe om die skerm aan te skakel. As jy toestemming gee, kan die program die skerm enige tyd sonder jou uitdruklike bedoeling aanskakel."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skandeer QR-kode"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Plaas die QR-kode hieronder in die middel om te begin luister"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR-kode is nie ’n geldige formaat nie"</string> </resources> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index a55cb4f7c628..bcd69e673b39 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"አዲስ ተጠቃሚን መፍጠር አልተሳካም"</string> <string name="add_guest_failed" msgid="8074548434469843443">"አዲስ እንግዳ መፍጠር አልተሳካም"</string> <string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"ተጠቃሚን አክል"</string> <string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"እንግዳን ዳግም አስጀምር"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"እንግዳ ዳግም ይጀምር?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"እንግዳ ይወገድ?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ዳግም አስጀምር"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"አስወግድ"</string> <string name="guest_resetting" msgid="7822120170191509566">"እንግዳን ዳግም በማስጀመር ላይ…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ፎቶ ይምረጡ"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"በጣም ብዙ ትክክል ያልሆኑ ሙከራዎች። የዚህ መሣሪያ ውሂብ ይሰረዛል።"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"በጣም ብዙ ትክክል ያልሆኑ ሙከራዎች። ይህ ተጠቃሚ ይሰረዛል።"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"በጣም ብዙ ትክክል ያልሆኑ ሙከራዎች። የዚህ የሥራ መገለጫ እና ውሂቡ ይሰረዛሉ።"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"አሰናብት"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"የመሣሪያ ነባሪ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ተሰናክሏል"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"ማያ ገጽን ያብሩ"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"ማያ ገጹን ማብራት ይፍቀዱ"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"አንድ መተግበሪያ ማያ ገጹን እንዲያበራ ይፍቀዱለት። ከተሰጠ፣ መተግበሪያው ያለእርስዎ ግልጽ ሐሳብ በማንኛውም ጊዜ ማያ ገጹን ሊያበራ ይችላል።"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR ኮድን ይቃኙ"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"ማዳመጥ ለመጀመር ከታች ያለውን QR ኮድ መሃል ላይ ያድርጉት"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR ኮድ ልክ ያልኾነ ቅርጸት ነው"</string> </resources> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 56e88cf0dddd..f28d5b393831 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -549,7 +549,7 @@ <string name="media_transfer_this_phone" msgid="7194341457812151531">"هذا الهاتف"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string> - <string name="help_label" msgid="3528360748637781274">"المساعدة والملاحظات والآراء"</string> + <string name="help_label" msgid="3528360748637781274">"المساعدة والملاحظات"</string> <string name="storage_category" msgid="2287342585424631813">"مساحة التخزين"</string> <string name="shared_data_title" msgid="1017034836800864953">"البيانات المشتركة"</string> <string name="shared_data_summary" msgid="5516326713822885652">"عرض البيانات المشتركة وتعديلها"</string> @@ -589,27 +589,23 @@ <string name="add_user_failed" msgid="4809887794313944872">"تعذّر إنشاء مستخدم جديد."</string> <string name="add_guest_failed" msgid="8074548434469843443">"تعذّر إنشاء جلسة ضيف جديدة."</string> <string name="user_nickname" msgid="262624187455825083">"اللقب"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"إضافة حساب مستخدم"</string> <string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"إعادة ضبط جلسة الضيف"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"هل تريد إعادة ضبط جلسة الضيف؟"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"هل تريد إزالة حساب الضيف؟"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"إعادة الضبط"</string> + <!-- no translation found for guest_remove_guest_confirm_button (7858123434954143879) --> + <skip /> <string name="guest_resetting" msgid="7822120170191509566">"جارٍ إعادة ضبط جلسة الضيف…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"اختيار صورة"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"لقد استنفدت عدد المحاولات غير الصحيحة وسيتم حذف بيانات هذا الجهاز."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"لقد استنفدت عدد المحاولات غير الصحيحة وسيتم حذف حساب هذا المستخدم."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"لقد استنفدت عدد المحاولات غير الصحيحة وسيتم حذف الملف الشخصي للعمل وبياناته."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"إغلاق"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"الإعداد التلقائي للجهاز"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غير مفعّل"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string> @@ -657,4 +653,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"تشغيل الشاشة"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"السماح بتشغيل الشاشة"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"يمكنك السماح لأحد التطبيقات بتشغيل الشاشة. في حال منحت هذا الإذن، قد يشغِّل التطبيق الشاشة في أي وقت بدون إذن صريح منك."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"إجراء مسح ضوئي لرمز الاستجابة السريعة"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"لبدء الاستماع، امسَح ضوئيًا رمز الاستجابة السريعة التالي."</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"تنسيق رمز الاستجابة السريعة غير صالح."</string> </resources> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index bb8fb28856d9..5c465ee48745 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"অতিথিৰ ছেশ্বন ৰিছেট কৰিবনে?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"অতিথি আঁতৰাবনে?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ৰিছেট কৰক"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"আঁতৰাওক"</string> <string name="guest_resetting" msgid="7822120170191509566">"অতিথিৰ ছেশ্বন ৰিছেট কৰি থকা হৈছে…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"স্ক্ৰীন অন কৰক"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"স্ক্ৰীনখন অন কৰিবলৈ অনুমতি দিয়ক"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"এপ্টোক স্ক্ৰীনখন অন কৰিবলৈ অনুমতি দিয়ক। যদি অনুমতি দিয়া হয়, এপ্টোৱে আপোনাৰ স্পষ্ট উদ্দেশ্য অবিহনেও যিকোনো সময়তে স্ক্ৰীনখন অন কৰিব পাৰে।"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"কিউআৰ ক’ডটো স্কেন কৰক"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"শুনিবলৈ আৰম্ভ কৰিবলৈ, তলৰ মাজৰ অংশত কিউআৰ ক’ডটো ৰাখক"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"কিউআৰ ক’ডটো মান্য ফৰ্মেটৰ নহয়"</string> </resources> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index ece3956fbe4d..50c8593c38ae 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Qonaq məlumatı sıfırlansın?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Qonaq silinsin?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Sıfırlayın"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Silin"</string> <string name="guest_resetting" msgid="7822120170191509566">"Qonaq məlumatı sıfırlanır…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ekranı aktiv etmək"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Ekranı aktiv etməyə icazə verin"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Tətbiqin ekranı aktiv etməsinə icazə verin. İcazə verilərsə, tətbiq istənilən vaxt sizə soruşmadan ekranı aktiv edə bilər."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR kodu skanlayın"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Dinləməyə başlamaq üçün aşağıda QR kodu mərkəzə yerləşdirin"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR kodu doğru formatda deyil"</string> </resources> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index e7c281397e2d..1d7bf9a084ec 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -448,7 +448,7 @@ <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (crveno-zeleno)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (plavo-žuto)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekcija boja"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Korekcija boja može da bude korisna kada želite:to:<br/> <ol> <li>&nbsp;da preciznije vidite boje</li> <li>&nbsp;da uklonite boje kako biste se fokusirali</li> </ol>"</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Korekcija boja može da bude korisna kada želite:<br/> <ol> <li>&nbsp;Preciznije da vidite boje</li> <li>&nbsp;Da uklonite boje kako biste se fokusirali</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Zamenjuje ga <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>–<xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Preostalo je oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Želite li da resetujete sesiju gosta?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Želite li da uklonite gosta?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetuj"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string> <string name="guest_resetting" msgid="7822120170191509566">"Sesija gosta se resetuje…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Uključite ekran"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Dozvoli uključivanje ekrana"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Dozvoljava aplikaciji da uključi ekran. Ako se omogući, aplikacija može da uključi ekran u bilo kom trenutku bez vaše eksplicitne namere."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skenirajte QR kôd"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Da biste počeli da slušate, centrirajte QR kôd ispod"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR kôd nije u važećem formatu"</string> </resources> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index ddc155c37d60..6ad50178faa9 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Не ўдалося стварыць новага карыстальніка"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Не ўдалося стварыць новага госця"</string> <string name="user_nickname" msgid="262624187455825083">"Псеўданім"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Дадаць карыстальніка"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Скінуць гасцявы сеанс"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Скінуць гасцявы сеанс?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Выдаліць госця?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Скінуць"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Выдаліць"</string> <string name="guest_resetting" msgid="7822120170191509566">"Ідзе скід гасцявога сеанса…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Выбраць фота"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Занадта шмат няўдалых спроб. Даныя з гэтай прылады будуць выдалены."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Занадта шмат няўдалых спроб. Гэты карыстальнік будзе выдалены."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Занадта шмат няўдалых спроб. Гэты працоўны профіль і звязаныя з ім даныя будуць выдалены."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Закрыць"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандартная прылада"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Выключана"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Уключана"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Уключэнне экрана"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Дазволіць уключэнне экрана"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Дазволіць праграме ўключаць экран. З гэтым дазволам праграма зможа ў любы час уключаць экран без вашага яўнага намеру."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Сканіраваць QR-код"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Каб пачаць праслухванне, памясціце ў цэнтр QR-код, які знаходзіцца ўнізе"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR-код мае несапраўдны фармат"</string> </resources> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 0398c1851da6..2087efda5179 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Неуспешно създаване на нов потребител"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Създаването на нов гост не бе успешно"</string> <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Добавяне на потребител"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Нулиране на сесията като гост"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Да се нулира ли сесията като гост?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Да се премахне ли гостът?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Нулиране"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Премахване"</string> <string name="guest_resetting" msgid="7822120170191509566">"Сесията като гост се нулира…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Избиране на снимката"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Твърде много неправилни опити. Данните от това устройство ще бъдат изтрити."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Твърде много неправилни опити. Този потребител ще бъде изтрит."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Твърде много неправилни опити. Този служебен потребителски профил и данните в него ще бъдат изтрити."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Отхвърляне"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандартна настройка за у-вото"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Деактивирано"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Активирано"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Включване на екрана"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Разрешаване на включването на екрана"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Разрешете на дадено приложение да включва екрана. Ако го направите, то може да включва екрана по всяко време без явното ви намерение."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Сканиране на QR код"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"За да започнете да слушате, центрирайте QR кода по-долу"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Невалиден формат на QR кода"</string> </resources> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index a0cf42bb79cd..d79c0181d14a 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"নতুন ব্যবহারকারী যোগ করা যায়নি"</string> <string name="add_guest_failed" msgid="8074548434469843443">"নতুন অতিথি তৈরি করা যায়নি"</string> <string name="user_nickname" msgid="262624187455825083">"বিশেষ নাম"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"ব্যবহারকারীর অ্যাকাউন্ট যোগ করুন"</string> <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"অতিথি সেশন রিসেট করুন"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"গেস্ট সেশন রিসেট করবেন?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"অতিথিকে সরাবেন?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"রিসেট করুন"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"সরান"</string> <string name="guest_resetting" msgid="7822120170191509566">"গেস্ট সেশন রিসেট করা হচ্ছে..."</string> <string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ফটো বেছে নিন"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"একাধিকবার ভুল ইনপুট দিয়েছেন। এই ডিভাইসের ডেটা মুছে ফেলা হবে।"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"একাধিকবার ভুল ইনপুট দিয়েছেন। এই ব্যবহারকারীর প্রোফাইল মুছে ফেলা হবে।"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"একাধিকবার ভুল ইনপুট দিয়েছেন। এই অফিস প্রোফাইল ও এর ডেটা মুছে ফেলা হবে।"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"বাতিল করুন"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইসের ডিফল্ট"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"বন্ধ করা আছে"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"স্ক্রিন চালু করুন"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"স্ক্রিন চালু করার অনুমতি দিন"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"অ্যাপকে স্ক্রিন চালু করার অনুমতি দিন। অনুমতি দেওয়া হলে, অ্যাপ আপনার এক্সপ্লিসিট ইনটেন্ট ছাড়াই যেকোনও সময় স্ক্রিন চালু করতে পারবে।"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR কোড স্ক্যান করুন"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"শোনা শুরু করতে, নিচের QR কোডটি মাঝখানে রাখুন"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR কোডের ফর্ম্যাট সঠিক নয়"</string> </resources> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index faa7333e7dfd..1381d57659c9 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -589,20 +589,21 @@ <string name="add_user_failed" msgid="4809887794313944872">"Kreiranje novog korisnika nije uspjelo"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Kreiranje novog gosta nije uspjelo"</string> <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> - <string name="user_add_user" msgid="7876449291500212468">"Dodavanje korisnika"</string> + <string name="user_add_user" msgid="7876449291500212468">"Dodaj korisnika"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Poništi sesiju gosta"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Poništiti sesiju gosta?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Ukloniti gosta?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Poništi"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string> <string name="guest_resetting" msgid="7822120170191509566">"Poništavanje sesije gosta…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir fotografije"</string> - <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Previše netočnih pokušaja. S uređaja će se izbrisati podaci."</string> - <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Previše netočnih pokušaja. Ovaj će se korisnik izbrisati."</string> - <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Previše netočnih pokušaja. Ovaj će se radni profil izbrisati zajedno sa svim svojim podacima."</string> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Previše je neispravnih pokušaja. Podaci ovog uređaja će se izbrisati."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Previše je neispravnih pokušaja. Ovaj korisnik će se izbrisati."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Previše je neispravnih pokušaja. Ovaj poslovni profil i njegovi podaci će se izbrisati."</string> <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Odbaci"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Zadana postavka uređaja"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Uključi ekran"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Dozvolite uključivanje ekrana"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Dozvolite aplikaciji da uključi ekran. Ako se odobri, aplikacija može uključiti ekran bilo kada bez vaše izričite namjere."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skenirajte QR kôd"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Da pokrenete slušanje, centrirajte QR kôd ispod"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Format QR koda nije važeći"</string> </resources> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 0c95e18d3c75..627a9d84d197 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -311,7 +311,7 @@ <string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"No s\'ha pogut connectar"</string> <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostra les opcions per a la certificació de pantalla sense fil"</string> <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Augmenta el nivell de registre de la connexió Wi‑Fi i es mostra per SSID RSSI al selector de Wi‑Fi"</string> - <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Redueix el consum de bateria i millora el rendiment de la xarxa"</string> + <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Exhaureix menys la bateria i millora el rendiment de la xarxa"</string> <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Quan aquest mode està activat, és possible que l’adreça MAC d\'aquest dispositiu canviï cada vegada que es connecti a una xarxa amb l\'aleatorització d\'adreces MAC activada."</string> <string name="wifi_metered_label" msgid="8737187690304098638">"D\'ús mesurat"</string> <string name="wifi_unmetered_label" msgid="6174142840934095093">"D\'ús no mesurat"</string> @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"No s\'ha pogut crear l\'usuari"</string> <string name="add_guest_failed" msgid="8074548434469843443">"No s\'ha pogut crear un convidat"</string> <string name="user_nickname" msgid="262624187455825083">"Àlies"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Afegeix un usuari"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Restableix el convidat"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Vols restablir el convidat?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Vols suprimir el convidat?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restableix"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Suprimeix"</string> <string name="guest_resetting" msgid="7822120170191509566">"S\'està restablint el convidat…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecciona una foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Has superat el nombre d\'intents incorrectes permesos. Les dades d\'aquest dispositiu se suprimiran."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Has superat el nombre d\'intents incorrectes permesos. Aquest usuari se suprimirà."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Has superat el nombre d\'intents incorrectes permesos. Aquest perfil de treball i les dades que contingui se suprimiran."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Ignora"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Opció predeter. del dispositiu"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivat"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Activa la pantalla"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permet que activi la pantalla"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permet que una aplicació activi la pantalla. Si concedeixes aquest permís, pot ser que l\'aplicació activi la pantalla en qualsevol moment sense la teva intenció explícita."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Escaneja un codi QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Per començar a escoltar, centra el codi QR més avall"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"El codi QR no té un format vàlid"</string> </resources> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 5780f7deef67..875daa4e1c20 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Nového uživatele se nepodařilo vytvořit"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Vytvoření nového hosta se nezdařilo"</string> <string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Přidat uživatele"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Resetovat hosta"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Resetovat hosta?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Odstranit hosta?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetovat"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstranit"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetování hosta…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrat fotku"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Příliš mnoho neplatných pokusů. Data v tomto zařízení budou smazána."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Příliš mnoho neplatných pokusů. Tento uživatel bude smazán."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Příliš mnoho neplatných pokusů. Tento pracovní profil a přidružená data budou smazána."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Zavřít"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Výchozí nastavení zařízení"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuto"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string> @@ -657,4 +652,10 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Zapínat obrazovku"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Povolit zapínání obrazovky"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Povolte aplikaci zapínat obrazovku. Pokud aplikace bude mít toto oprávnění, může kdykoli zapnout obrazovku bez explicitního intentu."</string> + <!-- no translation found for bt_le_audio_scan_qr_code (3521809854780392679) --> + <skip /> + <!-- no translation found for bt_le_audio_scan_qr_code_scanner (4679500020630341107) --> + <skip /> + <!-- no translation found for bt_le_audio_qr_code_is_not_valid_format (6092191081849434734) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 91009aa24674..776776d6e3d0 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Der kunne ikke oprettes en ny bruger"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Der kunne ikke oprettes en ny gæst"</string> <string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Tilføj bruger"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæst"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Nulstil gæstesession"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Vil du nulstille gæsten?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Vil du fjerne gæsten?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nulstil"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjern"</string> <string name="guest_resetting" msgid="7822120170191509566">"Nulstiller gæst…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Vælg billede"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"For mange forkerte forsøg. Dataene på denne enhed slettes."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"For mange forkerte forsøg. Denne bruger slettes."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"For mange forkerte forsøg. Denne arbejdsprofil og de tilhørende data slettes."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Luk"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhedens standardindstilling"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiveret"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Aktivér skærmen"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Tillad aktivering af skærmen"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Tillad, at en app aktiverer skærmen. Hvis du giver denne tilladelse, kan appen til enhver tid aktiverer skærmen, uden at du eksplicit har bedt om det."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Scan QR-kode"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Centrer QR-koden nedenfor for at gå i gang med at lytte"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR-koden har ikke et gyldigt format"</string> </resources> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 2be9872817c4..3049a695149f 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Gast zurücksetzen?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Gast entfernen?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Zurücksetzen"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Entfernen"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gast wird zurückgesetzt…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Display aktivieren"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Aktivieren des Displays erlauben"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne deine explizite Absicht."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR-Code scannen"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Scanne zum Anhören den QR-Code unten"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Das Format des QR-Codes ist nicht gültig"</string> </resources> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index d0460487abe7..76ee0083ee2a 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -530,7 +530,7 @@ <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Να επιτρέπεται ο ορισμός ξυπνητ. και υπενθυμίσεων"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ξυπνητήρια και υπενθυμίσεις"</string> <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Επιτρέψτε σε αυτήν την εφαρμογή να ορίζει ξυπνητήρια και να προγραμματίζει ενέργειες που εξαρτώνται από τον χρόνο. Αυτό επιτρέπει στην εφαρμογή να εκτελείται στο παρασκήνιο και, ως εκ τούτου, μπορεί να καταναλώνει περισσότερη μπαταρία.\n\nΑν αυτή η άδεια δεν είναι ενεργή, τα υπάρχοντα ξυπνητήρια και συμβάντα βάσει χρόνου που έχουν προγραμματιστεί από αυτήν την εφαρμογή δεν θα λειτουργούν."</string> - <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"χρονοδιάγραμμα, ξυπνητήρι, υπενθύμιση, ρολόι"</string> + <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"πρόγραμμα, ξυπνητήρι, υπενθύμιση, ρολόι"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ενεργοποίηση"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ενεργοποίηση λειτουργίας \"Μην ενοχλείτε\""</string> <string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Ποτέ"</string> @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Η δημιουργία νέου χρήστη απέτυχε"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Αποτυχία δημιουργίας νέου προσκεκλημένου"</string> <string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Προσθήκη χρήστη"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Επαναφορά περιόδου επισκέπτη"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Επαναφορά επισκέπτη;"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Κατάργηση επισκέπτη;"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Επαναφορά"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Κατάργηση"</string> <string name="guest_resetting" msgid="7822120170191509566">"Επαναφορά επισκέπτη…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Επιλογή φωτογραφίας"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Πάρα πολλές ανεπιτυχείς προσπάθειες. Τα δεδομένα αυτής της συσκευής θα διαγραφούν."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Πάρα πολλές ανεπιτυχείς προσπάθειες. Αυτός ο χρήστης θα διαγραφεί."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Πάρα πολλές ανεπιτυχείς προσπάθειες. Αυτό το προφίλ εργασίας και τα δεδομένα του θα διαγραφούν."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Παράβλεψη"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Προεπιλογή συσκευής"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ανενεργή"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ενεργή"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ενεργοποίηση οθόνης"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Να επιτρέπεται η ενεργοποίηση της οθόνης"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Επιτρέψτε σε μια εφαρμογή να ενεργοποιεί την οθόνη. Αν παραχωρήσετε την άδεια, η εφαρμογή ενδέχεται να ενεργοποιεί την οθόνη ανά πάσα στιγμή, χωρίς να εκφράσετε σαφή πρόθεση."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Σάρωση κωδικού QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Για έναρξη της ακρόασης, κεντράρετε τον παρακάτω κωδικό QR"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Ο κωδικός QR δεν έχει έγκυρη μορφή"</string> </resources> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 59f5b835db64..74d8f9afe9e6 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Reset guest?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Remove guest?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Turn screen on"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Allow turning the screen on"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Allow an app to turn the screen on. If granted, the app may turn on the screen at any time without your explicit intent."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Scan QR code"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"To start listening, centre the QR code below"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR code isn\'t a valid format"</string> </resources> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 24bd421e3a8f..63c395e4ba18 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Reset guest?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Remove guest?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Turn screen on"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Allow turning the screen on"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Allow an app to turn the screen on. If granted, the app may turn on the screen at any time without your explicit intent."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Scan QR code"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"To start listening, centre the QR code below"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR code isn\'t a valid format"</string> </resources> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 59f5b835db64..74d8f9afe9e6 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Reset guest?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Remove guest?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Turn screen on"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Allow turning the screen on"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Allow an app to turn the screen on. If granted, the app may turn on the screen at any time without your explicit intent."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Scan QR code"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"To start listening, centre the QR code below"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR code isn\'t a valid format"</string> </resources> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 59f5b835db64..74d8f9afe9e6 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Reset guest?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Remove guest?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Turn screen on"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Allow turning the screen on"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Allow an app to turn the screen on. If granted, the app may turn on the screen at any time without your explicit intent."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Scan QR code"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"To start listening, centre the QR code below"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR code isn\'t a valid format"</string> </resources> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index b5c5a8469c3d..5ad390433e89 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Reset guest?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Remove guest?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Turn screen on"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Allow turning the screen on"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Allow an app to turn the screen on. If granted, the app may turn on the screen at any time without your explicit intent."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Scan QR code"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"To start listening, center the QR code below"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR code isn\'t a valid format"</string> </resources> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 43144e95a200..4f4362e3709c 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"No se pudo crear el usuario nuevo"</string> <string name="add_guest_failed" msgid="8074548434469843443">"No se pudo crear un nuevo invitado"</string> <string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Agregar usuario"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer perfil de invitado"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"¿Quieres restablecer el invitado?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"¿Quieres quitar al invitado?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Restableciendo invitado…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Hubo demasiados intentos incorrectos. Se borrarán los datos de este dispositivo."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Hubo demasiados intentos incorrectos. Se borrará este usuario."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Hubo demasiados intentos incorrectos. Se borrarán este perfil de trabajo y sus datos."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Descartar"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado del dispositivo"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Activar pantalla"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permitir activación de la pantalla"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permite que una app active la pantalla. Si se otorga permiso, la app podría activar la pantalla en cualquier momento sin que lo pidas de manera explícita."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Escanear código QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Para comenzar a escuchar, centra el código QR que aparece a continuación"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"El código QR no tiene un formato válido"</string> </resources> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 508a74b22888..970b6891fcfc 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"No se ha podido crear el usuario"</string> <string name="add_guest_failed" msgid="8074548434469843443">"No se ha podido crear un nuevo invitado"</string> <string name="user_nickname" msgid="262624187455825083">"Apodo"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Añadir usuario"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer invitado"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"¿Restablecer invitado?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"¿Quitar invitado?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Restableciendo invitado…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Ha habido demasiados intentos fallidos. Los datos de este dispositivo se eliminarán."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Ha habido demasiados intentos fallidos. Este usuario se eliminará."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Ha habido demasiados intentos fallidos. Este perfil de trabajo y sus datos se eliminarán."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Cerrar"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado por el dispositivo"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Encender pantalla"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permitir encender la pantalla"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permite que una aplicación encienda la pantalla. Si das este permiso, la aplicación puede encender la pantalla en cualquier momento sin que se lo pidas."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Escanear código QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Para empezar a escuchar, centra el código QR aquí abajo"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"El formato del código QR no es válido"</string> </resources> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 3f1cc1991404..28412038afb7 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Uue kasutaja loomine ebaõnnestus"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Uue külalise loomine ei õnnestunud"</string> <string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Kasutaja lisamine"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Lähtesta külastajaseanss"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Kas lähtestada külastajaseanss?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Kas eemaldada külaline?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Lähtesta"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eemalda"</string> <string name="guest_resetting" msgid="7822120170191509566">"Külastajaseansi lähtestamine …"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Valige foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Liiga palju valesid katseid. Selle seadme andmed kustutatakse."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Liiga palju valesid katseid. See kasutaja kustutatakse."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Liiga palju valesid katseid. See tööprofiil ja selle andmed kustutatakse."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Loobu"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Seadme vaikeseade"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Keelatud"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ekraani sisselülitamine"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Luba ekraani sisselülitamine"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Rakendusel lubatakse ekraan sisse lülitada. Kui annate loa, võib rakendus ekraani igal ajal sisse lülitada ilma teie sõnaselge kavatsuseta."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR-koodi skannimine"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Kuulamise alustamiseks paigutage QR-kood allpool keskele"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR-kood ei ole sobilik vorming"</string> </resources> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index a7041d9802b4..a4ecadfc1ea1 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -109,12 +109,9 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Ezk. gailuaren bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. Esk- gailuaren bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktibo"</string> - <!-- no translation found for bluetooth_hearing_aid_left_active (7084887715570971441) --> - <skip /> - <!-- no translation found for bluetooth_hearing_aid_right_active (8574683234077567230) --> - <skip /> - <!-- no translation found for bluetooth_hearing_aid_left_and_right_active (407704460573163973) --> - <skip /> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktibo, ezkerreko audifonoa soilik"</string> + <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktibo, eskuineko audifonoa soilik"</string> + <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktibo, ezkerreko eta eskuineko audifonoak"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Euskarriaren audioa"</string> <string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefono-deiak"</string> <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fitxategi-transferentzia"</string> @@ -451,7 +448,7 @@ <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanopia (gorri-berdeak)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanopia (urdin-horia)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Koloreen zuzenketa"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Koloreen zuzenketa lagungarria izan daiteke hauek egin nahi dituzunean:<br/> <ol> <li>&nbsp;Koloreak zehaztasun handiagoz ikusi.</li> <li>&nbsp;Koloreak kendu, arreta gal ez dezazun.</li> </ol>"</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Baliteke koloreen zuzenketa lagungarria izatea hauek egin nahi dituzunean:<br/> <ol> <li>&nbsp;Koloreak zehaztasun handiagoz ikusi.</li> <li>&nbsp;Koloreak kendu, arreta gal ez dezazun.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string> @@ -592,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Ezin izan da sortu erabiltzailea"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Ezin izan da sortu beste gonbidatu bat"</string> <string name="user_nickname" msgid="262624187455825083">"Goitizena"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Gehitu erabiltzaile bat"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Berrezarri gonbidatuentzako saioa"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Gonbidatuentzako saioa berrezarri nahi duzu?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Gonbidatua kendu nahi duzu?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Berrezarri"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Kendu"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gonbidatuentzako saioa berrezartzen…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Hautatu argazki bat"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Saiakera oker gehiegi egin dituzu. Gailu honetako datuak ezabatu egingo dira."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Saiakera oker gehiegi egin dituzu. Erabiltzailea ezabatu egingo da."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Saiakera oker gehiegi egin dituzu. Laneko profila eta bertako datuak ezabatu egingo dira."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Baztertu"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gailuaren balio lehenetsia"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desgaituta"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string> @@ -660,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Piztu pantaila"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Eman pantaila pizteko baimena"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Eman pantaila pizteko baimena aplikazioei. Baimena emanez gero, aplikazioek edonoiz piztu ahal izango dute pantaila, zuk halako asmorik izan ez arren."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Eskaneatu QR kodea"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Entzuten hasteko, zentratu beheko QR kodea"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR kodearen formatuak ez du balio"</string> </resources> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 3f19420bb27e..dee837766ee6 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"کاربر جدید ایجاد نشد"</string> <string name="add_guest_failed" msgid="8074548434469843443">"مهمان جدید ایجاد نشد"</string> <string name="user_nickname" msgid="262624187455825083">"نام مستعار"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"افزودن کاربر"</string> <string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"بازنشانی مهمان"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"جلسه مهمان بازنشانی شود؟"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"مهمان برداشته شود؟"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"بازنشانی"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"برداشتن"</string> <string name="guest_resetting" msgid="7822120170191509566">"درحال بازنشانی مهمان…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"انتخاب عکس"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"تلاشهای نادرست بسیار زیادی انجام شده است. دادههای این دستگاه حذف خواهد شد."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"تلاشهای اشتباه بسیار زیادی انجام شده است. این کاربر حذف خواهد شد."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"تلاشهای اشتباه بسیار زیادی انجام شده است. این نمایه کاری و دادههای آن حذف خواهند شد."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"رد شدن"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"پیشفرض دستگاه"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیرفعال"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"روشن کردن صفحهنمایش"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"اعطای اجازه برای روشن کردن صفحهنمایش"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"به برنامه اجازه میدهد صفحهنمایش را روشن کند. اگر اجازه داده شود، ممکن است این برنامه در هر زمانی بدون هدف صریح شما صفحهنمایش را روشن کند."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"اسکن رمزینه پاسخسریع"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"برای گوش دادن، رمزینه پاسخسریع زیر را در مرکز کادر قرار دهید"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"قالب رمزینه پاسخسریع معتبر نیست"</string> </resources> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 55f9c56371ab..37f010952fe6 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Uuden käyttäjän luominen epäonnistui"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Uutta vierasta ei voitu luoda"</string> <string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Lisää käyttäjä"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Nollaa vieras"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Nollataanko vieras?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Poistetaaanko vieras?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nollaa"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Poista"</string> <string name="guest_resetting" msgid="7822120170191509566">"Nollataan vierasta…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Valitse kuva"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Liian monta virheellistä yritystä. Laitteen data poistetaan."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Liian monta virheellistä yritystä. Tämä käyttäjä poistetaan."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Liian monta virheellistä yritystä. Tämä työprofiili ja sen data poistetaan."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Ohita"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Laitteen oletusasetus"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ei käytössä"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Käynnistä näyttö"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Salli näytön käynnistäminen"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Salli sovelluksen käynnistää näyttö. Jos sovellus saa luvan, se voi käynnistää näytön itsenäisesti milloin tahansa."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skannaa QR-koodi"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Aloita kuuntelu keskittämällä alla olevaan QR-koodiin"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR-koodin muoto ei kelpaa"</string> </resources> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 0886acacada9..16922abc6330 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -518,7 +518,7 @@ <string name="ims_reg_title" msgid="8197592958123671062">"État d\'enregistrement IMS"</string> <string name="ims_reg_status_registered" msgid="884916398194885457">"Enregistré"</string> <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Non enregistré"</string> - <string name="status_unavailable" msgid="5279036186589861608">"Non accessible"</string> + <string name="status_unavailable" msgid="5279036186589861608">"Non disponible"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Les adresses MAC sont randomisées"</string> <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{Aucun appareil connecté}=1{1 appareil connecté}one{# appareil connecté}other{# appareils connectés}}"</string> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Plus longtemps."</string> @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Impossible de créer un utilisateur"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Impossible de créer un nouvel invité"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Ajouter un utilisateur"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Réinitialiser la session Invité"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Réinitialiser la session Invité?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Retirer l\'invité?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Réinitialiser"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Retirer"</string> <string name="guest_resetting" msgid="7822120170191509566">"Réinitialisation de la session Invité en cours…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionnez une photo"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Trop de tentatives incorrectes. Les données de cet appareil seront supprimées."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Trop de tentatives incorrectes. Cet utilisateur sera supprimé."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Trop de tentatives incorrectes. Ce profil professionnel sera supprimé, y compris ses données."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Fermer"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Valeur par défaut de l\'appareil"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string> @@ -657,4 +652,10 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Activation de l\'écran"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Autoriser l\'activation de l\'écran"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Autorisez une application à activer l\'écran. Lorsque vous accordez cette autorisation, l\'application peut activer l\'écran à tout moment sans votre volonté explicite."</string> + <!-- no translation found for bt_le_audio_scan_qr_code (3521809854780392679) --> + <skip /> + <!-- no translation found for bt_le_audio_scan_qr_code_scanner (4679500020630341107) --> + <skip /> + <!-- no translation found for bt_le_audio_qr_code_is_not_valid_format (6092191081849434734) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 04db494571fb..71acc6f26a89 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Échec de la création d\'un utilisateur"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Impossible de créer un profil invité"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Ajouter un utilisateur"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Réinitialiser la session Invité"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Réinitialiser la session Invité ?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Supprimer l\'invité ?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Réinitialiser"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Supprimer"</string> <string name="guest_resetting" msgid="7822120170191509566">"Réinitialisation de la session Invité…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionner une photo"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Trop de tentatives incorrectes. Les données de cet appareil vont être supprimées."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Trop de tentatives incorrectes. Ce compte utilisateur va être supprimé."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Trop de tentatives incorrectes. Ce profil professionnel et les données associées vont être supprimés."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Fermer"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Paramètre par défaut"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Activer l\'écran"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Autoriser l\'activation de l\'écran"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Autoriser une appli à activer l\'écran. Si l\'autorisation est accordée, l\'appli peut activer l\'écran à tout moment sans votre intention explicite."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Scanner un code QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Pour commencer à écouter, centrez le code QR ci-dessous"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Le format de code QR n\'est pas valide"</string> </resources> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 2f4f1d763fa3..cf0aacbc8775 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Non se puido crear un novo usuario"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Produciuse un erro ao crear o convidado"</string> <string name="user_nickname" msgid="262624187455825083">"Alcume"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Engadir usuario"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer sesión de convidado"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Queres restablecer a sesión de convidado?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Queres quitar o convidado?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Restablecendo sesión de convidado…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Realizaches demasiados intentos incorrectos. Eliminaranse os datos deste dispositivo."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Realizaches demasiados intentos incorrectos. Eliminarase este usuario."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Realizaches demasiados intentos incorrectos. Eliminaranse este perfil de traballo e os datos asociados."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Ignorar"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Funcionamento predeterminado"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Activar a pantalla"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permitir activación da pantalla"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permite que unha aplicación active a pantalla. Se lle dás permiso, a aplicación poderá activar a pantalla en calquera momento sen que llo pidas."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Escanear código QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Para comezar a escoitar audio, encadra o seguinte código QR"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"O formato do código QR non é válido"</string> </resources> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 5f31d4adeb6c..0d16ebd6fca4 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"અતિથિને રીસેટ કરીએ?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"અતિથિને કાઢી નાખીએ?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"રીસેટ કરો"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"કાઢી નાખો"</string> <string name="guest_resetting" msgid="7822120170191509566">"અતિથિને રીસેટ કરી રહ્યાં છીએ…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"સ્ક્રીન ચાલુ કરો"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"સ્ક્રીન ચાલુ કરવાની મંજૂરી આપો"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ઍપને સ્ક્રીન ચાલુ કરવાની મંજૂરી આપો. જો મંજૂરી આપી હોય, તો ઍપ તમારા સ્પષ્ટ હેતુ વિના કોઈપણ સમયે સ્ક્રીન ચાલુ કરી શકે છે."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR કોડ સ્કૅન કરો"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"સાંભળવાનું શરૂ કરવા માટે, QR કોડને નીચે ફ્રેમની મધ્યમાં લાવો"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"આ QR કોડ માન્ય ફૉર્મેટમાં નથી"</string> </resources> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 214a922bf147..0e347653070a 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"क्या आप मेहमान के तौर पर ब्राउज़ करने का सेशन रीसेट करना चाहते हैं?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"क्या मेहमान को हटाना है?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रीसेट करें"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"हटाएं"</string> <string name="guest_resetting" msgid="7822120170191509566">"मेहमान के तौर पर ब्राउज़ करने का सेशन रीसेट किया जा रहा है…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"स्क्रीन चालू करें"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"स्क्रीन चालू करने की अनुमति दें"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ऐप्लिकेशन को स्क्रीन चालू करने की अनुमति दें. ऐसा करने पर, ऐप्लिकेशन आपकी अनुमति लिए बिना भी, जब चाहे स्क्रीन चालू कर सकता है."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"क्यूआर कोड को स्कैन करें"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"सुनने के लिए, दिए गए क्यूआर कोड को बीच में लाएं"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"क्यूआर कोड का फ़ॉर्मैट गलत है"</string> </resources> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 8ffbd549c446..ff4f2395d102 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Poništiti gostujuću sesiju?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Ukloniti gosta?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Poništi"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string> <string name="guest_resetting" msgid="7822120170191509566">"Poništavanje gostujuće sesije…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Uključivanje zaslona"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Dopusti uključivanje zaslona"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Dopustite aplikaciji da uključuje zaslon. Ako date to dopuštenje, aplikacija može uključiti zaslon u bilo kojem trenutku bez vaše izričite namjere."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skeniraj QR kôd"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Da biste počeli slušati, centrirajte QR kôd u nastavku"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR kôd nije u važećem formatu"</string> </resources> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index f9571b83c333..dc12f69fd71f 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Az új felhasználó létrehozása sikertelen"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Az új vendég létrehozása nem sikerült"</string> <string name="user_nickname" msgid="262624187455825083">"Becenév"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Felhasználó hozzáadása"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Vendég munkamenet visszaállítása"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Visszaállítja a vendég munkamenetet?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Eltávolítja a vendéget?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Visszaállítás"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eltávolítás"</string> <string name="guest_resetting" msgid="7822120170191509566">"Vendég munkamenet visszaállítása…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Fotó kiválasztása"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Túl sok sikertelen próbálkozás. A rendszer törli az adatokat az eszközről."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Túl sok sikertelen próbálkozás. A rendszer törli ezt a felhasználót."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Túl sok sikertelen próbálkozás. A rendszer törli ezt a munkaprofilt és a kapcsolódó adatokat."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Bezárás"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Alapértelmezett"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Letiltva"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Képernyő bekapcsolása"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"A képernyő bekapcsolásának engedélyezése"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"A képernyő bekapcsolásának engedélyezése az adott alkalmazás számára. Ha megadja az engedélyt, az alkalmazás az Ön kifejezett szándéka nélkül, bármikor bekapcsolhatja a képernyőt."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR-kód beolvasása"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"A hallgatás megkezdéséhez igazítsa a QR-kódot az alábbi panel közepére"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"A QR-kód formátuma nem érvényes"</string> </resources> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index cd1b9343cdd0..cc1f2bed5597 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Չհաջողվեց ստեղծել նոր օգտատեր"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Չհաջողվեց նոր հյուր ստեղծել"</string> <string name="user_nickname" msgid="262624187455825083">"Կեղծանուն"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Ավելացնել օգտատեր"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Վերակայել հյուրի աշխատաշրջանը"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Վերակայե՞լ հյուրի աշխատաշրջանը"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Հեռացնե՞լ հյուրին"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Վերակայել"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Հեռացնել"</string> <string name="guest_resetting" msgid="7822120170191509566">"Հյուրի աշխատաշրջանը վերակայվում է…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Ընտրեք լուսանկար"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Չափից շատ սխալ փորձեր են արվել։ Այս սարքի տվյալները կջնջվեն։"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Չափից շատ սխալ փորձեր են արվել։ Oգտատիրոջ պրոֆիլը կջնջվի։"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Չափից շատ սխալ փորձեր են արվել։ Աշխատանքային պրոֆիլը և դրա տվյալները կջնջվեն։"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Փակել"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Կանխադրված տարբերակ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Անջատված է"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Միացված է"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Էկրանի միացում"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Թույլատրել հավելվածին միացնել էկրանը"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Եթե թույլ տաք, հավելվածը ցանկացած ժամանակ կկարողանա միացնել էկրանը՝ առանց ձեր բացահայտ համաձայնության։"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR կոդի սկանավորում"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Լսելու համար տեսախցիկը պահեք QR կոդի վրա"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR կոդի ձևաչափն անվավեր է"</string> </resources> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index ff14ad36056d..8293463cac5c 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Gagal membuat pengguna baru"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Gagal membuat tamu baru"</string> <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Tambahkan pengguna"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Reset tamu"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Reset tamu?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Hapus tamu?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Hapus"</string> <string name="guest_resetting" msgid="7822120170191509566">"Mereset tamu …"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Terlalu banyak percobaan yang salah. Data perangkat ini akan dihapus."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Terlalu banyak percobaan yang salah. Pengguna ini akan dihapus."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Terlalu banyak percobaan yang salah. Profil kerja ini dan datanya akan dihapus."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Tutup"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Default perangkat"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Nonaktif"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Aktifkan layar"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Izinkan pengaktifan layar"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Mengizinkan aplikasi mengaktifkan layar. Jika diizinkan, aplikasi dapat mengaktifkan layar kapan saja tanpa izin Anda."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Pindai kode QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Untuk mulai mendengarkan, fokuskan kode QR berikut"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Format kode QR tidak valid"</string> </resources> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 3634d5f63e3e..30dd9324e880 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Ekki tókst að stofna nýjan notanda"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Ekki tókst að búa til nýjan gest"</string> <string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Bæta notanda við"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Endurstilla gestastillingu"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Endurstilla gestastillingu?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Fjarlægja gest?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Endurstilla"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjarlægja"</string> <string name="guest_resetting" msgid="7822120170191509566">"Endurstillir gest…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Velja mynd"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Of margar rangar tilraunir. Gögnum tækisins verður eytt."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Of margar rangar tilraunir. Þessum notanda verður eytt."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Of margar rangar tilraunir. Þessu vinnusniði og gögnum þess verður eytt."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Hunsa"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Sjálfgefin stilling tækis"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slökkt"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Kveikja á skjánum"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Leyfa að kveikt sé á skjánum"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Leyfa forriti að kveikja á skjánum. Ef þetta er leyft getur forritið kveikt á skjánum hvenær sem er án þess að þú samþykkir það sérstaklega."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skanna QR-kóða"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Byrjaðu að hlusta með því að skanna QR-kóðann hér að neðan"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR-kóði er ekki gilt snið"</string> </resources> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index af308e41074b..92bb253d7fdc 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Creazione nuovo utente non riuscita"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Impossibile creare un nuovo ospite"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Aggiungi utente"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Reimposta sessione Ospite"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Reimpostare sessione Ospite?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Vuoi rimuovere l\'ospite?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reimposta"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Rimuovi"</string> <string name="guest_resetting" msgid="7822120170191509566">"Reimpostazione sessione Ospite in corso…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleziona la foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Troppi tentativi sbagliati. I dati del dispositivo verranno eliminati."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Troppi tentativi sbagliati. Questo utente verrà eliminato."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Troppi tentativi sbagliati. Questo profilo di lavoro e i relativi dati verranno eliminati."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Ignora"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parametro predefinito"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Non attivo"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Attiva lo schermo"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Consenti l\'attivazione dello schermo"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Consenti a un\'app di attivare lo schermo. Se la autorizzi, l\'app può attivare lo schermo in qualsiasi momento senza la tua autorizzazione esplicita."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Scansiona codice QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Per iniziare ad ascoltare, centra il codice QR qui sotto"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Il formato del codice QR non è valido"</string> </resources> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 93b1c97b0703..86b283c34360 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -109,12 +109,9 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"טעינת הסוללה: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"שמאל: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> סוללה, ימין: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> סוללה"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"פעיל"</string> - <!-- no translation found for bluetooth_hearing_aid_left_active (7084887715570971441) --> - <skip /> - <!-- no translation found for bluetooth_hearing_aid_right_active (8574683234077567230) --> - <skip /> - <!-- no translation found for bluetooth_hearing_aid_left_and_right_active (407704460573163973) --> - <skip /> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"פועל: שמאל בלבד"</string> + <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"פועל: ימין בלבד"</string> + <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"פועל: ימין ושמאל"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"אודיו של מדיה"</string> <string name="bluetooth_profile_headset" msgid="5395952236133499331">"שיחות טלפון"</string> <string name="bluetooth_profile_opp" msgid="6692618568149493430">"העברת קבצים"</string> @@ -592,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"לא ניתן היה ליצור משתמש חדש"</string> <string name="add_guest_failed" msgid="8074548434469843443">"יצירת אורח חדש נכשלה"</string> <string name="user_nickname" msgid="262624187455825083">"כינוי"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"הוספת משתמש"</string> <string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח/ת"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"איפוס הגלישה כאורח"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"לאפס את הגלישה כאורח?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"להסיר את האורח/ת?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"איפוס"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"הסרה"</string> <string name="guest_resetting" msgid="7822120170191509566">"מתבצע איפוס של הגלישה כאורח…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"בחירת תמונה"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"נעשו יותר מדי ניסיונות שגויים. הנתונים במכשיר יימחקו."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"נעשו יותר מדי ניסיונות שגויים. המשתמש הזה יימחק."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"נעשו יותר מדי ניסיונות שגויים. פרופיל העבודה והנתונים שמשויכים אליו יימחקו."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"סגירה"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ברירת המחדל של המכשיר"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"מושבת"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string> @@ -660,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"הפעלת המסך"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"הרשאה להפעלת המסך"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"הרשאה לאפליקציה להפעיל את המסך. אם מעניקים את ההרשאה, האפליקציה יכולה להפעיל את המסך בכל זמן, גם בלי שמתכוונים לכך במפורש."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"סריקת קוד QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"כדי להתחיל בהאזנה, צריך להציב את קוד ה‑QR במרכז החלון שבהמשך"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"הפורמט של קוד ה‑QR לא תקין"</string> </resources> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index c84a3d900992..82432e8dbfcf 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"新しいユーザーを作成できませんでした"</string> <string name="add_guest_failed" msgid="8074548434469843443">"新しいゲストを作成できませんでした"</string> <string name="user_nickname" msgid="262624187455825083">"ニックネーム"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"ユーザーを追加"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"ゲストをリセット"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"ゲストをリセットしますか?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"ゲストを削除しますか?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"リセット"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"削除"</string> <string name="guest_resetting" msgid="7822120170191509566">"ゲストをリセットしています…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"写真を選択"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"間違えた回数が上限を超えました。このデバイスのデータが削除されます。"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"間違えた回数が上限を超えました。このユーザーが削除されます。"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"間違えた回数が上限を超えました。この仕事用プロファイルと関連データが削除されます。"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"閉じる"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"デバイスのデフォルト"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"無効"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"画面をオンにする"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"画面をオンにすることを許可する"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"画面をオンにすることをアプリに許可します。許可すると、ユーザーからの明示的インテントを必要とせずに、アプリがいつでも画面をオンにできるようになります。"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR コードをスキャン"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"再生を開始するには、下の枠に QR コードを合わせてください"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR コードの形式が無効です"</string> </resources> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 5400adc8776e..bd7c513ad9d4 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"ახალი მომხმარებლის შექმნა ვერ მოხერხდა"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ახალი სტუმრის შექმნა ვერ მოხერხდა"</string> <string name="user_nickname" msgid="262624187455825083">"მეტსახელი"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"მომხმარებლის დამატება"</string> <string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"სტუმრის სესიის გადაყენება"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"გადაყენდეს სტუმარი?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"გსურთ სტუმრის ამოშლა?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"გადაყენება"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ამოშლა"</string> <string name="guest_resetting" msgid="7822120170191509566">"მიმდინარეობს სტუმრის გადაყენება…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ფოტოს გადაღება"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"აირჩიეთ სურათი"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ფოტოს არჩევა"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"დაფიქსირდა ზედმეტად ბევრი არასწორი მცდელობა. შედეგად, ამ მოწყობილობის მონაცემები წაიშლება."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"დაფიქსირდა ზედმეტად ბევრი არასწორი მცდელობა. შედეგად, ეს მომხმარებელი წაიშლება."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"დაფიქსირდა ზედმეტად ბევრი არასწორი მცდელობა. შედეგად, სამსახურის ეს პროფილი და მისი მონაცემები წაიშლება."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"უარყოფა"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"მოწყობილობის ნაგულისხმევი"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"გათიშული"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ჩართული"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"ჩართეთ ეკრანი"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"ეკრანის ჩართვის დაშვება"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"დართეთ ნება აპს, ჩართოს ეკრანი. თუ ამ ნებართვას მიანიჭებთ, აპმა შეიძლება ნებისმიერ დროს ჩართოს ეკრანი თქვენი ცალსახა განზრახვის გარეშე."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR კოდის სკანირება"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"მოსმენის დასაწყებად ცენტრში მოაქციეთ ქვემოთ მოცემული QR კოდი"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR კოდის ფორმატი არასწორია"</string> </resources> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 8160f70ea994..2e71d517f201 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Жаңа пайдаланушы жасалмады."</string> <string name="add_guest_failed" msgid="8074548434469843443">"Жаңа қонақ профилі жасалмады."</string> <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Пайдаланушы қосу"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Қонақ қосу"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты жою"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Қонақ сеансын әдепкі күйге қайтару"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Қонақ сеансы бастапқы күйге қайтарылсын ба?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Қонақты өшіру керек пе?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Бастапқы күйге қайтару"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Өшіру"</string> <string name="guest_resetting" msgid="7822120170191509566">"Қонақ сеансы бастапқы күйге қайтарылуда…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Фотосурет таңдау"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Тым көп қате әрекет жасалды. Бұл құрылғының деректері жойылады."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Тым көп қате әрекет жасалды. Бұл пайдаланушы жойылады."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Тым көп қате әрекет жасалды. Бұл жұмыс профилі мен оның деректері жойылады."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Жабу"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Құрылғының әдепкі параметрлері"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өшірулі"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Қосулы"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Экранды қосу"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Экранды қосуға рұқсат беру"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Қолданбаның экранды қосуына рұқсат береді. Рұқсат берілсе, қолданба кез келген уақытта экранды өздігінен қосуы мүмкін."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR кодын сканерлеу"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Тыңдай бастау үшін төмендегі QR кодын ортаға орналастырыңыз."</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR кодының форматы жарамсыз."</string> </resources> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 0208265ade61..821813b1a13f 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"មិនអាចបង្កើតអ្នកប្រើប្រាស់ថ្មីបានទេ"</string> <string name="add_guest_failed" msgid="8074548434469843443">"មិនអាចបង្កើតភ្ញៀវថ្មីបានទេ"</string> <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះហៅក្រៅ"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"បញ្ចូលអ្នកប្រើប្រាស់"</string> <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូលភ្ញៀវ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ដកភ្ញៀវចេញ"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"កំណត់ភ្ញៀវឡើងវិញ"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"កំណត់ភ្ញៀវឡើងវិញឬ?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"ដកភ្ញៀវចេញឬ?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"កំណត់ឡើងវិញ"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ដកចេញ"</string> <string name="guest_resetting" msgid="7822120170191509566">"កំពុងកំណត់ភ្ញៀវឡើងវិញ…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើសរូបភាព"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ជ្រើសរើសរូបថត"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"ដោយសារមានការព្យាយាមដោះសោមិនត្រឹមត្រូវច្រើនដងពេក ទិន្នន័យរបស់ឧបករណ៍នេះនឹងត្រូវបានលុប។"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"ដោយសារមានការព្យាយាមដោះសោមិនត្រឹមត្រូវច្រើនដងពេក អ្នកប្រើប្រាស់នេះនឹងត្រូវបានលុប។"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"ដោយសារមានការព្យាយាមដោះសោមិនត្រឹមត្រូវច្រើនដងពេក កម្រងព័ត៌មានការងារនេះ និងទិន្នន័យរបស់វានឹងត្រូវបានលុប។"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"ច្រានចោល"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"លំនាំដើមរបស់ឧបករណ៍"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"បានបិទ"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"បើកអេក្រង់"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"អនុញ្ញាតឱ្យបើកអេក្រង់"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"អនុញ្ញាតឱ្យកម្មវិធីបើកអេក្រង់។ ប្រសិនបើអនុញ្ញាត កម្មវិធីអាចបើកអេក្រង់បានគ្រប់ពេល ទោះបីជាអ្នកគ្មានបំណងធ្វើអន្តរកម្មក៏ដោយ។"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"ស្កេនកូដ QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"ដើម្បីចាប់ផ្ដើមស្ដាប់ សូមដាក់កូដ QR ខាងក្រោមឱ្យនៅចំកណ្ដាល"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"កូដ QR មិនមែនជាទម្រង់ដែលត្រឹមត្រូវទេ"</string> </resources> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 0284bc131b09..7beba07407a8 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -232,7 +232,7 @@ <string name="vpn_settings_not_available" msgid="2894137119965668920">"VPN ಸೆಟ್ಟಿಂಗ್ಗಳು ಈ ಬಳಕೆದಾರರಿಗೆ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="tethering_settings_not_available" msgid="266821736434699780">"ಟೆಥರಿಂಗ್ ಸೆಟ್ಟಿಂಗ್ಗಳು ಈ ಬಳಕೆದಾರರಿಗೆ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="apn_settings_not_available" msgid="1147111671403342300">"ಪ್ರವೇಶ ಬಿಂದು ಹೆಸರಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು ಈ ಬಳಕೆದಾರರಿಗೆ ಲಭ್ಯವಿಲ್ಲ"</string> - <string name="enable_adb" msgid="8072776357237289039">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆ"</string> + <string name="enable_adb" msgid="8072776357237289039">"USB ಡೀಬಗಿಂಗ್"</string> <string name="enable_adb_summary" msgid="3711526030096574316">"USB ಸಂಪರ್ಕಗೊಂಡಾಗ ಡೀಬಗ್ ಮೋಡ್"</string> <string name="clear_adb_keys" msgid="3010148733140369917">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯ ಅಧಿಕೃತಗೊಳಿಸುವಿಕೆಗಳನ್ನು ಹಿಂತೆಗೆದುಕೊಳ್ಳಿ"</string> <string name="enable_adb_wireless" msgid="6973226350963971018">"ವೈರ್ಲೆಸ್ ಡೀಬಗಿಂಗ್"</string> @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"ಅತಿಥಿ ಬಳಕೆದಾರರನ್ನು ರೀಸೆಟ್ ಮಾಡಬೇಕೆ?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಬೇಕೇ?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ರೀಸೆಟ್ ಮಾಡಿ"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ತೆಗೆದುಹಾಕಿ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ಅತಿಥಿ ಬಳಕೆದಾರರ ಸೆಟ್ಟಿಂಗ್ ಅನ್ನು ರೀಸೆಟ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಆನ್ ಮಾಡಿ"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಆನ್ ಮಾಡಲು ಅನುಮತಿಸಿ"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಆನ್ ಮಾಡಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಿ. ಅನುಮತಿಸಿದರೆ, ನಿಮಗೆ ಅಗತ್ಯವಿಲ್ಲದಿದ್ದಾಗಲೂ ಆ್ಯಪ್ ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಆನ್ ಮಾಡಬಹುದು."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR ಕೋಡ್ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"ಆಲಿಸುವುದಕ್ಕೆ ಪ್ರಾರಂಭಿಸಲು, ಕ್ಯಾಮರಾವನ್ನು ಕೆಳಗಿನ QR ಕೋಡ್ ಮೇಲೆ ಕೇಂದ್ರೀಕರಿಸಿ"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR ಕೋಡ್ ಮಾನ್ಯ ಫಾರ್ಮ್ಯಾಟ್ನಲ್ಲಿಲ್ಲ"</string> </resources> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index c16902312712..c4841996f554 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"새 사용자를 만들지 못함"</string> <string name="add_guest_failed" msgid="8074548434469843443">"새 게스트 생성 실패"</string> <string name="user_nickname" msgid="262624187455825083">"닉네임"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"사용자 추가"</string> <string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"게스트 재설정"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"게스트를 재설정하시겠습니까?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"게스트를 삭제하시겠습니까?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"재설정"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"삭제"</string> <string name="guest_resetting" msgid="7822120170191509566">"게스트 재설정 중…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"사진 선택"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"잘못된 시도 횟수가 너무 많습니다. 이 기기의 데이터가 삭제됩니다."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"잘못된 시도 횟수가 너무 많습니다. 이 사용자가 삭제됩니다."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"잘못된 시도 횟수가 너무 많습니다. 이 직장 프로필 및 관련 데이터가 삭제됩니다."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"닫기"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"기기 기본값"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"사용 중지됨"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"사용 설정됨"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"화면 켜기"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"화면 켜기 허용"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"앱에서 화면을 켜도록 허용합니다. 권한이 부여된 경우 앱에서 명시적 인텐트 없이 언제든지 화면을 켤 수 있습니다."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR 코드 스캔"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"아래의 QR 코드가 스캐너 가운데에 오도록 맞춘 다음 듣기를 시작하세요"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR 코드의 형식이 유효하지 않습니다."</string> </resources> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 3f0844ca581b..944ba8d5a450 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Жаңы колдонуучу түзүлбөй калды"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Жаңы конок түзүлгөн жок"</string> <string name="user_nickname" msgid="262624187455825083">"Ылакап аты"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Колдонуучу кошуу"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Конок сеансын баштапкы абалга келтирүү"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Конок сеансын баштапкы абалга келтиресизби?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Конокту өчүрөсүзбү?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Баштапкы абалга келтирүү"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Өчүрүү"</string> <string name="guest_resetting" msgid="7822120170191509566">"Конок сеансы баштапкы абалга келтирилүүдө…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Сүрөт тандаңыз"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Өтө көп жолу туура эмес аракет кылынды. Бул түзмөктүн дайындары жок кылынат."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Өтө көп жолу жаңылдыңыз. Бул колдонуучу өчүрүлөт."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Өтө көп жолу жаңылдыңыз. Бул жумуш профили жана андагы нерселер өчүрүлөт."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Жабуу"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Түзмөктүн демейки параметри"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өчүк"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Экранды күйгүзүү"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Экранды күйгүзүүгө уруксат берүү"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Колдонмого экранды күйгүзүүгө уруксат бериңиз. Уруксат берилсе, колдонмо экранды каалаган убакта сизден уруксат сурабастан күйгүзүшү мүмкүн."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR кодун скандоо"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Угуп баштоо үчүн QR кодун борборго жайгаштырыңыз"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR кодунун форматы жараксыз"</string> </resources> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index af682e0ed331..bd125e712148 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"ສ້າງຜູ້ໃຊ້ໃໝ່ບໍ່ສຳເລັດ"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ສ້າງແຂກໃໝ່ບໍ່ສຳເລັດ"</string> <string name="user_nickname" msgid="262624187455825083">"ຊື່ຫຼິ້ນ"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"ເພີ່ມຜູ້ໃຊ້"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"ຣີເຊັດແຂກ"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"ຣີເຊັດແຂກບໍ?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"ລຶບແຂກອອກບໍ?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ຣີເຊັດ"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ລຶບອອກ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ກຳລັງຣີເຊັດແຂກ…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ເລືອກຮູບ"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"ພະຍາຍາມປົດລັອກບໍ່ສຳເລັດຫຼາຍເທື່ອເກີນໄປ. ຂໍ້ມູນຂອງອຸປະກອນນີ້ຈະຖືກລຶບອອກ."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"ພະຍາຍາມປົດລັອກບໍ່ສຳເລັດຫຼາຍເທື່ອເກີນໄປ. ຜູ້ໃຊ້ນີ້ຈະຖືກລຶບຂໍ້ມູນອອກ."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"ພະຍາຍາມປົດລັອກບໍ່ສຳເລັດຫຼາຍເທື່ອເກີນໄປ. ໂປຣໄຟລ໌ບ່ອນເຣັດວຽກ ແລະ ຂໍ້ມູນຂອງມັນຈະຖືກລຶບອອກ."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"ປິດໄວ້"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ຄ່າເລີ່ມຕົ້ນອຸປະກອນ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ປິດການນຳໃຊ້ແລ້ວ"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"ເປີດໜ້າຈໍ"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"ອະນຸຍາດໃຫ້ເປີດໜ້າຈໍ"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ອະນຸຍາດໃຫ້ແອັບເປີດໜ້າຈໍໄດ້. ຫາກອະນຸມັດ, ແອັບຈະສາມາດເປີດໜ້າຈໍຕອນໃດກໍໄດ້ໂດຍທີ່ທ່ານບໍ່ຕ້ອງມີເຈດຕະນາຢ່າງຈະແຈ້ງ."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"ສະແກນລະຫັດ QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"ເພື່ອເລີ່ມການຟັງ, ໃຫ້ວາງລະຫັດ QR ທາງລຸ່ມນີ້ໄວ້ທາງກາງ"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"ຮູບແບບລະຫັດ QR ບໍ່ຖືກຕ້ອງ"</string> </resources> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index b4f2e6ec847f..bf099dca96b0 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Nepavyko sukurti naujo naudotojo"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Nepavyko sukurti naujo gesto"</string> <string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Pridėti naudotoją"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Iš naujo nustatyti svečią"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Nustatyti svečią iš naujo?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Pašalinti svečią?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nustatyti iš naujo"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Pašalinti"</string> <string name="guest_resetting" msgid="7822120170191509566">"Svečias nustatomas iš naujo…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Pasirinkti nuotrauką"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Per daug netinkamų bandymų. Šio įrenginio duomenys bus ištrinti."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Per daug netinkamų bandymų. Šis naudotojas bus ištrintas."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Per daug netinkamų bandymų. Šis darbo profilis ir jo duomenys bus ištrinti."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Atsisakyti"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Numatyt. įrenginio nustatymas"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Išjungta"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ekrano įjungimas"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Leisti įjungti ekraną"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Leiskite programai įjungti ekraną. Jei suteiksite leidimą, programa galės įjungti ekraną bet kuriuo metu be nurodyto tikslo."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR kodo nuskaitymas"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Jei norite pradėti klausyti, nustatykite toliau pateiktą QR kodą per vidurį"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR kodas netinkamo formato"</string> </resources> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 9153b93c3bc8..8d500ddf2267 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Neizdevās izveidot jaunu lietotāju"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Neizdevās izveidot jaunu viesa profilu"</string> <string name="user_nickname" msgid="262624187455825083">"Segvārds"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Pievienot lietotāju"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Atiestatīt viesa sesiju"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Vai atiestatīt viesa sesiju?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Vai noņemt viesi?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Atiestatīt"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Noņemt"</string> <string name="guest_resetting" msgid="7822120170191509566">"Notiek viesa sesijas atiestatīšana…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Atlasīt fotoattēlu"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Pārāk daudz nesekmīgu mēģinājumu. Dati šajā ierīcē tiks dzēsti."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Pārāk daudz nesekmīgu mēģinājumu. Šis lietotājs tiks dzēsts."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Pārāk daudz nesekmīgu mēģinājumu. Šis darba profils un ar to saistītie dati tiks dzēsti."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Nerādīt"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Ierīces noklusējums"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Atspējots"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ekrāna ieslēgšana"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Atļaut ieslēgt ekrānu"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Atļaujiet lietotnei ieslēgt ekrānu. Ja to atļausiet, lietotne varēs jebkurā laikā ieslēgt ekrānu bez tiešas jūsu piekrišanas."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Kvadrātkoda skenēšana"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Lai sāktu klausīties, centrējiet tālāk norādīto kvadrātkodu."</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Kvadrātkoda formāts nav derīgs."</string> </resources> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 22d724ac869b..dbde6913d8da 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Да се ресетира гостинот?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Да се отстрани гостинот?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ресетирај"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Отстрани"</string> <string name="guest_resetting" msgid="7822120170191509566">"Се ресетира гостинот…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string> @@ -651,4 +652,10 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Вклучување на екранот"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Дозволи вклучување на екранот"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Дозволете апликација да го вклучи екранот. Ако дозволите, апликацијата може да го вклучи екранот во секое време без ваша намера."</string> + <!-- no translation found for bt_le_audio_scan_qr_code (3521809854780392679) --> + <skip /> + <!-- no translation found for bt_le_audio_scan_qr_code_scanner (4679500020630341107) --> + <skip /> + <!-- no translation found for bt_le_audio_qr_code_is_not_valid_format (6092191081849434734) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index b8a0fc845222..9519f0d55b78 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -447,7 +447,7 @@ <string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"വർണ്ണാന്ധത (ചുവപ്പ്-പച്ച)"</string> <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"പ്രോട്ടാനോമലി (ചുവപ്പ്-പച്ച)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ട്രിട്ടാനോമലി (നീല-മഞ്ഞ)"</string> - <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"നിറം ക്രമീകരിക്കൽ"</string> + <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"നിറം ശരിയാക്കൽ"</string> <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"ഇനിപ്പറയുന്ന കാര്യങ്ങൾ ചെയ്യാൻ ആഗ്രഹിക്കുമ്പോൾ നിറം ശരിയാക്കൽ സഹായകരമാകും:<br/> <ol> <li>&nbsp;നിറങ്ങൾ കൂടുതൽ കൃത്യമായി കാണാൻ</li> <li>&nbsp;ഫോക്കസ് ചെയ്യാൻ നിങ്ങളെ സഹായിക്കുന്നതിന് നിറങ്ങൾ നീക്കം ചെയ്യാൻ</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"അതിഥിയെ റീസെറ്റ് ചെയ്യണോ?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"അതിഥിയെ നീക്കം ചെയ്യണോ?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"റീസെറ്റ് ചെയ്യുക"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"നീക്കം ചെയ്യുക"</string> <string name="guest_resetting" msgid="7822120170191509566">"അതിഥിയെ റീസെറ്റ് ചെയ്യുന്നു…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"സ്ക്രീൻ ഓണാക്കുക"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"സ്ക്രീൻ ഓണാക്കാൻ അനുവദിക്കുക"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"സ്ക്രീൻ ഓണാക്കാൻ ആപ്പിനെ അനുവദിക്കുക. അനുവദിക്കുകയാണെങ്കിൽ, നിങ്ങളുടെ താൽപ്പര്യം കൂടാതെ ഏതുസമയത്തും സ്ക്രീൻ ഓണാക്കാൻ ആപ്പിന് കഴിയും."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR കോഡ് സ്കാൻ ചെയ്യുക"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"കേട്ട് തുടങ്ങാൻ ചുവടെയുള്ള QR കോഡിലേക്ക് കേന്ദ്രീകരിക്കുക"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR കോഡ് ഫോർമാറ്റ് അസാധുവാണ്"</string> </resources> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 74556959ef70..77bcbc72c113 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Зочныг шинэчлэх үү?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Зочныг хасах уу?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Шинэчлэх"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Хасах"</string> <string name="guest_resetting" msgid="7822120170191509566">"Зочныг шинэчилж байна…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Дэлгэцийг асаах"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Дэлгэцийг асаахыг зөвшөөрнө үү"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Аппад дэлгэцийг асаахыг зөвшөөрнө үү. Зөвшөөрсөн тохиолдолд тухайн апп таны тодорхой оролцоогүйгээр ямар ч үед дэлгэцийг асааж болно."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR код скан хийх"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Сонсож эхлэхийн тулд доорх QR кодыг голлуулаарай"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR код буруу форматтай байна"</string> </resources> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 1f4334304ff0..262064764e56 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"नवीन वापरकर्ता तयार करता आला नाही"</string> <string name="add_guest_failed" msgid="8074548434469843443">"नवीन अतिथी तयार करता आला नाही"</string> <string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"वापरकर्ता जोडा"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"अतिथी सेशन रीसेट करा"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"अतिथीला रीसेट करायचे आहे का?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"अतिथीला काढून टाकायचे आहे का?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रीसेट करा"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"काढून टाका"</string> <string name="guest_resetting" msgid="7822120170191509566">"अतिथीला रीसेट करत आहे…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो निवडा"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"बरेच चुकीचे प्रयत्न. या डिव्हाइसचा डेटा हटवला जाईल."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"बरेच चुकीचे प्रयत्न. हा वापरकर्ता हटवला जाईल."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"बरेच चुकीचे प्रयत्न. ही कार्य प्रोफाइल आणि त्यामधील डेटा हटवला जाईल."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"डिसमिस करा"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिव्हाइस डीफॉल्ट"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद केले आहे"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सुरू केले आहे"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"स्क्रीन सुरू करा"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"स्क्रीन सुरू करण्यासाठी अनुमती द्या"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"स्क्रीन सुरू करण्यासाठी ॲपला अनुमती द्या. अनुमती दिल्यास, ॲप तुमच्या स्पष्ट हेतूशिवाय कधीही स्क्रीन सुरू करू शकते."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR कोड स्कॅन करा"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"ऐकणे सुरू करण्यासाठी, खालील QR कोड मध्यभागी ठेवा"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR कोडचा फॉरमॅट चुकीचा आहे"</string> </resources> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 49a8c209d854..09d7fef89e1c 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Tetapkan semula tetamu?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Alih keluar tetamu?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tetapkan semula"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Alih keluar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Menetapkan semula tetamu…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Hidupkan skrin"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Benarkan menghidupkan skrin"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Benarkan apl menghidupkan skrin. Jika dibenarkan, apl boleh menghidupkan skrin pada bila-bila masa tanpa niat eksplisit anda."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Imbas kod QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Untuk mula mendengar, pusatkan kod QR di bawah"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Kod QR bukan dalam format yang sah"</string> </resources> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 010ec69f99b5..755b46833c16 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -225,7 +225,7 @@ <string name="choose_profile" msgid="343803890897657450">"ပရိုဖိုင်ကို ရွေးရန်"</string> <string name="category_personal" msgid="6236798763159385225">"ကိုယ်ရေး"</string> <string name="category_work" msgid="4014193632325996115">"အလုပ်"</string> - <string name="development_settings_title" msgid="140296922921597393">"ဆော့ဖ်ဝဲရေးသူအတွက် ရွေးစရာများ"</string> + <string name="development_settings_title" msgid="140296922921597393">"ဆော့ဝဲလ်ရေးသူ ရွေးစရာများ"</string> <string name="development_settings_enable" msgid="4285094651288242183">"ဆော့ဖ်ဝဲရေးသူအတွက် ရွေးစရာများကို ဖွင့်ပါ"</string> <string name="development_settings_summary" msgid="8718917813868735095">"အပလီကေးရှင်းတိုးတက်မှုအတွက် ရွေးချယ်မှုကိုသတ်မှတ်သည်"</string> <string name="development_settings_not_available" msgid="355070198089140951">"ဤအသုံးပြုသူအတွက် ဆော့ဖ်ဝဲရေးသူ ရွေးစရာများ မရနိုင်ပါ"</string> @@ -265,7 +265,7 @@ <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"ချွတ်ယွင်းမှု အစီရင်ခံရန် ဖြတ်လမ်း"</string> <string name="bugreport_in_power_summary" msgid="1885529649381831775">"ချွတ်ယွင်းမှု အစီရင်ခံစာကို တင်ရန် ပါဝါမီနူးမှ ခလုတ်ကို ပြပါ"</string> - <string name="keep_screen_on" msgid="1187161672348797558">"ဖွင့်လျက်သား"</string> + <string name="keep_screen_on" msgid="1187161672348797558">"ဖွင့်ထားခြင်း"</string> <string name="keep_screen_on_summary" msgid="1510731514101925829">"အားသွင်းနေချိန် ဖန်သားပြင် ပိတ်သွားမည် မဟုတ်ပါ"</string> <string name="bt_hci_snoop_log" msgid="7291287955649081448">"ဘလူးတုသ် HCI snoop မှတ်တမ်းကို ဖွင့်ခြင်း"</string> <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"ဘလူးတုသ် အတွဲများ သိမ်းယူပါ။ (ဤဆက်တင်ကို ပြောင်းပြီးသည့်အခါ ဘလူးတုသ် ဖွင့်/ပိတ် လုပ်ပါ)"</string> @@ -311,7 +311,7 @@ <string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"ချိတ်ဆက်၍ မရပါ"</string> <string name="wifi_display_certification_summary" msgid="8111151348106907513">"ကြိုးမဲ့ အခင်းအကျင်း အသိအမှတ်ပြုလက်မှတ်အတွက် ရွေးချယ်စရာများပြရန်"</string> <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi မှတ်တမ်းတင်ခြင်း နှုန်းအားမြင့်ကာ၊ Wi‑Fi ရွေးရာတွင် SSID RSSI ဖြင့်ပြပါ"</string> - <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ဘက်ထရီ အသုံးပြုမှုကို လျှော့ကျစေပြီး ကွန်ရက်စွမ်းဆောင်ရည်ကို ပိုမိုကောင်းမွန်စေသည်"</string> + <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ဘက်ထရီအသုံးပြုမှု လျှော့ချပြီး ကွန်ရက်စွမ်းဆောင်ရည် ပိုမိုကောင်းမွန်သည်"</string> <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ဤမုဒ်ကို ဖွင့်ထားသည့်အခါ MAC ကျပန်းပြုလုပ်ထားသည့် ကွန်ရက်သို့ ချိတ်ဆက်လိုက်သည့်အခါတိုင်း ဤစက်၏ MAC လိပ်စာ ပြောင်းသွားနိုင်သည်။"</string> <string name="wifi_metered_label" msgid="8737187690304098638">"အခမဲ့ မဟုတ်ပါ"</string> <string name="wifi_unmetered_label" msgid="6174142840934095093">"အခမဲ့"</string> @@ -377,7 +377,7 @@ <string name="enable_opengl_traces_title" msgid="4638773318659125196">"OpenGL ခြေရာခံခြင်းဖွင့်ပါ။"</string> <string name="usb_audio_disable_routing" msgid="3367656923544254975">"USB အသံလမ်းကြောင်း ပိတ်ခြင်း"</string> <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"USB အသံစက်ပစ္စည်းများသို့ အလိုအလျောက် ချိတ်ဆက်ခြင်းကို ပိတ်ရန်"</string> - <string name="debug_layout" msgid="1659216803043339741">"ဖွဲ့စည်းပုံဘောင်များ ပြခြင်း"</string> + <string name="debug_layout" msgid="1659216803043339741">"ပြကွက်၏ဘောင်များ ပြခြင်း"</string> <string name="debug_layout_summary" msgid="8825829038287321978">"ဖြတ်ပိုင်းအနားသတ်များ၊ အနားများ စသဖြင့် ပြပါ။"</string> <string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"RTL အပြင်အဆင်အတိုင်း ဖြစ်စေခြင်း"</string> <string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"ဘာသာစကားအားလုံးအတွက် RTL အပြင်အဆင်အတိုင်း ဖြစ်စေသည်"</string> @@ -406,7 +406,7 @@ <string name="force_allow_on_external_summary" msgid="8525425782530728238">"တိကျစွာ သတ်မှတ်ထားသည့်တန်ဖိုးများရှိသော်လည်း၊ ပြင်ပသိုလှောင်ခန်းများသို့ မည်သည့်အက်ပ်ကိုမဆို ဝင်ရောက်ခွင့်ပြုပါ"</string> <string name="force_resizable_activities" msgid="7143612144399959606">"လုပ်ဆောင်ချက်များ အရွယ်ပြောင်းနိုင်ခြင်း"</string> <string name="force_resizable_activities_summary" msgid="2490382056981583062">"သတ်မှတ်တန်ဖိုး မည်သို့ပင်ရှိစေ ဝင်းဒိုးများ၏ လုပ်ဆောင်မှုအားလုံးကို အရွယ်အစားပြင်သည်။"</string> - <string name="enable_freeform_support" msgid="7599125687603914253">"အခမဲ့ပုံစံ ဝင်းဒိုးကို ဖွင့်ပါ"</string> + <string name="enable_freeform_support" msgid="7599125687603914253">"ရွှေ့နိုင်ပြင်နိုင်သော ဝင်းဒိုးများ ဖွင့်ရန်"</string> <string name="enable_freeform_support_summary" msgid="1822862728719276331">"ပုံစံမျိုးစုံဝင်းဒိုးများ စမ်းသပ်မှုအတွက် အထောက်အပံ့ကို ဖွင့်ပါ"</string> <string name="local_backup_password_title" msgid="4631017948933578709">"ဒက်စ်တော့ အရန်စကားဝှက်"</string> <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ဒက်စ်တော့ အရန်သိမ်းဆည်းခြင်းအားလုံးကို လောလောဆယ် ကာကွယ်မထားပါ"</string> @@ -448,7 +448,8 @@ <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (အနီ-အစိမ်း)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (အပြာ-အဝါ)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"အရောင်ပြင်ဆင်မှု"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"အရောင် အမှန်ပြင်ခြင်းသည် အောက်ပါတို့အတွက် အသုံးဝင်နိုင်သည်-<br/> <ol> <li>&nbsp;အရောင်များကို ပိုမိုမှန်ကန်စွာ ကြည့်ရှုခြင်း</li> <li>နှင့်nbsp;အာရုံစိုက်နိုင်ရန် အရောင်များ ဖယ်ရှားခြင်း</li> </ol>"</string> + <!-- syntax error in translation for accessibility_display_daltonizer_preference_subtitle (1522101114585266455) org.xmlpull.v1.XmlPullParserException: expected: /string read: li (position:END_TAG </li>@1:326 in <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"အရောင် အမှန်ပြင်ခြင်းသည် အောက်ပါတို့အတွက် အသုံးဝင်နိုင်သည်-<br/> <ol> <li>&nbsp;အရောင်များကို ပိုမိုမှန်ကန်စွာ ကြည့်ရှုခြင်း</li> <li>&nbsp;အာရုံစိုက်နိုင်ရန် အရောင်များ ဖယ်ရှားခြင်း</li> </ol>"</string> +) --> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည်"</string> @@ -589,27 +590,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"အသုံးပြုသူအသစ် ပြုလုပ်၍မရပါ"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ဧည့်သည်သစ် ပြုလုပ်၍မရပါ"</string> <string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"အသုံးပြုသူ ထည့်ရန်"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်ရန်"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်မလား။"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"ဧည့်သည်ကို ဖယ်ရှားမလား။"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ပြင်ဆင်သတ်မှတ်ရန်"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ဖယ်ရှားရန်"</string> <string name="guest_resetting" msgid="7822120170191509566">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်နေသည်…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ဓာတ်ပုံရွေးရန်"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"မှားယွင်းသည့် အကြိမ်ရေ အလွန်များနေပါပြီ။ ဤစက်၏ ဒေတာကို ဖျက်လိုက်ပါမည်။"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"မှားယွင်းသည့် အကြိမ်ရေ အလွန်များနေပါပြီ။ ဤအသုံးပြုသူကို ဖျက်လိုက်ပါမည်။"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"မှားယွင်းသည့် အကြိမ်ရေ အလွန်များနေပါပြီ။ ဤအလုပ်ပရိုဖိုင်နှင့် ၎င်း၏ဒေတာကို ဖျက်လိုက်ပါမည်။"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"ပယ်ရန်"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"စက်ပစ္စည်းမူရင်း"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ပိတ်ထားသည်"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ဖွင့်ထားသည်"</string> @@ -657,4 +653,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"ဖန်သားပြင် ဖွင့်ခြင်း"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"ဖန်သားပြင် ဖွင့်ခွင့်ပြုရန်"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"အက်ပ်ကို ဖန်သားပြင် ဖွင့်ခွင့်ပြုနိုင်သည်။ ခွင့်ပြုထားပါက အက်ပ်သည် သင့်ထံမှ တိကျသောရည်ရွယ်ချက်မလိုဘဲ ဖန်သားပြင်ကို အချိန်မရွေး ဖွင့်နိုင်မည်။"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR ကုဒ်ကို စကင်ဖတ်ရန်"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"စတင်နားဆင်ရန် အောက်ရှိ QR ကုဒ်ကို အလယ်တွင်ထားပါ"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR ကုဒ်သည် မှန်ကန်သောဖော်မက် မဟုတ်ပါ"</string> </resources> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 7abf82b357b7..cc7bcf8891ff 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Kunne ikke opprette noen ny bruker"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Kunne ikke opprette en ny gjest"</string> <string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Legg til bruker"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Tilbakestill gjest"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Vil du tilbakestille gjesten?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Vil du fjerne gjesten?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tilbakestill"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjern"</string> <string name="guest_resetting" msgid="7822120170191509566">"Tilbakestiller gjesten …"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Velg et bilde"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"For mange mislykkede forsøk. Dataene på denne enheten blir slettet."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"For mange mislykkede forsøk. Denne brukeren blir slettet."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"For mange mislykkede forsøk. Denne jobbprofilen og tilknyttede data blir slettet."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Lukk"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Standard for enheten"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slått av"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Slå på skjermen"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Tillat å slå på skjermen"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Tillat at en app slår på skjermen. Hvis tillatelsen gis, kan appen slå på skjermen når som helst uten din eksplisitte intensjon."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skann QR-koden"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"For å begynne å lytte, midtstill QR-koden nedenfor"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR-koden er ikke i et gyldig format"</string> </resources> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index f2e156b64a2a..b88d6b7b7c03 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -510,7 +510,7 @@ <string name="retail_demo_reset_message" msgid="5392824901108195463">"डेमो मोडमा फ्याक्ट्री रिसेट गर्न पासवर्ड प्रविष्टि गर्नुहोस्"</string> <string name="retail_demo_reset_next" msgid="3688129033843885362">"अर्को"</string> <string name="retail_demo_reset_title" msgid="1866911701095959800">"पासवर्ड आवश्यक छ"</string> - <string name="active_input_method_subtypes" msgid="4232680535471633046">"आगत विधिहरू सक्रिय गर्नुहोस्"</string> + <string name="active_input_method_subtypes" msgid="4232680535471633046">"आगत विधिहरू अन गर्नुहोस्"</string> <string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"सिष्टममा भएका भाषा प्रयोग गरियोस्"</string> <string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>का लागि सेटिङहरू खोल्न विफल भयो।"</string> <string name="ime_security_warning" msgid="6547562217880551450">"यस इनपुट विधिले तपाईँले टाइप गर्नुहुने सम्पूर्ण पाठ बटु्ल्न सक्छ, व्यक्तिगत डेटा जस्तै पासवर्ड र क्रेडिट कार्ड नम्बर लगायतका। यो <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> अनुप्रयोगबाट आउँदछ। यो इनपुट विधि प्रयोग गर्ने हो?"</string> @@ -531,8 +531,8 @@ <string name="alarms_and_reminders_title" msgid="8819933264635406032">"घडी तथा रिमाइन्डरहरू"</string> <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"यो एपलाई अलार्म सेट गर्ने र समयमै पूरा गर्नु पर्ने कारबाहीहरूको रुटिन बनाउने अनुमति दिनुहोस्। यो अनुमति दिइएको छ भने यो एप ब्याकग्राउन्डमा चल्छ र धेरै ब्याट्री खपत हुन्छ।\n\nयो अनुमति दिइएको छैन भने सेट गरिएका अलार्म बज्दैनन् र यो एपले तय गरेका गतिविधि चल्दैनन्।"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"समयतालिका, अलार्म, रिमाइन्डर, घडी"</string> - <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"सक्रिय गर्नुहोस्"</string> - <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"बाधा नपुऱ्याउनुहोस् नामक मोडलाई सक्रिय गर्नुहोस्"</string> + <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"अन गर्नुहोस्"</string> + <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"बाधा नपुऱ्याउनुहोस् नामक मोडलाई अन गर्नुहोस्"</string> <string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"कहिल्यै होइन"</string> <string name="zen_interruption_level_priority" msgid="5392140786447823299">"प्राथमिकता दिइएको मात्र"</string> <string name="zen_mode_and_condition" msgid="8877086090066332516">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>। <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string> @@ -547,7 +547,7 @@ <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"यो फोन"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"यो ट्याब्लेट"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"यो फोन"</string> - <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि सक्रिय गर्नुहोस्"</string> + <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि अन गर्नुहोस्"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"तारयुक्त अडियो यन्त्र"</string> <string name="help_label" msgid="3528360748637781274">"मद्दत र प्रतिक्रिया"</string> <string name="storage_category" msgid="2287342585424631813">"भण्डारण"</string> @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"नयाँ प्रयोगकर्ता सिर्जना गर्न सकिएन"</string> <string name="add_guest_failed" msgid="8074548434469843443">"नयाँ अतिथि बनाउन सकिएन"</string> <string name="user_nickname" msgid="262624187455825083">"उपनाम"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"प्रयोगकर्ता थप्नुहोस्"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"अतिथि सत्र रिसेट गर्नुहोस्"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गर्ने हो?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"यी अतिथि हटाउने हो?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रिसेट गर्नुहोस्"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"हटाउनुहोस्"</string> <string name="guest_resetting" msgid="7822120170191509566">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गरिँदै छ…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो चयन गर्नुहोस्"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"धेरै पटक गलत तरिकाले अनलक गर्ने प्रयास गरियो। यो डिभाइसमा भएको डेटा मेटाइने छ।"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"धेरै पटक गलत तरिकाले अनलक गर्ने प्रयास गरियो। यो प्रयोगकर्ता मेटाइने छ।"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"धेरै पटक गलत तरिकाले अनलक गर्ने प्रयास गरियो। यो कार्य प्रोफाइल र यसमा भएको डेटा मेटाइने छ।"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"हटाउनुहोस्"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिफल्ट डिभाइस"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"असक्षम पारिएको छ"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सक्षम पारिएको छ"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"स्क्रिन अन गर्नुहोस्"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"स्क्रिन अन गर्ने अनुमति दिइयोस्"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"कुनै एपलाई स्क्रिन अन गर्ने अनुमति दिइयोस्। यो अनुमति दिइएका खण्डमा तपाईंले अन गर्न नखोजेका बेलामा पनि एपले जुनसुकै बेला स्क्रिन अन गर्न सक्छ।"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR कोड स्क्यान गर्नुहोस्"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"अडियो सुन्न तलको QR कोडलाई केन्द्र भागमा पार्नुहोस्"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR कोडको फर्म्याट वैध छैन"</string> </resources> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 72d574fb389c..82d584453d7c 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Kan geen nieuwe gebruiker maken"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Kan geen nieuwe gast maken"</string> <string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Gebruiker toevoegen"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Gastsessie resetten"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Gast resetten?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Gast verwijderen?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetten"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Verwijderen"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gast resetten…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Foto selecteren"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Te veel onjuiste pogingen. De gegevens van dit apparaat worden verwijderd."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Te veel onjuiste pogingen. Deze gebruiker wordt verwijderd."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Te veel onjuiste pogingen. Dit werkprofiel en de bijbehorende gegevens worden verwijderd."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Sluiten"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Apparaatstandaard"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Uitgezet"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aangezet"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Scherm aanzetten"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Scherm aanzetten toestaan"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Toestaan dat een app het scherm aanzet. Indien toegestaan, kan de app het scherm op elk moment aanzetten zonder jouw expliciete intentie."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR-code scannen"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Centreer de onderstaande QR-code om te beginnen met luisteren"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR-code heeft geen geldige indeling"</string> </resources> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 53777dfe6cf7..128328f8c5eb 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରିବାକୁ ବିଫଳ ହେଲା"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ଜଣେ ନୂଆ ଅତିଥି ତିଆରି କରିବାରେ ବିଫଳ ହୋଇଛି"</string> <string name="user_nickname" msgid="262624187455825083">"ଡାକନାମ"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"ଉପଯୋଗକର୍ତ୍ତା ଯୋଗ କରନ୍ତୁ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"ଅତିଥି ସେସନକୁ ରିସେଟ୍ କରନ୍ତୁ"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"ଅତିଥି ସେସନକୁ ରିସେଟ୍ କରିବେ?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"ଅତିଥିଙ୍କୁ କାଢ଼ିବେ?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ରିସେଟ୍ କରନ୍ତୁ"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"କାଢ଼ି ଦିଅନ୍ତୁ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ଅତିଥି ସେସନକୁ ରିସେଟ୍ କରାଯାଉଛି…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ଫଟୋ ବାଛନ୍ତୁ"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପ୍ରଚେଷ୍ଟା। ଏହି ଡିଭାଇସର ଡାଟା ଡିଲିଟ ହୋଇଯିବ।"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପ୍ରଚେଷ୍ଟା। ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଡିଲିଟ କରିଦିଆଯିବ।"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପ୍ରଚେଷ୍ଟା। ଏହି ୱାର୍କ ପ୍ରୋଫାଇଲ ଏବଂ ଏହାର ଡାଟା ଡିଲିଟ ହୋଇଯିବ।"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"ଖାରଜ କରନ୍ତୁ"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ଡିଭାଇସ୍ ଡିଫଲ୍ଟ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ଅକ୍ଷମ କରାଯାଇଛି"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"ସ୍କ୍ରିନକୁ ଚାଲୁ କରନ୍ତୁ"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"ସ୍କ୍ରିନକୁ ଚାଲୁ କରିବା ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ସ୍କ୍ରିନକୁ ଚାଲୁ କରିବା ପାଇଁ ଏକ ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଯଦି ଅନୁମତି ଦିଆଯାଏ, ତେବେ ଆପଟି ଆପଣଙ୍କ ସ୍ପଷ୍ଟ ଇଣ୍ଟେଣ୍ଟ ବିନା ଯେ କୌଣସି ସମୟରେ ସ୍କ୍ରିନକୁ ଚାଲୁ କରିପାରେ।"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR କୋଡ ସ୍କାନ କରନ୍ତୁ"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"ଶୁଣିବା ଆରମ୍ଭ କରିବା ପାଇଁ, ନିମ୍ନରେ ଥିବା QR କୋଡକୁ କେନ୍ଦ୍ରରେ ରଖନ୍ତୁ"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR କୋଡ ଏକ ବୈଧ ଫର୍ମାଟ ନୁହେଁ"</string> </resources> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 07ef29c06c52..3beda13db011 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਉਣਾ ਅਸਫਲ ਰਿਹਾ"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ਨਵਾਂ ਮਹਿਮਾਨ ਪ੍ਰੋਫਾਈਲ ਬਣਾਉਣਾ ਅਸਫਲ ਰਿਹਾ"</string> <string name="user_nickname" msgid="262624187455825083">"ਉਪਨਾਮ"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"ਵਰਤੋਂਕਾਰ ਨੂੰ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਨੂੰ ਰੀਸੈੱਟ ਕਰੋ"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"ਕੀ ਮਹਿਮਾਨ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"ਕੀ ਮਹਿਮਾਨ ਨੂੰ ਹਟਾਉਣਾ ਹੈ?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ਰੀਸੈੱਟ ਕਰੋ"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ਹਟਾਓ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ਮਹਿਮਾਨ ਨੂੰ ਰੀਸੈੱਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ਫ਼ੋਟੋ ਚੁਣੋ"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"ਬਹੁਤ ਸਾਰੀਆਂ ਗਲਤ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸ ਡੀਵਾਈਸ ਦਾ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"ਬਹੁਤ ਸਾਰੀਆਂ ਗਲਤ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸ ਵਰਤੋਂਕਾਰ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"ਬਹੁਤ ਸਾਰੀਆਂ ਗਲਤ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਹ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਅਤੇ ਇਸਦਾ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"ਖਾਰਜ ਕਰੋ"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ਡੀਵਾਈਸ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ਬੰਦ ਕੀਤਾ ਗਿਆ"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"ਸਕ੍ਰੀਨ ਚਾਲੂ ਕਰੋ"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"ਸਕ੍ਰੀਨ ਚਾਲੂ ਕਰਨ ਦਿਓ"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ਐਪ ਨੂੰ ਸਕ੍ਰੀਨ ਚਾਲੂ ਕਰਨ ਦਿਓ। ਜੇ ਇਜਾਜ਼ਤ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ, ਤਾਂ ਐਪ ਕਿਸੇ ਵੀ ਸਮੇਂ ਸਕ੍ਰੀਨ ਨੂੰ ਚਾਲੂ ਕਰ ਸਕਦੀ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਨੂੰ ਇਸਦੀ ਲੋੜ ਨਾ ਹੋਵੇ।"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR ਕੋਡ ਸਕੈਨ ਕਰੋ"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"ਸੁਣਨਾ ਸ਼ੁਰੂ ਕਰਨ ਲਈ, ਹੇਠਾਂ ਦਿੱਤੇ QR ਕੋਡ ਨੂੰ ਕੇਂਦਰ ਵਿੱਚ ਰੱਖੋ"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR ਕੋਡ ਦਾ ਫਾਰਮੈਟ ਵੈਧ ਨਹੀਂ ਹੈ"</string> </resources> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index b2deca7b9a0a..e8fa3ae5c802 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -259,7 +259,7 @@ <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Paruję urządzenie…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Nie udało się sparować z urządzeniem. Kod QR jest nieprawidłowy albo urządzenie nie jest podłączone do tej samej sieci."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Adres IP i port"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Zeskanuj kod QR"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Skaner kodów QR"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Sparuj urządzenia przez Wi-Fi, skanując kod QR"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Połącz się z siecią Wi-Fi"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string> @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Nie udało się utworzyć nowego użytkownika"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Nie udało się utworzyć nowego gościa"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Dodaj użytkownika"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Resetuj sesję gościa"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Zresetować sesję gościa?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Usunąć gościa?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetuj"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Usuń"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetuję sesję gościa…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Wybierz zdjęcie"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Zbyt wiele nieudanych prób. Dane na urządzeniu zostaną usunięte."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Zbyt wiele nieudanych prób. Użytkownik zostanie usunięty."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Zbyt wiele nieudanych prób. Profil służbowy i powiązane z nim dane zostaną usunięte."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Zamknij"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Ustawienie domyślne urządzenia"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Wyłączono"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Włączono"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Włączanie ekranu"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Zezwalaj na włączanie ekranu"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Zezwalaj na włączanie ekranu przez aplikację. Gdy przyznasz te uprawnienia, aplikacja będzie mogła w dowolnym momencie włączyć ekran bez Twojego wyraźnego pozwolenia."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skaner kodów QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Aby odsłuchać, wyśrodkuj kod QR poniżej"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Nieprawidłowy format kodu QR"</string> </resources> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index b210f4bbf4aa..7f923be23145 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Falha ao criar um novo usuário"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Falha ao criar um novo convidado"</string> <string name="user_nickname" msgid="262624187455825083">"Apelido"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Adicionar usuário"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Redefinir sessão de visitante"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Redefinir visitante?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Remover convidado?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Redefinir"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string> <string name="guest_resetting" msgid="7822120170191509566">"Redefinindo visitante…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Excesso de tentativas incorretas. Os dados deste dispositivo serão excluídos."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Excesso de tentativas incorretas. O usuário será excluído."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Excesso de tentativas incorretas. Este perfil de trabalho e os dados dele serão excluídos."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Dispensar"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Padrão do dispositivo"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ligar tela"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permitir que a tela seja ligada"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permitir que um app ligue a tela. Se permitido, o app vai poder ligar a tela a qualquer momento sem uma intent explícita."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Ler o código QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Para começar a ouvir, centralize o código QR abaixo"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"O código QR não está em um formato válido"</string> </resources> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 73bba6709c63..beecbab8dd89 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -306,7 +306,7 @@ <string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"Selecionar modo DNS privado"</string> <string name="private_dns_mode_off" msgid="7065962499349997041">"Desativado"</string> <string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"Automático"</string> - <string name="private_dns_mode_provider" msgid="3619040641762557028">"Nome de anfitrião do fornecedor DNS privado"</string> + <string name="private_dns_mode_provider" msgid="3619040641762557028">"Nome do anfitrião do fornecedor DNS privado"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"Introduza nome - anfitrião do fornecedor DNS"</string> <string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"Não foi possível estabelecer ligação"</string> <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostrar opções da certificação de display sem fios"</string> @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Falha ao criar um novo utilizador"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Falha ao criar um novo convidado"</string> <string name="user_nickname" msgid="262624187455825083">"Alcunha"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Adicionar utilizador"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Repor convidado"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Pretende repor o convidado?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Remover o convidado?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Repor"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string> <string name="guest_resetting" msgid="7822120170191509566">"A repor o convidado…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Demasiadas tentativas incorretas. Os dados deste dispositivo vão ser eliminados."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Demasiadas tentativas incorretas. Este utilizador vai ser eliminado."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Demasiadas tentativas incorretas. Este perfil de trabalho e os respetivos dados vão ser eliminados."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Ignorar"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predefinição do dispositivo"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativada"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ative o ecrã"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permitir a ativação do ecrã"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permita que uma app ative o ecrã. Se a autorização for concedida, a app pode ativar o ecrã em qualquer altura sem a sua intenção explícita."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Leia o código QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Para começar a ouvir, centre o código QR abaixo"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"O código QR não é um formato válido"</string> </resources> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index b210f4bbf4aa..7f923be23145 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Falha ao criar um novo usuário"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Falha ao criar um novo convidado"</string> <string name="user_nickname" msgid="262624187455825083">"Apelido"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Adicionar usuário"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Redefinir sessão de visitante"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Redefinir visitante?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Remover convidado?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Redefinir"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string> <string name="guest_resetting" msgid="7822120170191509566">"Redefinindo visitante…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Excesso de tentativas incorretas. Os dados deste dispositivo serão excluídos."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Excesso de tentativas incorretas. O usuário será excluído."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Excesso de tentativas incorretas. Este perfil de trabalho e os dados dele serão excluídos."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Dispensar"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Padrão do dispositivo"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ligar tela"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permitir que a tela seja ligada"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permitir que um app ligue a tela. Se permitido, o app vai poder ligar a tela a qualquer momento sem uma intent explícita."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Ler o código QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Para começar a ouvir, centralize o código QR abaixo"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"O código QR não está em um formato válido"</string> </resources> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index bb9176ec7dc8..1a0902f6d4fd 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Resetați invitatul?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Excludeți invitatul?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetați"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eliminați"</string> <string name="guest_resetting" msgid="7822120170191509566">"Se resetează invitatul…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Activați ecranul"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permiteți activarea ecranului"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permiteți unei aplicații să activeze ecranul. Dacă acordați permisiunea, aplicația poate să activeze oricând ecranul, fără intenția dvs. explicită."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Scanați codul QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Pentru a începe să ascultați, centrați codul QR de mai jos"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Codul QR nu are un format valid"</string> </resources> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 33f35445ac14..8c2f78c866a0 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Не удалось создать пользователя"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Не удалось создать гостя."</string> <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Добавить пользователя"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавить гостя"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Сбросить гостевой сеанс"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Сбросить гостевой сеанс?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Удалить гостя?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Сбросить"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Удалить"</string> <string name="guest_resetting" msgid="7822120170191509566">"Сброс гостевого сеанса…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Выбрать фотографию"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Слишком много неудачных попыток. С устройства будут удалены все данные."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Слишком много неудачных попыток. Этот пользователь будет удален."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Слишком много неудачных попыток. Этот рабочий профиль и его данные будут удалены."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Закрыть"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Вариант по умолчанию"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Отключено"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Включено"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Включение экрана"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Разрешить приложению включать экран"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Приложение сможет в любое время включать экран без явного согласия с вашей стороны"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Сканирование QR-кода"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Чтобы прослушать, поместите QR-код в центр"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Недопустимый формат QR-кода"</string> </resources> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index abedbb95de09..d9f08f23154f 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"නව පරිශීලකයෙකු තැනීමට අසමත් විය"</string> <string name="add_guest_failed" msgid="8074548434469843443">"නව අමුත්තකු තැනීම අසාර්ථක විය"</string> <string name="user_nickname" msgid="262624187455825083">"අපනාමය"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"පරිශීලකයා එක් කරන්න"</string> <string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"ආගන්තුකයා යළි සකසන්න"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"අමුත්තා යළි සකසන්නද?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"අමුත්තා ඉවත් කරන්නද?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"යළි සකසන්න"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ඉවත් කරන්න"</string> <string name="guest_resetting" msgid="7822120170191509566">"අමුත්තා යළි සකසමින්…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ඡායාරූපය තෝරන්න"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"වැරදි උත්සාහයන් ඉතා වැඩි ගණනකි. මෙම උපාංගයෙහි දත්ත මකනු ඇත."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"වැරදි උත්සාහයන් ඉතා වැඩි ගණනකි. මෙම පරිශීලකයා මකනු ඇත."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"වැරදි උත්සාහයන් ඉතා වැඩි ගණනකි. මෙම කාර්යාල පැතිකඩ සහ එහි දත්ත මකනු ඇත."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"ඉවත ලන්න"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"උපාංගයේ පෙරනිමිය"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"අබල කළා"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"සබලයි"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"තිරය ක්රියාත්මක කරන්න"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"තිරය ක්රියාත්මක කිරීමට ඉඩ දෙන්න"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"යෙදුමකට තිරය ක්රියාත්මක කිරීමට ඉඩ දෙන්න. ඉඩ දුනහොත්, යෙදුම ඔබගේ පැහැදිලි අභිප්රායෙන් තොරව ඕනෑම වේලාවක තිරය ක්රියාත්මක කළ හැකිය."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR කේතය ස්කෑන් කරන්න"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"සවන් දීම ආරම්භ කිරීමට, පහත QR කේතය මධ්යගත කරන්න"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR කේතය වලංගු ආකෘතියක් නොවේ"</string> </resources> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index c531a56576c4..ac6f38d21122 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Nového použív. sa nepodarilo vytvoriť"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Nového hosťa sa nepodarilo vytvoriť"</string> <string name="user_nickname" msgid="262624187455825083">"Prezývka"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Pridať používateľa"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Obnoviť reláciu hosťa"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Chcete resetovať reláciu hosťa?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Chcete odobrať hosťa?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetovať"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstrániť"</string> <string name="guest_resetting" msgid="7822120170191509566">"Relácia hosťa sa resetuje…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrať fotku"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Príliš veľa chybných pokusov. Údaje tohto zariadenia budú odstránené."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Príliš veľa chybných pokusov. Tento používateľ bude odobraný."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Príliš veľa chybných pokusov. Tento pracovný profil a jeho údaje budú odstránené."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Zavrieť"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predvol. nastavenie zariadenia"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuté"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Zapínanie obrazovky"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Povolenie zapínania obrazovky"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Povoľte aplikácii zapínať obrazovku. Ak to urobíte, aplikácia bude môcť kedykoľvek zapínať obrazovku, a to aj vtedy, keď to nebudete mať v úmysle."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Naskenovanie QR kódu"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Ak chcete začať počúvať, umiestnite QR kód do stredu nižšie"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR kód nie je platný formát"</string> </resources> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index ad7256d237d2..9746320f2864 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Ustvarjanje novega uporabnika ni uspelo."</string> <string name="add_guest_failed" msgid="8074548434469843443">"Ustvarjanje novega gosta ni uspelo."</string> <string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Dodaj uporabnika"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Ponastavi gosta"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Želite ponastaviti gosta?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Želite odstraniti gosta?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ponastavi"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstrani"</string> <string name="guest_resetting" msgid="7822120170191509566">"Ponastavljanje gosta …"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Izbira fotografije"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Preveč napačnih poskusov. Podatki v napravi bodo izbrisani."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Preveč napačnih poskusov. Uporabnik bo izbrisan."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Preveč napačnih poskusov. Delovni profil in podatki v njem bodo izbrisani."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Opusti"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Privzeta nastavitev naprave"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogočeno"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Vklop zaslona"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Dovoli vklop zaslona"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Dovolite aplikaciji, da vklopi zaslon. Če ji to odobrite, lahko aplikacija kadar koli brez vašega eksplicitnega namena vklopi zaslon."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Optično branje kode QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Če želite začeti poslušati, postavite spodnjo kodo QR na sredino."</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Koda QR nima pravilne oblike zapisa."</string> </resources> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 51fda8cfabb6..5e7ca023c429 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Krijimi i një përdoruesi të ri dështoi"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Profili i vizitorit të ri nuk u krijua"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Shto përdorues"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Rivendos vizitorin"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Të rivendoset vizitori?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Të hiqet vizitori?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Rivendos"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Hiq"</string> <string name="guest_resetting" msgid="7822120170191509566">"Vizitori po rivendoset…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Zgjidh një fotografi"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Shumë tentativa të pasakta. Të dhënat e kësaj pajisjeje do të fshihen."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Shumë tentativa të pasakta. Ky përdorues do të fshihet."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Shumë tentativa të pasakta. Ky profil pune dhe të dhënat e tij do të fshihen."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Hiq"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parazgjedhja e pajisjes"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Joaktiv"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ndiz ekranin"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Lejo ndezjen e ekranit"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Lejo një aplikacion që të ndezë ekranin. Nëse lejohet, aplikacioni mund ta ndezë ekranin në çdo kohë pa synimin tënd të shprehur."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skano kodin QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Që të fillosh të dëgjosh, vendos në qendër kodin QR më poshtë"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Kodi QR nuk është në format të vlefshëm"</string> </resources> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 80092d0765de..7cda37116269 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -448,7 +448,7 @@ <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалија (црвено-зелено)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалија (плаво-жуто)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Корекција боја"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Корекција боја може да буде корисна када желите:to:<br/> <ol> <li>&nbsp;да прецизније видите боје</li> <li>&nbsp;да уклоните боје како бисте се фокусирали</li> </ol>"</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Корекција боја може да буде корисна када желите:<br/> <ol> <li>&nbsp;Прецизније да видите боје</li> <li>&nbsp;Да уклоните боје како бисте се фокусирали</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Замењује га <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>–<xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Преостало је око <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Желите ли да ресетујете сесију госта?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Желите ли да уклоните госта?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ресетуј"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Уклони"</string> <string name="guest_resetting" msgid="7822120170191509566">"Сесија госта се ресетује…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Укључите екран"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Дозволи укључивање екрана"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Дозвољава апликацији да укључи екран. Ако се омогући, апликација може да укључи екран у било ком тренутку без ваше експлицитне намере."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Скенирајте QR кôд"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Да бисте почели да слушате, центрирајте QR кôд испод"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR кôд није у важећем формату"</string> </resources> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index f0400251ca06..2715029a50b3 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Det gick inte att skapa en ny användare"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Det gick inte att skapa en ny gäst"</string> <string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Lägg till användare"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Återställ gästsession"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Vill du återställa gästsessionen?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Vill du ta bort gästen?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Återställ"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ta bort"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gästsessionen återställs …"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Välj foto"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"För många felaktiga försök. Enhetens data raderas."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"För många felaktiga försök. Den här användaren raderas."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"För många felaktiga försök. Den här jobbprofilen och dess data raderas."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Stäng"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhetens standardinställning"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inaktiverat"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Aktivera skärmen"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Tillåt att skärmen aktiveras"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Tillåt att en app aktiverar skärmen. Om du ger tillåtelse kan appen aktivera skärmen när som helst utan din uttryckliga avsikt."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skanna QR-kod"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Centrera QR-koden nedan om du vill börja lyssna"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR-kodens format är ogiltigt"</string> </resources> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index fcc07c68e715..0da048c8dee5 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Imeshindwa kuweka mtumiaji mpya"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Imeshindwa kuunda wasifu mpya wa mgeni"</string> <string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Weka mtumiaji"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ongeza mgeni"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Badilisha kipindi cha mgeni"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Ungependa kubadilisha kipindi cha mgeni?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Ungependa kumwondoa mgeni?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Badilisha"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ondoa"</string> <string name="guest_resetting" msgid="7822120170191509566">"Inabadilisha kipindi cha mgeni…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Chagua picha"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Umejaribu kufungua mara nyingi mno kwa njia isiyo sahihi. Data iliyo kwenye kifaa hiki itafutwa."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Umejaribu kufungua mara nyingi mno kwa njia isiyo sahihi. Maelezo ya mtumiaji huyu yatafutwa."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Umejaribu kufungua mara nyingi mno kwa njia isiyo sahihi. Wasifu huu wa kazini utafutwa pamoja na data yake."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Ondoa"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Hali chaguomsingi ya kifaa"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Imezimwa"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Washa skrini"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Ruhusu kuwasha skrini"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Ruhusu programu iwashe skrini. Ikiwa imepewa idhini, programu inaweza kuwasha skrini wakati wowote bila utaratibu wako dhahiri wa kuratibu."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Changanua msimbo wa QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Ili uanze kusikiliza, weka katikati msimbo wa QR ulio hapa chini"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Msimbo wa QR si muundo sahihi"</string> </resources> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 64682012c5cb..99cfc8d588af 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"புதிய பயனரை உருவாக்க முடியவில்லை"</string> <string name="add_guest_failed" msgid="8074548434469843443">"புதிய விருந்தினரை உருவாக்க முடியவில்லை"</string> <string name="user_nickname" msgid="262624187455825083">"புனைப்பெயர்"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"பயனரைச் சேர்"</string> <string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"கெஸ்ட் அமர்வை மீட்டமை"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"கெஸ்ட்டை மீட்டமைக்கவா?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"கெஸ்ட் பயனரை அகற்றவா?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"மீட்டமை"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"அகற்று"</string> <string name="guest_resetting" msgid="7822120170191509566">"கெஸ்ட்டை மீட்டமைக்கிறது…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"படத்தைத் தேர்ந்தெடுங்கள்"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"பலமுறை தவறாக முயன்ற காரணத்தால் இந்தச் சாதனத்தின் தரவு நீக்கப்படும்."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"பலமுறை தவறாக முயன்ற காரணத்தால் இந்தப் பயனர் நீக்கப்படுவார்."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"பலமுறை தவறாக முயன்ற காரணத்தால் இந்தப் பணிக் கணக்கும் அதன் தரவும் நீக்கப்படும்."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"நிராகரி"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"சாதனத்தின் இயல்புநிலை"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"முடக்கப்பட்டது"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"இயக்கப்பட்டது"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"திரையை ஆன் செய்தல்"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"திரையை ஆன் செய்வதை அனுமதி"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"திரையை ஆன் செய்ய ஓர் ஆப்ஸை அனுமதிக்கவும். அனுமதித்தால், உங்கள் தலையீடு இல்லாமலே ஆப்ஸ் எப்போது வேண்டுமானாலும் திரையை ஆன் செய்யக்கூடும்."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR குறியீட்டை ஸ்கேன் செய்யுங்கள்"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"ஆடியோவைக் கேட்க, கீழுள்ள QR குறியீட்டை மையப்படுத்திக் காட்டுங்கள்"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR குறியீடு சரியான வடிவமைப்பில் இல்லை"</string> </resources> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index da2527a4b6ab..cf237de32a98 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -287,7 +287,7 @@ <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"సంపూర్ణ వాల్యూమ్ను డిజేబుల్ చేయి"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheను ఎనేబుల్ చేయి"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"బ్లూటూత్ AVRCP వెర్షన్"</string> - <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"బ్లూటూత్ AVRCP సంస్కరణను ఎంచుకోండి"</string> + <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"బ్లూటూత్ AVRCP వెర్షన్ను ఎంచుకోండి"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"బ్లూటూత్ MAP వెర్షన్"</string> <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"బ్లూటూత్ MAP వెర్షన్ను ఎంచుకోండి"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"బ్లూటూత్ ఆడియో కోడెక్"</string> @@ -569,7 +569,7 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"యూజర్"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"పరిమితం చేయబడిన ప్రొఫైల్"</string> <string name="user_add_user_title" msgid="5457079143694924885">"కొత్త వినియోగదారుని జోడించాలా?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"అదనపు యూజర్లను క్రియేట్ చేయడం ద్వారా మీరు ఈ దేవైజ్ను ఇతరులతో షేర్ చేయవచ్చు. ప్రతి యూజర్కు వారికంటూ ప్రత్యేక స్థలం ఉంటుంది, వారు ఆ స్థలాన్ని యాప్లు, వాల్పేపర్ మొదలైనవాటితో అనుకూలీకరించవచ్చు. యూజర్లు ప్రతి ఒక్కరిపై ప్రభావం చూపే Wi‑Fi వంటి పరికర సెట్టింగ్లను కూడా సర్దుబాటు చేయవచ్చు.\n\nమీరు కొత్త యూజర్ ను జోడించినప్పుడు, ఆ వ్యక్తి వారికంటూ స్వంత స్థలం సెట్ చేసుకోవాలి.\n\nఏ వినియోగదారు అయినా మిగిలిన అందరు యూజర్ల కోసం యాప్లను అప్డేట్ చేయవచ్చు. యాక్సెస్ సామర్ధ్యం సెట్టింగ్లు మరియు సేవలు కొత్త యూజర్కి బదిలీ కాకపోవచ్చు."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"అదనపు యూజర్లను క్రియేట్ చేయడం ద్వారా మీరు ఈ దేవైజ్ను ఇతరులతో షేర్ చేయవచ్చు. ప్రతి యూజర్కు వారికంటూ ప్రత్యేక స్థలం ఉంటుంది, వారు ఆ స్థలాన్ని యాప్లు, వాల్పేపర్ మొదలైనవాటితో అనుకూలీకరించవచ్చు. యూజర్లు ప్రతి ఒక్కరిపై ప్రభావం చూపే Wi‑Fi వంటి పరికర సెట్టింగ్లను కూడా సర్దుబాటు చేయవచ్చు.\n\nమీరు కొత్త యూజర్ ను జోడించినప్పుడు, ఆ వ్యక్తి వారికంటూ స్వంత స్థలం సెట్ చేసుకోవాలి.\n\nఏ వినియోగదారు అయినా మిగిలిన అందరు యూజర్ల కోసం యాప్లను అప్డేట్ చేయవచ్చు. యాక్సెసిబిలిటీ సెట్టింగ్లు మరియు సేవలు కొత్త యూజర్కి బదిలీ కాకపోవచ్చు."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"మీరు కొత్త వినియోగదారుని జోడించినప్పుడు, ఆ వ్యక్తి తన స్థలాన్ని సెటప్ చేసుకోవాలి.\n\nఏ వినియోగదారు అయినా మిగతా అందరు వినియోగదారుల కోసం యాప్లను అప్డేట్ చేయగలరు."</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"యూజర్ను ఇప్పుడే సెటప్ చేయాలా?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"పరికరాన్ని తీసుకోవడానికి వ్యక్తి అందుబాటులో ఉన్నారని నిర్ధారించుకొని, ఆపై వారికి నిల్వ స్థలాన్ని సెటప్ చేయండి"</string> @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"గెస్ట్ సెషన్ను రీసెట్ చేయాలా?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"అతిథిని తీసివేయాలా?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"రీసెట్ చేయండి"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"తీసివేయండి"</string> <string name="guest_resetting" msgid="7822120170191509566">"గెస్ట్ సెషన్ను రీసెట్ చేస్తోంది…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్ను ఎంచుకోండి"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"స్క్రీన్ను ఆన్ చేయండి"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"స్క్రీన్ను ఆన్ చేయడానికి అనుమతించండి"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"స్క్రీన్ను ఆన్ చేయడానికి యాప్ను అనుమతించండి. మంజూరు చేయబడితే, మీ స్పష్టమైన ఉద్దేశం లేకుండా యాప్ ఎప్పుడైనా స్క్రీన్ను ఆన్ చేయవచ్చు."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR కోడ్ను స్కాన్ చేయండి"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"వినడం ప్రారంభించడానికి, కింద ఉన్న QR కోడ్ను మధ్యలో ఉంచండి"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR కోడ్ చెల్లుబాటు అయ్యే ఫార్మాట్లో లేదు"</string> </resources> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index c5bd8268be43..0b548235ef02 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"รีเซ็ตผู้เข้าร่วมไหม"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"นำผู้เข้าร่วมออกไหม"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"รีเซ็ต"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"นำออก"</string> <string name="guest_resetting" msgid="7822120170191509566">"กำลังรีเซ็ตผู้เข้าร่วม…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string> @@ -651,4 +652,10 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"เปิดหน้าจอ"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"อนุญาตให้เปิดหน้าจอ"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"อนุญาตให้แอปเปิดหน้าจอ หากอนุญาต แอปอาจเปิดหน้าจอได้ทุกเมื่อแม้คุณไม่ได้ระบุเจตนาที่ชัดแจ้ง"</string> + <!-- no translation found for bt_le_audio_scan_qr_code (3521809854780392679) --> + <skip /> + <!-- no translation found for bt_le_audio_scan_qr_code_scanner (4679500020630341107) --> + <skip /> + <!-- no translation found for bt_le_audio_qr_code_is_not_valid_format (6092191081849434734) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index e08ba325859f..4cb30486da81 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"I-reset ang session ng bisita?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Alisin ang bisita?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"I-reset"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Alisin"</string> <string name="guest_resetting" msgid="7822120170191509566">"Nire-reset ang bisita…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"I-on ang screen"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Payagan ang pag-on sa screen"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Nagpapahintulot sa app na i-on ang screen. Kung papayagan, puwedeng i-on ng app ang screen anumang oras nang wala ng iyong malinaw na intent."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"I-scan ang QR code"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Para simulang makinig, igitna ang QR code sa ibaba"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Hindi valid na format ang QR code"</string> </resources> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index c6ea09808db7..0a20d9f67aad 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Yeni kullanıcı oluşturulamadı"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Yeni misafir oluşturulamadı"</string> <string name="user_nickname" msgid="262624187455825083">"Takma ad"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Kullanıcı ekle"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Misafir oturumunu sıfırla"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Misafir oturumu sıfırlansın mı?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Konuk çıkarılsın mı?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Sıfırla"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Kaldır"</string> <string name="guest_resetting" msgid="7822120170191509566">"Misafir oturumu sıfırlanıyor…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Fotoğraf seç"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Çok fazla sayıda hatalı deneme yapıldı. Bu cihazın verileri silinecek."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Çok fazla sayıda hatalı deneme yapıldı. Bu kullanıcı silinecek."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Çok fazla sayıda hatalı denemede yapıldı. İş profiliniz ve verileri silinecek."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Kapat"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz varsayılanı"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Devre dışı"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ekranı aç"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Ekranı açmaya izin ver"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Bir uygulamanın ekranı açmasına izin verin. İzin verildiğinde, uygulama sizin belirgin niyetiniz olmadan istediği zaman ekranı açabilir."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR kodunu tara"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Dinlemeye başlamak için aşağıdaki QR kodunu ortalayın"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR kodu geçerli bir biçim değil"</string> </resources> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 28ac9fb75ca0..a6f828eab316 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Не вдалося створити користувача"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Не вдалося створити гостя"</string> <string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Додати користувача"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Скинути сеанс у режимі \"Гість\""</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Скинути сеанс у режимі \"Гість\"?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Вилучити гостя?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Скинути"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Вилучити"</string> <string name="guest_resetting" msgid="7822120170191509566">"Скидання сеансу в режимі \"Гість\"…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Вибрати фотографію"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Забагато невдалих спроб. Дані на цьому пристрої буде видалено."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Забагато невдалих спроб. Цього користувача буде видалено."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Забагато невдалих спроб. Цей робочий профіль і його дані буде видалено."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Закрити"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"За умовчанням для пристрою"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Вимкнено"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Увімкнення екрана"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Дозволити ввімкнення екрана"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Дозвольте додатку вмикати екран. Якщо ви надасте цей дозвіл, додаток зможе будь-коли вмикати екран пристрою навіть без вашого явного наміру."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Сканування QR-коду"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Щоб почати слухати аудіо, наведіть камеру на QR-код нижче"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Недійсний формат QR-коду"</string> </resources> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 8e2a1a3e7715..c09202e55e88 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"نیا صارف بنانے میں ناکام"</string> <string name="add_guest_failed" msgid="8074548434469843443">"نیا مہمان بنانے میں ناکام"</string> <string name="user_nickname" msgid="262624187455825083">"عرفی نام"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"صارف کو شامل کریں"</string> <string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"مہمان کو ری سیٹ کریں"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"مہمان کو ری سیٹ کریں؟"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"مہمان کو ہٹائیں؟"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ری سیٹ کریں"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ہٹائیں"</string> <string name="guest_resetting" msgid="7822120170191509566">"مہمان کو ری سیٹ کرنا…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"تصویر منتخب کریں"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"بہت زیادہ غلط کوششیں۔ اس آلے کا ڈیٹا حذف کر دیا جائے گا۔"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"بہت زیادہ غلط کوششیں۔ اس صارف کو حذف کر دیا جائے گا۔"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"بہت زیادہ غلط کوششیں۔ یہ دفتری پروفائل اور اس کا ڈیٹا حذف کر دیا جائے گا۔"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"برخاست کریں"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"آلہ ڈیفالٹ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیر فعال"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"اسکرین آن کریں"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"اسکرین آن کرنے کی اجازت دیں"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ایپ کو اسکرین آن کرنے کی اجازت دیں۔ اگر اجازت دی گئی تو ایپ آپ کے واضح مقصد کے بغیر کسی بھی وقت اسکرین کو آن کر سکتی ہے۔"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR کوڈ اسکین کریں"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"سننا شروع کرنے کے لیے، نیچے کے QR کوڈ کو سینٹر میں رکھیں"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR کوڈ درست فارمیٹ نہیں ہے"</string> </resources> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 5a9763278cd8..c26b9ce30ab3 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Yangi foydalanuvchi yaratilmadi"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Yangi mehmon yaratilmadi"</string> <string name="user_nickname" msgid="262624187455825083">"Nik"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Foydalanuvchi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmonni olib tashlash"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Mehmon seansini tiklash"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Mehmon seansi tiklansinmi?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Mehmon olib tashlansinmi?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tiklash"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Olib tashlash"</string> <string name="guest_resetting" msgid="7822120170191509566">"Mehmon seansi tiklanmoqda…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Surat tanlash"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Juda koʻp marta muvaffaqiyatsiz urinishlar amalga oshirildi. Bu qurilmadagi maʼlumotlar o‘chirib tashlanadi."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Juda koʻp marta muvaffaqiyatsiz urindingiz. Bu foydalanuvchi oʻchirib tashlanadi."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Juda koʻp marta muvaffaqiyatsiz urindingiz. Bu ish profili va undagi maʼlumotlar oʻchirib tashlanadi."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Yopish"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Qurilma standarti"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Yoqilmagan"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Ekranni yoqish"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Ekranni yoqishga ruxsat"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Ilovaga ekranni yoqishga ruxsat berish. Ruxsat berilsa, ilova istalgan vaqt ruxsatingizsiz ekranni yoqishi mumkin."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"QR kodni skanerlash"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Tinglashni boshlash uchun quyidagi QR kodni markazga joylang"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR xato formatda"</string> </resources> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 710a02af7650..a9e99b23a94a 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"Không tạo được người dùng mới"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Không tạo được khách mới"</string> <string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"Thêm người dùng"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Đặt lại phiên khách"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Đặt lại phiên khách?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Loại bỏ khách?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Đặt lại"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Xoá"</string> <string name="guest_resetting" msgid="7822120170191509566">"Đang đặt lại phiên khách…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Chọn ảnh"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Quá nhiều lần thử không chính xác. Dữ liệu trên thiết bị này sẽ bị xoá."</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Quá nhiều lần thử không chính xác. Người dùng này sẽ bị xoá."</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Quá nhiều lần thử không chính xác. Hồ sơ công việc này và dữ liệu của hồ sơ công việc sẽ bị xoá."</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Đóng"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Theo giá trị mặc định của thiết bị"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Đã tắt"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Bật màn hình"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Cho phép bật màn hình"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Cho phép ứng dụng bật màn hình. Nếu được phép, ứng dụng có thể bật màn hình bất kỳ lúc nào kể cả khi bạn không có ý định như vậy."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Quét mã QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Để bắt đầu nghe, hãy hướng máy ảnh vào mã QR bên dưới và căn sao cho mã nằm chính giữa"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Định dạng của mã QR là không hợp lệ"</string> </resources> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index ae579ab03cfb..dcc3a200f27c 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"无法创建新用户"</string> <string name="add_guest_failed" msgid="8074548434469843443">"未能创建新的访客"</string> <string name="user_nickname" msgid="262624187455825083">"昵称"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"添加用户"</string> <string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"重置访客会话"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"要重置访客会话吗?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"要移除访客吗?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重置"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string> <string name="guest_resetting" msgid="7822120170191509566">"正在重置访客会话…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"选择照片"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"错误次数过多。系统将删除此设备上的数据。"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"错误次数过多。系统将删除此用户。"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"错误次数过多。系统将删除此工作资料和相关数据。"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"关闭"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"设备默认设置"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已启用"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"开启屏幕"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"允许开启屏幕"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"允许应用开启屏幕。如获授权,该应用便可在您未明确表达意愿的情况下随时开启屏幕。"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"扫描二维码"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"将扫描器对准下方二维码,即可开始收听"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"二维码的格式无效"</string> </resources> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 0579c3bf385e..a36e4e3987df 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"無法建立新使用者"</string> <string name="add_guest_failed" msgid="8074548434469843443">"無法建立新訪客"</string> <string name="user_nickname" msgid="262624187455825083">"暱稱"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"新增使用者"</string> <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"重設訪客"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"要重設訪客嗎?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"要移除訪客嗎?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重設"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string> <string name="guest_resetting" msgid="7822120170191509566">"正在重設訪客…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"揀相"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"錯誤次數太多,系統將會刪除此裝置上的資料。"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"錯誤次數太多,系統將會刪除此使用者。"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"錯誤次數太多,系統將會刪除此工作設定檔和相關資料。"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"關閉"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"裝置預設設定"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"開啟螢幕"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"允許開啟螢幕"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"允許應用程式開啟螢幕。應用程式獲授權後,可在您未有明確表明意圖的情況下隨時開啟螢幕。"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"掃瞄 QR 碼"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"如要開始收聽,請將掃瞄器對準下方的 QR 碼"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR 碼格式無效"</string> </resources> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 882190e91123..78ac9d9938c7 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -589,27 +589,22 @@ <string name="add_user_failed" msgid="4809887794313944872">"無法建立新的使用者"</string> <string name="add_guest_failed" msgid="8074548434469843443">"無法建立新訪客"</string> <string name="user_nickname" msgid="262624187455825083">"暱稱"</string> - <!-- no translation found for user_add_user (7876449291500212468) --> - <skip /> + <string name="user_add_user" msgid="7876449291500212468">"新增使用者"</string> <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"重設訪客"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"要重設訪客嗎?"</string> - <!-- no translation found for guest_remove_guest_dialog_title (4548511006624088072) --> - <skip /> + <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"要移除訪客嗎?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重設"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string> <string name="guest_resetting" msgid="7822120170191509566">"正在重設訪客…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"選取相片"</string> - <!-- no translation found for failed_attempts_now_wiping_device (4016329172216428897) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_user (469060411789668050) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_profile (7626589520888963129) --> - <skip /> - <!-- no translation found for failed_attempts_now_wiping_dialog_dismiss (2749889771223578925) --> - <skip /> + <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"錯誤次數過多,系統將刪除這部裝置中的資料。"</string> + <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"錯誤次數過多,系統將刪除這位使用者。"</string> + <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"錯誤次數過多,系統將刪除這個工作資料夾和當中的資料。"</string> + <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"關閉"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"裝置預設設定"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string> @@ -657,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"開啟螢幕"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"允許開啟螢幕"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"允許應用程式開啟螢幕。如果授予這項權限,即使你未明確指示,應用程式也隨時可能會開啟螢幕。"</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"掃描 QR 圖碼"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"如要開始收聽,請將掃描器對準下方的 QR 圖碼"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"QR 圖碼格式無效"</string> </resources> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index b24e9349e5c2..2e0665c354bc 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -596,6 +596,7 @@ <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Setha kabusha isimenywa?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Susa isihambeli?"</string> <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Setha kabusha"</string> + <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Susa"</string> <string name="guest_resetting" msgid="7822120170191509566">"Ukusetha kabusha isimenywa…"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string> @@ -651,4 +652,7 @@ <string name="turn_screen_on_title" msgid="3266937298097573424">"Vula isikrini"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Vumela ukuvula isikrini"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Vumela i-app ivule isikrini. Uma kuvunyiwe, i-app ingavula isikrini noma nini ngaphandle kwenhloso yakho esobala."</string> + <string name="bt_le_audio_scan_qr_code" msgid="3521809854780392679">"Skena ikhodi ye-QR"</string> + <string name="bt_le_audio_scan_qr_code_scanner" msgid="4679500020630341107">"Ukuze uqale ukulalela, beka ikhodi ye-QR ngezansi"</string> + <string name="bt_le_audio_qr_code_is_not_valid_format" msgid="6092191081849434734">"Ikhodi ye-QR ayiyona ifomethi evumelekile"</string> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index c2e36b79b753..e1a2e8daf971 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -28,6 +28,7 @@ import android.text.TextUtils; import android.util.Log; import androidx.annotation.IntDef; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; @@ -239,13 +240,24 @@ public class LocalMediaManager implements BluetoothCallback { /** * Dispatch a change in the about-to-connect device. See - * {@link DeviceCallback#onAboutToConnectDeviceChanged} for more information. + * {@link DeviceCallback#onAboutToConnectDeviceAdded} for more information. */ - public void dispatchAboutToConnectDeviceChanged( - @Nullable String deviceName, + public void dispatchAboutToConnectDeviceAdded( + @NonNull String deviceAddress, + @NonNull String deviceName, @Nullable Drawable deviceIcon) { for (DeviceCallback callback : getCallbacks()) { - callback.onAboutToConnectDeviceChanged(deviceName, deviceIcon); + callback.onAboutToConnectDeviceAdded(deviceAddress, deviceName, deviceIcon); + } + } + + /** + * Dispatch a change in the about-to-connect device. See + * {@link DeviceCallback#onAboutToConnectDeviceRemoved} for more information. + */ + public void dispatchAboutToConnectDeviceRemoved() { + for (DeviceCallback callback : getCallbacks()) { + callback.onAboutToConnectDeviceRemoved(); } } @@ -705,13 +717,27 @@ public class LocalMediaManager implements BluetoothCallback { * connect imminently and should be displayed as the current device in the media player. * See [AudioManager.muteAwaitConnection] for more details. * - * @param deviceName the name of the device (displayed to the user). - * @param deviceIcon the icon that should be used with the device. + * The information in the most recent callback should override information from any previous + * callbacks. + * + * @param deviceAddress the address of the device. {@see AudioDeviceAttributes.address}. + * If present, we'll use this address to fetch the full information + * about the device (if we can find that information). + * @param deviceName the name of the device (displayed to the user). Used as a backup in + * case using deviceAddress doesn't work. + * @param deviceIcon the icon that should be used with the device. Used as a backup in case + * using deviceAddress doesn't work. */ - default void onAboutToConnectDeviceChanged( - @Nullable String deviceName, + default void onAboutToConnectDeviceAdded( + @NonNull String deviceAddress, + @NonNull String deviceName, @Nullable Drawable deviceIcon ) {} + + /** + * Callback for notifying that we no longer have an about-to-connect device. + */ + default void onAboutToConnectDeviceRemoved() {} } /** diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java index fa4ae6712aaa..6535665b7653 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java @@ -24,6 +24,8 @@ import android.util.Log; import androidx.annotation.ChecksSdkIntAtLeast; +import com.android.internal.annotations.VisibleForTesting; + /* Utility class is to confirm the Wi-Fi function is available by enterprise restriction */ public class WifiEnterpriseRestrictionUtils { private static final String TAG = "WifiEntResUtils"; @@ -76,6 +78,26 @@ public class WifiEnterpriseRestrictionUtils { return true; } + /** + * Confirm Wi-Fi state is allowed to change to whether user restriction is set + * + * @param context A context + * @return whether the device is permitted to change Wi-Fi state + */ + public static boolean isChangeWifiStateAllowed(Context context) { + if (!hasUserRestrictionFromT(context, UserManager.DISALLOW_CHANGE_WIFI_STATE)) return true; + Log.w(TAG, "WI-FI state isn't allowed to change due to user restriction."); + return false; + } + + @VisibleForTesting + static boolean hasUserRestrictionFromT(Context context, String restrictionKey) { + if (!isAtLeastT()) return false; + final UserManager userManager = context.getSystemService(UserManager.class); + if (userManager == null) return false; + return userManager.hasUserRestriction(restrictionKey); + } + @ChecksSdkIntAtLeast(api=Build.VERSION_CODES.TIRAMISU) private static boolean isAtLeastT() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java index f6af09a34388..e9326dd39faa 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java @@ -15,8 +15,11 @@ */ package com.android.settingslib.wifi; +import static android.os.UserManager.DISALLOW_CHANGE_WIFI_STATE; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -50,6 +53,8 @@ public class WifiEnterpriseRestrictionUtilsTest { mContext = spy(ApplicationProvider.getApplicationContext()); when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); when(mUserManager.getUserRestrictions()).thenReturn(mBundle); + ReflectionHelpers.setStaticField( + Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU); } @Test @@ -129,4 +134,41 @@ public class WifiEnterpriseRestrictionUtilsTest { assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isTrue(); } + + @Test + public void isChangeWifiStateAllowed_hasDisallowRestriction_shouldReturnFalse() { + when(mUserManager.hasUserRestriction(DISALLOW_CHANGE_WIFI_STATE)).thenReturn(true); + + assertThat(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).isFalse(); + } + + @Test + public void isChangeWifiStateAllowed_hasNoDisallowRestriction_shouldReturnTrue() { + when(mUserManager.hasUserRestriction(DISALLOW_CHANGE_WIFI_STATE)).thenReturn(false); + + assertThat(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).isTrue(); + } + + @Test + public void hasUserRestrictionFromT_setSDKForS_shouldReturnTrue() { + ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S); + + assertThat(WifiEnterpriseRestrictionUtils.hasUserRestrictionFromT(mContext, "key")) + .isFalse(); + } + + @Test + public void hasUserRestrictionFromT_setSDKForT_shouldReturnHasUserRestriction() { + ReflectionHelpers.setStaticField( + Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU); + when(mUserManager.hasUserRestriction(anyString())).thenReturn(false); + + assertThat(WifiEnterpriseRestrictionUtils.hasUserRestrictionFromT(mContext, "key")) + .isFalse(); + + when(mUserManager.hasUserRestriction(anyString())).thenReturn(true); + + assertThat(WifiEnterpriseRestrictionUtils.hasUserRestrictionFromT(mContext, "key")) + .isTrue(); + } } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 74b759fcdcf9..f934b1f3ab99 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -432,7 +432,8 @@ class ActivityLaunchAnimator( right = windowBounds.right ) val callback = this@ActivityLaunchAnimator.callback!! - val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo) + val windowBackgroundColor = window.taskInfo?.let { callback.getBackgroundColor(it) } + ?: window.backgroundColor // Make sure we use the modified timings when animating a dialog into an app. val launchAnimator = if (controller.isDialogLaunch) { diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt index 2e9a16fffe9a..47c11010c072 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt @@ -138,7 +138,7 @@ class RemoteTransitionAdapter { info: TransitionInfo, t: SurfaceControl.Transaction ): RemoteAnimationTarget { - return RemoteAnimationTarget( + val target = RemoteAnimationTarget( /* taskId */ if (change.taskInfo != null) change.taskInfo!!.taskId else -1, /* mode */ newModeToLegacyMode(change.mode), /* leash */ createLeash(info, change, order, t), @@ -160,6 +160,8 @@ class RemoteTransitionAdapter { /* taskInfo */ change.taskInfo, /* allowEnterPip */ change.allowEnterPip, /* windowType */ WindowManager.LayoutParams.INVALID_WINDOW_TYPE) + target.backgroundColor = change.backgroundColor + return target } /** diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt index d15b8c169535..ca557796462f 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt @@ -37,7 +37,6 @@ class ViewHierarchyAnimator { private const val DEFAULT_DURATION = 500L private val DEFAULT_INTERPOLATOR = Interpolators.STANDARD private val DEFAULT_ADDITION_INTERPOLATOR = Interpolators.STANDARD_DECELERATE - private val DEFAULT_BOUNDS = setOf(Bound.LEFT, Bound.TOP, Bound.RIGHT, Bound.BOTTOM) /** The properties used to animate the view bounds. */ private val PROPERTIES = mapOf( @@ -61,8 +60,7 @@ class ViewHierarchyAnimator { /** * Instruct the animator to watch for changes to the layout of [rootView] and its children - * and animate them. The animation can be limited to a subset of [bounds]. It uses the - * given [interpolator] and [duration]. + * and animate them. It uses the given [interpolator] and [duration]. * * If a new layout change happens while an animation is already in progress, the animation * is updated to continue from the current values to the new end state. @@ -74,40 +72,31 @@ class ViewHierarchyAnimator { * * Returns true if the [rootView] is already visible and will be animated, false otherwise. * To animate the addition of a view, see [animateAddition]. - * - * TODO(b/221418522): remove the ability to select which bounds to animate and always - * animate all of them. */ @JvmOverloads fun animate( rootView: View, - bounds: Set<Bound> = DEFAULT_BOUNDS, interpolator: Interpolator = DEFAULT_INTERPOLATOR, duration: Long = DEFAULT_DURATION ): Boolean { - return animate(rootView, bounds, interpolator, duration, ephemeral = false) + return animate(rootView, interpolator, duration, ephemeral = false) } /** * Like [animate], but only takes effect on the next layout update, then unregisters itself * once the first animation is complete. - * - * TODO(b/221418522): remove the ability to select which bounds to animate and always - * animate all of them. */ @JvmOverloads fun animateNextUpdate( rootView: View, - bounds: Set<Bound> = DEFAULT_BOUNDS, interpolator: Interpolator = DEFAULT_INTERPOLATOR, duration: Long = DEFAULT_DURATION ): Boolean { - return animate(rootView, bounds, interpolator, duration, ephemeral = true) + return animate(rootView, interpolator, duration, ephemeral = true) } private fun animate( rootView: View, - bounds: Set<Bound>, interpolator: Interpolator, duration: Long, ephemeral: Boolean @@ -123,26 +112,24 @@ class ViewHierarchyAnimator { return false } - val listener = createUpdateListener(bounds, interpolator, duration, ephemeral) + val listener = createUpdateListener(interpolator, duration, ephemeral) recursivelyAddListener(rootView, listener) return true } /** * Returns a new [View.OnLayoutChangeListener] that when called triggers a layout animation - * for the specified [bounds], using [interpolator] and [duration]. + * using [interpolator] and [duration]. * * If [ephemeral] is true, the listener is unregistered after the first animation. Otherwise * it keeps listening for further updates. */ private fun createUpdateListener( - bounds: Set<Bound>, interpolator: Interpolator, duration: Long, ephemeral: Boolean ): View.OnLayoutChangeListener { return createListener( - bounds, interpolator, duration, ephemeral @@ -156,17 +143,7 @@ class ViewHierarchyAnimator { * Any animations already in progress continue until their natural conclusion. */ fun stopAnimating(rootView: View) { - val listener = rootView.getTag(R.id.tag_layout_listener) - if (listener != null && listener is View.OnLayoutChangeListener) { - rootView.setTag(R.id.tag_layout_listener, null /* tag */) - rootView.removeOnLayoutChangeListener(listener) - } - - if (rootView is ViewGroup) { - for (i in 0 until rootView.childCount) { - stopAnimating(rootView.getChildAt(i)) - } - } + recursivelyRemoveListener(rootView) } /** @@ -224,7 +201,6 @@ class ViewHierarchyAnimator { ignorePreviousValues: Boolean ): View.OnLayoutChangeListener { return createListener( - DEFAULT_BOUNDS, interpolator, duration, ephemeral = true, @@ -235,7 +211,7 @@ class ViewHierarchyAnimator { /** * Returns a new [View.OnLayoutChangeListener] that when called triggers a layout animation - * for the specified [bounds], using [interpolator] and [duration]. + * using [interpolator] and [duration]. * * If [ephemeral] is true, the listener is unregistered after the first animation. Otherwise * it keeps listening for further updates. @@ -244,7 +220,6 @@ class ViewHierarchyAnimator { * [ignorePreviousValues] controls whether the previous values should be taken into account. */ private fun createListener( - bounds: Set<Bound>, interpolator: Interpolator, duration: Long, ephemeral: Boolean, @@ -300,10 +275,11 @@ class ViewHierarchyAnimator { ) val boundsToAnimate = mutableSetOf<Bound>() - bounds.forEach { bound -> - if (endValues.getValue(bound) != startValues.getValue(bound)) { - boundsToAnimate.add(bound) - } + if (startValues.getValue(Bound.LEFT) != left) boundsToAnimate.add(Bound.LEFT) + if (startValues.getValue(Bound.TOP) != top) boundsToAnimate.add(Bound.TOP) + if (startValues.getValue(Bound.RIGHT) != right) boundsToAnimate.add(Bound.RIGHT) + if (startValues.getValue(Bound.BOTTOM) != bottom) { + boundsToAnimate.add(Bound.BOTTOM) } if (boundsToAnimate.isNotEmpty()) { @@ -462,6 +438,20 @@ class ViewHierarchyAnimator { } } + private fun recursivelyRemoveListener(view: View) { + val listener = view.getTag(R.id.tag_layout_listener) + if (listener != null && listener is View.OnLayoutChangeListener) { + view.setTag(R.id.tag_layout_listener, null /* tag */) + view.removeOnLayoutChangeListener(listener) + } + + if (view is ViewGroup) { + for (i in 0 until view.childCount) { + recursivelyRemoveListener(view.getChildAt(i)) + } + } + } + private fun getBound(view: View, bound: Bound): Int? { return view.getTag(bound.overrideTag) as? Int } @@ -513,11 +503,10 @@ class ViewHierarchyAnimator { // When an animation is cancelled, a new one might be taking over. We shouldn't // unregister the listener yet. if (ephemeral && !cancelled) { - val listener = view.getTag(R.id.tag_layout_listener) - if (listener != null && listener is View.OnLayoutChangeListener) { - view.setTag(R.id.tag_layout_listener, null /* tag */) - view.removeOnLayoutChangeListener(listener) - } + // The duration is the same for the whole hierarchy, so it's safe to remove + // the listener recursively. We do this because some descendant views might + // not change bounds, and therefore not animate and leak the listener. + recursivelyRemoveListener(view) } } @@ -538,8 +527,7 @@ class ViewHierarchyAnimator { CENTER, LEFT, TOP_LEFT, TOP, TOP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT } - // TODO(b/221418522): make this private once it can't be passed as an arg anymore. - enum class Bound(val label: String, val overrideTag: Int) { + private enum class Bound(val label: String, val overrideTag: Int) { LEFT("left", R.id.tag_override_left) { override fun setValue(view: View, value: Int) { view.left = value diff --git a/packages/SystemUI/docs/keyguard/bouncer.md b/packages/SystemUI/docs/device-entry/bouncer.md index 4bfe7340db30..589cb5d300d3 100644 --- a/packages/SystemUI/docs/keyguard/bouncer.md +++ b/packages/SystemUI/docs/device-entry/bouncer.md @@ -2,6 +2,8 @@ [KeyguardBouncer][1] is the component responsible for displaying the security method set by the user (password, PIN, pattern) as well as SIM-related security methods, allowing the user to unlock the device or SIM. + + ## Supported States 1. Phone, portrait mode - The default and typically only way to view the bouncer. Screen cannot rotate. diff --git a/packages/SystemUI/docs/keyguard/doze.md b/packages/SystemUI/docs/device-entry/doze.md index a6ccab9698d4..5ff8851b4c69 100644 --- a/packages/SystemUI/docs/keyguard/doze.md +++ b/packages/SystemUI/docs/device-entry/doze.md @@ -2,6 +2,8 @@ Always-on Display (AOD) provides an alternative 'screen-off' experience. Instead, of completely turning the display off, it provides a distraction-free, glanceable experience for the phone in a low-powered mode. In this low-powered mode, the display will have a lower refresh rate and the UI should frequently shift its displayed contents in order to prevent burn-in. The recommended max on-pixel-ratio (OPR) is 5% to reduce battery consumption. + + The default doze component controls AOD and is specified by `config_dozeComponent` in the [framework config][1]. SystemUI provides a default Doze Component: [DozeService][2]. [DozeService][2] builds a [DozeMachine][3] with dependencies specified in [DozeModule][4] and configurations in [AmbientDisplayConfiguration][13] and [DozeParameters][14]. Note: The default UI used in AOD shares views with the Lock Screen and does not create its own new views. Once dozing begins, [DozeUI][17] informs SystemUI's [DozeServiceHost][18] that dozing has begun - which sends this signal to relevant SystemUI Lock Screen views to animate accordingly. Within SystemUI, [StatusBarStateController][19] #isDozing and #getDozeAmount can be used to query dozing state. diff --git a/packages/SystemUI/docs/device-entry/glossary.md b/packages/SystemUI/docs/device-entry/glossary.md new file mode 100644 index 000000000000..f3d12c21a3a5 --- /dev/null +++ b/packages/SystemUI/docs/device-entry/glossary.md @@ -0,0 +1,48 @@ +# Device Entry Glossary + +## Keyguard + +| Term | Description | +| :-----------: | ----------- | +| Keyguard, [keyguard.md][1] | Coordinates the first experience when turning on the display of a device, as long as the user has not specified a security method of NONE. Consists of the lock screen and bouncer.| +| Lock screen<br><br>| The first screen available when turning on the display of a device, as long as the user has not specified a security method of NONE. On the lock screen, users can access:<ul><li>Quick Settings - users can swipe down from the top of the screen to interact with quick settings tiles</li><li>[Keyguard Status Bar][9] - This special status bar shows SIM related information and system icons.</li><li>Clock - uses the font specified at [clock.xml][8]. If the clock font supports variable weights, users will experience delightful clock weight animations - in particular, on transitions between the lock screen and AOD.</li><li>Notifications - ability to view and interact with notifications depending on user lock screen notification settings: `Settings > Display > Lock screen > Privacy`</li><li>Message area - contains device information like biometric errors, charging information and device policy information. Also includes user configured information from `Settings > Display > Lock screen > Add text on lock screen`. </li><li>Bouncer - if the user has a primary authentication method, they can swipe up from the bottom of the screen to bring up the bouncer.</li></ul>The lock screen is one state of the notification shade. See [StatusBarState#KEYGUARD][10] and [StatusBarState#SHADE_LOCKED][10].| +| Bouncer, [bouncer.md][2]<br><br>| The component responsible for displaying the primary security method set by the user (password, PIN, pattern). The bouncer can also show SIM-related security methods, allowing the user to unlock the device or SIM.| +| Split shade | State of the shade (which keyguard is a part of) in which notifications are on the right side and Quick Settings on the left. For keyguard that means notifications being on the right side and clock with media being on the left.<br><br>Split shade is automatically activated - using resources - for big screens in landscape, see [sw600dp-land/config.xml][3] `config_use_split_notification_shade`.<br><br>In that state we can see the big clock more often - every time when media is not visible on the lock screen. When there is no media and no notifications - or we enter AOD - big clock is always positioned in the center of the screen.<br><br>The magic of positioning views happens by changing constraints of [NotificationsQuickSettingsContainer][4] and positioning elements vertically in [KeyguardClockPositionAlgorithm][5]| +| Ambient display (AOD), [doze.md][6]<br><br>| UI shown when the device is in a low-powered display state. This is controlled by the doze component. The same lock screen views (ie: clock, notification shade) are used on AOD. The AOSP image on the left shows the usage of a clock that does not support variable weights which is why the clock is thicker in that image than what users see on Pixel devices.| + +## General Authentication Terms +| Term | Description | +| ----------- | ----------- | +| Primary Authentication | The strongest form of authentication. Includes: Pin, pattern and password input.| +| Biometric Authentication | Face or fingerprint input. Biometric authentication is categorized into different classes of security. See [Measuring Biometric Security][7].| + +## Face Authentication Terms +| Term | Description | +| ----------- | ----------- | +| Passive Authentication | When a user hasn't explicitly requested an authentication method; however, it may still put the device in an unlocked state.<br><br>For example, face authentication is triggered immediately when waking the device; however, users may not have the intent of unlocking their device. Instead, they could have wanted to just check the lock screen. Because of this, SystemUI provides the option for a bypass OR non-bypass face authentication experience which have different user flows.<br><br>In contrast, fingerprint authentication is considered an active authentication method since users need to actively put their finger on the fingerprint sensor to authenticate. Therefore, it's an explicit request for authentication and SystemUI knows the user has the intent for device-entry.| +| Bypass | Used to refer to the face authentication bypass device entry experience. We have this distinction because face auth is a passive authentication method (see above).| +| Bypass User Journey <br><br>| Once the user successfully authenticates with face, the keyguard immediately dismisses and the user is brought to the home screen/last app. This CUJ prioritizes speed of device entry. SystemUI hides interactive views (notifications) on the lock screen to avoid putting users in a state where the lock screen could immediately disappear while they're interacting with affordances on the lock screen.| +| Non-bypass User Journey | Once the user successfully authenticates with face, the device remains on keyguard until the user performs an action to indicate they'd like to enter the device (ie: swipe up on the lock screen or long press on the unlocked icon). This CUJ prioritizes notification visibility.| + +## Fingerprint Authentication Terms +| Term | Description | +| ----------- | ----------- | +| Under-display fingerprint sensor (UDFPS) | References the HW affordance for a fingerprint sensor that is under the display, which requires a software visual affordance. System UI supports showing the UDFPS affordance on the lock screen and on AOD. Users cannot authenticate from the screen-off state.<br><br>Supported SystemUI CUJs include:<ul><li> sliding finger on the screen to the UDFPS area to being authentication (as opposed to directly placing finger in the UDFPS area) </li><li> when a11y services are enabled, there is a haptic played when a touch is detected on UDFPS</li><li>after two hard-fingerprint-failures, the primary authentication bouncer is shown</li><li> when tapping on an affordance that requests to dismiss the lock screen, the user may see the UDFPS icon highlighted - see UDFPS bouncer</li></ul>| +| UDFPS Bouncer | UI that highlights the UDFPS sensor. Users can get into this state after tapping on a notification from the lock screen or locked expanded shade.| + +## Other Authentication Terms +| Term | Description | +| ---------- | ----------- | +| Trust Agents | Provides signals to the keyguard to allow it to lock less frequently.| + + +[1]: /frameworks/base/packages/SystemUI/docs/device-entry/keyguard.md +[2]: /frameworks/base/packages/SystemUI/docs/device-entry/bouncer.md +[3]: /frameworks/base/packages/SystemUI/res/values-sw600dp-land/config.xml +[4]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java +[5]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +[6]: /frameworks/base/packages/SystemUI/docs/device-entry/doze.md +[7]: https://source.android.com/security/biometric/measure +[8]: /frameworks/base/packages/SystemUI/res-keyguard/font/clock.xml +[9]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +[10]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java diff --git a/packages/SystemUI/docs/device-entry/imgs/aod.png b/packages/SystemUI/docs/device-entry/imgs/aod.png Binary files differnew file mode 100644 index 000000000000..abd554a8936f --- /dev/null +++ b/packages/SystemUI/docs/device-entry/imgs/aod.png diff --git a/packages/SystemUI/docs/device-entry/imgs/bouncer_pin.png b/packages/SystemUI/docs/device-entry/imgs/bouncer_pin.png Binary files differnew file mode 100644 index 000000000000..da15e4115ab9 --- /dev/null +++ b/packages/SystemUI/docs/device-entry/imgs/bouncer_pin.png diff --git a/packages/SystemUI/docs/device-entry/imgs/bypass.png b/packages/SystemUI/docs/device-entry/imgs/bypass.png Binary files differnew file mode 100644 index 000000000000..f4cbd3efb6fc --- /dev/null +++ b/packages/SystemUI/docs/device-entry/imgs/bypass.png diff --git a/packages/SystemUI/docs/device-entry/imgs/lockscreen.png b/packages/SystemUI/docs/device-entry/imgs/lockscreen.png Binary files differnew file mode 100644 index 000000000000..d1fe0853f578 --- /dev/null +++ b/packages/SystemUI/docs/device-entry/imgs/lockscreen.png diff --git a/packages/SystemUI/docs/keyguard.md b/packages/SystemUI/docs/device-entry/keyguard.md index 8914042ee3cd..337f73b79260 100644 --- a/packages/SystemUI/docs/keyguard.md +++ b/packages/SystemUI/docs/device-entry/keyguard.md @@ -30,6 +30,12 @@ An indication to power off the device most likely comes from one of two signals: ### How the device locks +## Debugging Tips +Enable verbose keyguard logs that will print to logcat. Should only be used temporarily for debugging. See [KeyguardConstants][5]. +``` +adb shell setprop log.tag.Keyguard DEBUG && adb shell am crash com.android.systemui +``` + More coming * Screen timeout * Smart lock @@ -38,9 +44,8 @@ More coming * Lock timeout after screen timeout setting -[1]: /frameworks/base/packages/SystemUI/docs/keyguard/bouncer.md -[2]: /frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java -[3]: /frameworks/base/packages/SystemUI/docs/keyguard/doze.md -[4]: /frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java - - +[1]: /frameworks/base/packages/SystemUI/docs/device-entry/bouncer.md +[2]: /com/android/server/power/PowerManagerService.java +[3]: /frameworks/base/packages/SystemUI/docs/device-entry/doze.md +[4]: /com/android/server/policy/PhoneWindowManager.java +[5]: /frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java diff --git a/packages/SystemUI/docs/user-switching.md b/packages/SystemUI/docs/user-switching.md index dcf66b943f1d..b9509eb41c3c 100644 --- a/packages/SystemUI/docs/user-switching.md +++ b/packages/SystemUI/docs/user-switching.md @@ -37,7 +37,7 @@ A fullscreen user switching activity, supporting add guest/user actions if confi Renders user switching as a dialog over the current surface, and supports add guest user/actions if configured. -[1]: /frameworks/base/packages/SystemUI/docs/keyguard/bouncer.md +[1]: /frameworks/base/packages/SystemUI/docs/device-entry/bouncer.md [2]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserController.java [3]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java [4]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java diff --git a/packages/SystemUI/res/drawable/ic_media_connecting_container.xml b/packages/SystemUI/res/drawable/ic_media_connecting_container.xml new file mode 100644 index 000000000000..79d2a0601e08 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_media_connecting_container.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G"
+ android:translateX="24"
+ android:translateY="24"
+ android:scaleX="0.5"
+ android:scaleY="0.5"/>
+ <group android:name="_R_G_L_0_G"
+ android:translateX="24"
+ android:translateY="24"
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffddb3"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "/>
+ </group>
+ </group>
+</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml index 8de80844d784..c60609b06d38 100644 --- a/packages/SystemUI/res/layout/screenshot_static.xml +++ b/packages/SystemUI/res/layout/screenshot_static.xml @@ -98,6 +98,7 @@ android:scaleType="fitEnd" android:background="@drawable/overlay_preview_background" android:adjustViewBounds="true" + android:clickable="true" app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border" app:layout_constraintStart_toStartOf="@id/screenshot_preview_border" app:layout_constraintEnd_toEndOf="@id/screenshot_preview_border" diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml index b6de8738f889..79d29ab7b11e 100644 --- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml @@ -144,7 +144,7 @@ <string-array name="tile_states_mictoggle"> <item msgid="6895831614067195493">"Hakipatikani"</item> <item msgid="3296179158646568218">"Kimezimwa"</item> - <item msgid="8998632451221157987">"Kimewashwa"</item> + <item msgid="8998632451221157987">"Imewashwa"</item> </string-array> <string-array name="tile_states_controls"> <item msgid="8199009425335668294">"Hakipatikani"</item> diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml index 129b61017b11..827c72ae2867 100644 --- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml @@ -157,7 +157,7 @@ <item msgid="6866424167599381915">"Đang bật"</item> </string-array> <string-array name="tile_states_qr_code_scanner"> - <item msgid="7435143266149257618">"Không dùng được"</item> + <item msgid="7435143266149257618">"Không hoạt động"</item> <item msgid="3301403109049256043">"Đang tắt"</item> <item msgid="8878684975184010135">"Đang bật"</item> </string-array> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 926734c2749f..ff71b4f5e405 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -23,6 +23,7 @@ <item type="id" name="scale_x_animator_tag"/> <item type="id" name="scale_y_animator_tag"/> <item type="id" name="top_inset_animator_tag"/> + <item type="id" name="bottom_inset_animator_tag"/> <item type="id" name="height_animator_tag"/> <item type="id" name="x_animator_tag"/> <item type="id" name="y_animator_tag"/> @@ -33,6 +34,7 @@ <item type="id" name="scale_y_animator_end_value_tag"/> <item type="id" name="alpha_animator_end_value_tag"/> <item type="id" name="top_inset_animator_end_value_tag"/> + <item type="id" name="bottom_inset_animator_end_value_tag"/> <item type="id" name="height_animator_end_value_tag"/> <item type="id" name="x_animator_tag_end_value"/> <item type="id" name="y_animator_tag_end_value"/> @@ -43,6 +45,7 @@ <item type="id" name="scale_y_animator_start_value_tag"/> <item type="id" name="alpha_animator_start_value_tag"/> <item type="id" name="top_inset_animator_start_value_tag"/> + <item type="id" name="bottom_inset_animator_start_value_tag"/> <item type="id" name="height_animator_start_value_tag"/> <item type="id" name="x_animator_tag_start_value"/> <item type="id" name="y_animator_tag_start_value"/> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 33ed7b8421db..b248efe93e98 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2199,6 +2199,8 @@ <string name="controls_media_button_prev">Previous track</string> <!-- Description for button in media controls. Pressing button goes to next track [CHAR_LIMIT=NONE] --> <string name="controls_media_button_next">Next track</string> + <!-- Description for button in media controls. Used when media is connecting to a remote device (via something like chromecast). Pressing button does nothing [CHAR_LIMIT=NONE] --> + <string name="controls_media_button_connecting">Connecting</string> <!-- Title for Smartspace recommendation card within media controls. The "Play" means the action to play a media [CHAR_LIMIT=10] --> <string name="controls_media_smartspace_rec_title">Play</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index 120b09a2ad31..de35514be2cb 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -41,6 +41,7 @@ import android.os.Parcelable; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; +import android.util.SparseArray; import android.view.IRecentsAnimationController; import android.view.SurfaceControl; import android.window.IRemoteTransition; @@ -244,22 +245,28 @@ public class RemoteTransitionCompat implements Parcelable { @SuppressLint("NewApi") boolean merge(TransitionInfo info, SurfaceControl.Transaction t, RecentsAnimationListener recents) { - ArrayList<TransitionInfo.Change> openingTasks = null; + SparseArray<TransitionInfo.Change> openingTasks = null; boolean cancelRecents = false; boolean homeGoingAway = false; boolean hasChangingApp = false; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) { - if (change.getTaskInfo() != null) { - if (change.getTaskInfo().topActivityType == ACTIVITY_TYPE_HOME) { + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); + if (taskInfo != null) { + if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { // canceling recents animation cancelRecents = true; } if (openingTasks == null) { - openingTasks = new ArrayList<>(); + openingTasks = new SparseArray<>(); + } + if (taskInfo.hasParentTask()) { + // Collects opening leaf tasks only since Launcher monitors leaf task + // ids to perform recents animation. + openingTasks.remove(taskInfo.parentTaskId); } - openingTasks.add(change); + openingTasks.put(taskInfo.taskId, change); } } else if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) { @@ -287,7 +294,7 @@ public class RemoteTransitionCompat implements Parcelable { int pauseMatches = 0; if (!cancelRecents) { for (int i = 0; i < openingTasks.size(); ++i) { - if (mPausingTasks.contains(openingTasks.get(i).getContainer())) { + if (mPausingTasks.contains(openingTasks.valueAt(i).getContainer())) { ++pauseMatches; } } @@ -308,10 +315,11 @@ public class RemoteTransitionCompat implements Parcelable { final RemoteAnimationTargetCompat[] targets = new RemoteAnimationTargetCompat[openingTasks.size()]; for (int i = 0; i < openingTasks.size(); ++i) { - mOpeningLeashes.add(openingTasks.get(i).getLeash()); + final TransitionInfo.Change change = openingTasks.valueAt(i); + mOpeningLeashes.add(change.getLeash()); // We are receiving new opening tasks, so convert to onTasksAppeared. final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat( - openingTasks.get(i), layer, info, t); + change, layer, info, t); mLeashMap.put(mOpeningLeashes.get(i), target.leash); t.reparent(target.leash, mInfo.getRootLeash()); t.setLayer(target.leash, layer); diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt index ad8c126aa2fa..19d39d515325 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt @@ -22,6 +22,7 @@ import android.annotation.IntRange import android.annotation.SuppressLint import android.content.Context import android.graphics.Canvas +import android.text.TextUtils import android.text.format.DateFormat import android.util.AttributeSet import android.util.Log @@ -125,13 +126,30 @@ class AnimatableClockView @JvmOverloads constructor( fun refreshTime() { time.timeInMillis = System.currentTimeMillis() - text = DateFormat.format(format, time) contentDescription = DateFormat.format(descFormat, time) - Log.d(tag, "refreshTime this=$this" + - " currTimeContextDesc=$contentDescription" + - " measuredHeight=$measuredHeight" + - " lastMeasureCall=$lastMeasureCall" + - " isSingleLineInternal=$isSingleLineInternal") + val formattedText = DateFormat.format(format, time) + // Setting text actually triggers a layout pass (because the text view is set to + // wrap_content width and TextView always relayouts for this). Avoid needless + // relayout if the text didn't actually change. + if (!TextUtils.equals(text, formattedText)) { + text = formattedText + Log.d( + tag, "refreshTime this=$this" + + " currTimeContextDesc=$contentDescription" + + " measuredHeight=$measuredHeight" + + " lastMeasureCall=$lastMeasureCall" + + " isSingleLineInternal=$isSingleLineInternal" + ) + } else { + Log.d( + tag, "refreshTime (skipped due to unchanged text)" + + " this=$this" + + " currTimeContextDesc=$contentDescription" + + " measuredHeight=$measuredHeight" + + " lastMeasureCall=$lastMeasureCall" + + " isSingleLineInternal=$isSingleLineInternal" + ) + } } fun onTimeZoneChanged(timeZone: TimeZone?) { diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index 2b0c083e2f31..f444b373947f 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -32,6 +32,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.provider.Settings; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; @@ -267,8 +268,15 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { if (mBatteryPercentView == null) { return; } - mBatteryPercentView.setText( - NumberFormat.getPercentInstance().format(mLevel / 100f)); + + String percentText = NumberFormat.getPercentInstance().format(mLevel / 100f); + // Setting text actually triggers a layout pass (because the text view is set to + // wrap_content width and TextView always relayouts for this). Avoid needless + // relayout if the text didn't actually change. + if (!TextUtils.equals(mBatteryPercentView.getText(), percentText)) { + mBatteryPercentView.setText(percentText); + } + setContentDescription( getContext().getString(mCharging ? R.string.accessibility_battery_level_charging : R.string.accessibility_battery_level, mLevel)); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 63b2b201c498..2ac240885faa 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -376,7 +376,6 @@ public class UdfpsController implements DozeReceiver { boolean withinSensorArea = isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView); if (withinSensorArea) { - mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE); Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0); Log.v(TAG, "onTouch | action down"); // The pointer that causes ACTION_DOWN is always at index 0. @@ -792,6 +791,7 @@ public class UdfpsController implements DozeReceiver { + " current: " + mOverlay.getRequestId()); return; } + mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE); if (!mOnFingerDown) { playStartHaptic(); @@ -806,11 +806,9 @@ public class UdfpsController implements DozeReceiver { final UdfpsView view = mOverlay.getOverlayView(); if (view != null) { - Trace.beginAsyncSection("UdfpsController.e2e.startIllumination", 0); view.startIllumination(() -> { mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId); mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE); - Trace.endAsyncSection("UdfpsController.e2e.startIllumination", 0); }); } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java index f710d0154060..0d8987988f0a 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java @@ -95,7 +95,7 @@ public class EditTextActivity extends Activity private void share() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, mEditText.getText()); + sendIntent.putExtra(Intent.EXTRA_TEXT, mEditText.getText().toString()); sendIntent.setType("text/plain"); Intent shareIntent = Intent.createChooser(sendIntent, null); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index 6d727b4cf966..b172e92871ce 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -65,6 +65,7 @@ import android.safetycenter.SafetyCenterManager; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.telecom.TelecomManager; +import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.view.CrossWindowBlurListeners; @@ -452,6 +453,12 @@ public class FrameworkServicesModule { @Provides @Singleton + static CarrierConfigManager provideCarrierConfigManager(Context context) { + return context.getSystemService(CarrierConfigManager.class); + } + + @Provides + @Singleton static WindowManager provideWindowManager(Context context) { return context.getSystemService(WindowManager.class); } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index c9a61a8a09df..44580aa4230a 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -162,6 +162,17 @@ public class Flags { public static final SysPropBooleanFlag WM_ENABLE_SHELL_TRANSITIONS = new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false); + // 1200 - predictive back + @Keep + public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag( + 1200, "persist.wm.debug.predictive_back", true); + @Keep + public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK_ANIM = new SysPropBooleanFlag( + 1201, "persist.wm.debug.predictive_back_anim", false); + @Keep + public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK = + new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false); + // Pay no attention to the reflection behind the curtain. // ========================== Curtain ========================== // | | diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 96ae646ac7f9..290bf0d0734c 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -41,26 +41,23 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; -import dagger.Lazy; - public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks { private final Context mContext; - private final Lazy<GlobalActionsDialogLite> mGlobalActionsDialogLazy; private final KeyguardStateController mKeyguardStateController; private final DeviceProvisionedController mDeviceProvisionedController; private final BlurUtils mBlurUtils; private final CommandQueue mCommandQueue; - private GlobalActionsDialogLite mGlobalActionsDialog; + private final GlobalActionsDialogLite mGlobalActionsDialog; private boolean mDisabled; @Inject public GlobalActionsImpl(Context context, CommandQueue commandQueue, - Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils, + GlobalActionsDialogLite globalActionsDialog, BlurUtils blurUtils, KeyguardStateController keyguardStateController, DeviceProvisionedController deviceProvisionedController) { mContext = context; - mGlobalActionsDialogLazy = globalActionsDialogLazy; + mGlobalActionsDialog = globalActionsDialog; mKeyguardStateController = keyguardStateController; mDeviceProvisionedController = deviceProvisionedController; mCommandQueue = commandQueue; @@ -71,16 +68,12 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks @Override public void destroy() { mCommandQueue.removeCallback(this); - if (mGlobalActionsDialog != null) { - mGlobalActionsDialog.destroy(); - mGlobalActionsDialog = null; - } + mGlobalActionsDialog.destroy(); } @Override public void showGlobalActions(GlobalActionsManager manager) { if (mDisabled) return; - mGlobalActionsDialog = mGlobalActionsDialogLazy.get(); mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(), mDeviceProvisionedController.isDeviceProvisioned(), null /* view */); } @@ -189,7 +182,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0; if (displayId != mContext.getDisplayId() || disabled == mDisabled) return; mDisabled = disabled; - if (disabled && mGlobalActionsDialog != null) { + if (disabled) { mGlobalActionsDialog.dismissDialog(); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index b93239ba7197..d674b2b85db4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -690,6 +690,11 @@ class MediaCarouselController @Inject constructor( startDelay: Long = 0 ) { desiredHostState?.let { + if (this.desiredLocation != desiredLocation) { + // Only log an event when location changes + logger.logCarouselPosition(desiredLocation) + } + // This is a hosting view, let's remeasure our players this.desiredLocation = desiredLocation this.desiredHostState = it diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 5ead375a1084..3a727ba7b70c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -333,7 +333,7 @@ public class MediaControlPanel { mMediaViewHolder.getPlayer().setOnClickListener(v -> { if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return; if (mMediaViewController.isGutsVisible()) return; - + mLogger.logTapContentView(mUid, mPackageName, mInstanceId); logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT); mActivityStarter.postStartActivityDismissingKeyguard(clickIntent, buildLaunchAnimatorController(mMediaViewHolder.getPlayer())); @@ -660,38 +660,42 @@ public class MediaControlPanel { final ImageButton button, MediaAction mediaAction, ConstraintSet collapsedSet, ConstraintSet expandedSet, boolean showInCompact) { - animHandler.unregisterAll(); if (mediaAction != null) { - final Drawable icon = mediaAction.getIcon(); - button.setImageDrawable(icon); - button.setContentDescription(mediaAction.getContentDescription()); - final Drawable bgDrawable = mediaAction.getBackground(); - button.setBackground(bgDrawable); - - animHandler.tryRegister(icon); - animHandler.tryRegister(bgDrawable); - - Runnable action = mediaAction.getAction(); - if (action == null) { - button.setEnabled(false); - } else { - button.setEnabled(true); - button.setOnClickListener(v -> { - if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { - mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId); - logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT); - action.run(); - - if (icon instanceof Animatable) { - ((Animatable) icon).start(); - } - if (bgDrawable instanceof Animatable) { - ((Animatable) bgDrawable).start(); + if (animHandler.updateRebindId(mediaAction.getRebindId())) { + animHandler.unregisterAll(); + + final Drawable icon = mediaAction.getIcon(); + button.setImageDrawable(icon); + button.setContentDescription(mediaAction.getContentDescription()); + final Drawable bgDrawable = mediaAction.getBackground(); + button.setBackground(bgDrawable); + + animHandler.tryRegister(icon); + animHandler.tryRegister(bgDrawable); + + Runnable action = mediaAction.getAction(); + if (action == null) { + button.setEnabled(false); + } else { + button.setEnabled(true); + button.setOnClickListener(v -> { + if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId); + logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT); + action.run(); + + if (icon instanceof Animatable) { + ((Animatable) icon).start(); + } + if (bgDrawable instanceof Animatable) { + ((Animatable) bgDrawable).start(); + } } - } - }); + }); + } } } else { + animHandler.unregisterAll(); button.setImageDrawable(null); button.setContentDescription(null); button.setEnabled(false); @@ -702,9 +706,29 @@ public class MediaControlPanel { setVisibleAndAlpha(expandedSet, button.getId(), mediaAction != null); } + // AnimationBindHandler is responsible for tracking the bound animation state and preventing + // jank and conflicts due to media notifications arriving at any time during an animation. It + // does this in two parts. + // - Exit animations fired as a result of user input are tracked. When these are running, any + // bind actions are delayed until the animation completes (and then fired in sequence). + // - Continuous animations are tracked using their rebind id. Later calls using the same + // rebind id will be totally ignored to prevent the continuous animation from restarting. private static class AnimationBindHandler extends Animatable2.AnimationCallback { private ArrayList<Runnable> mOnAnimationsComplete = new ArrayList<>(); private ArrayList<Animatable2> mRegistrations = new ArrayList<>(); + private Integer mRebindId = null; + + // This check prevents rebinding to the action button if the identifier has not changed. A + // null value is always considered to be changed. This is used to prevent the connecting + // animation from rebinding (and restarting) if multiple buffer PlaybackStates are pushed by + // an application in a row. + public boolean updateRebindId(Integer rebindId) { + if (mRebindId == null || rebindId == null || !mRebindId.equals(rebindId)) { + mRebindId = rebindId; + return true; + } + return false; + } public void tryRegister(Drawable drawable) { if (drawable instanceof Animatable2) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt index a4d2f7bc96c4..bc8cca55154d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt @@ -184,7 +184,12 @@ data class MediaAction( val icon: Drawable?, val action: Runnable?, val contentDescription: CharSequence?, - val background: Drawable? + val background: Drawable?, + + // Rebind Id is used to detect identical rebinds and ignore them. It is intended + // to prevent continuously looping animations from restarting due to the arrival + // of repeated media notifications that are visually identical. + val rebindId: Int? = null ) /** State of the media device. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 908aef41034e..57c93bae3bbf 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -30,6 +30,7 @@ import android.content.IntentFilter import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.ImageDecoder +import android.graphics.drawable.Animatable import android.graphics.drawable.Icon import android.media.MediaDescription import android.media.MediaMetadata @@ -57,6 +58,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.BcSmartspaceDataPlugin import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState +import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState import com.android.systemui.statusbar.notification.row.HybridGroupManager import com.android.systemui.tuner.TunerService import com.android.systemui.util.Assert @@ -777,7 +779,20 @@ class MediaDataManager( val actions = MediaButton() controller.playbackState?.let { state -> // First, check for standard actions - actions.playOrPause = if (isPlayingState(state.state)) { + actions.playOrPause = if (isConnectingState(state.state)) { + // Spinner needs to be animating to render anything. Start it here. + val drawable = context.getDrawable( + com.android.internal.R.drawable.progress_small_material) + (drawable as Animatable).start() + MediaAction( + drawable, + null, // no action to perform when clicked + context.getString(R.string.controls_media_button_connecting), + context.getDrawable(R.drawable.ic_media_connecting_container), + // Specify a rebind id to prevent the spinner from restarting on later binds. + com.android.internal.R.drawable.progress_small_material + ) + } else if (isPlayingState(state.state)) { getStandardAction(controller, state.actions, PlaybackState.ACTION_PAUSE) } else { getStandardAction(controller, state.actions, PlaybackState.ACTION_PLAY) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index 824a6fd9d96e..d6231911244f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -164,7 +164,7 @@ class MediaDeviceManager @Inject constructor( } // A device that is not yet connected but is expected to connect imminently. Because it's // expected to connect imminently, it should be displayed as the current device. - private var aboutToConnectDeviceOverride: MediaDeviceData? = null + private var aboutToConnectDeviceOverride: AboutToConnectDevice? = null @AnyThread fun start() = bgExecutor.execute { @@ -222,22 +222,34 @@ class MediaDeviceManager @Inject constructor( } } - override fun onAboutToConnectDeviceChanged(deviceName: String?, deviceIcon: Drawable?) { - aboutToConnectDeviceOverride = if (deviceName == null || deviceIcon == null) { - null - } else { - MediaDeviceData(enabled = true, deviceIcon, deviceName) - } + override fun onAboutToConnectDeviceAdded( + deviceAddress: String, + deviceName: String, + deviceIcon: Drawable? + ) { + aboutToConnectDeviceOverride = AboutToConnectDevice( + fullMediaDevice = localMediaManager.getMediaDeviceById(deviceAddress), + backupMediaDeviceData = MediaDeviceData(enabled = true, deviceIcon, deviceName) + ) + updateCurrent() + } + + override fun onAboutToConnectDeviceRemoved() { + aboutToConnectDeviceOverride = null updateCurrent() } @WorkerThread private fun updateCurrent() { - if (aboutToConnectDeviceOverride != null) { - current = aboutToConnectDeviceOverride - return + val aboutToConnect = aboutToConnectDeviceOverride + if (aboutToConnect != null && + aboutToConnect.fullMediaDevice == null && + aboutToConnect.backupMediaDeviceData != null) { + // Only use [backupMediaDeviceData] when we don't have [fullMediaDevice]. + current = aboutToConnect.backupMediaDeviceData + return } - val device = localMediaManager.currentConnectedDevice + val device = aboutToConnect?.fullMediaDevice ?: localMediaManager.currentConnectedDevice val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) } // If we have a controller but get a null route, then don't trust the device @@ -247,3 +259,17 @@ class MediaDeviceManager @Inject constructor( } } } + +/** + * A class storing information for the about-to-connect device. See + * [LocalMediaManager.DeviceCallback.onAboutToConnectDeviceAdded] for more information. + * + * @property fullMediaDevice a full-fledged [MediaDevice] object representing the device. If + * non-null, prefer using [fullMediaDevice] over [backupMediaDeviceData]. + * @property backupMediaDeviceData a backup [MediaDeviceData] object containing the minimum + * information required to display the device. Only use if [fullMediaDevice] is null. + */ +private data class AboutToConnectDevice( + val fullMediaDevice: MediaDevice? = null, + val backupMediaDeviceData: MediaDeviceData? = null +) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 3d9f933b3a05..30771c728e86 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -1146,7 +1146,10 @@ private val EMPTY_RECT = Rect() @Retention(AnnotationRetention.SOURCE) private annotation class TransformationType -@IntDef(prefix = ["LOCATION_"], value = [MediaHierarchyManager.LOCATION_QS, - MediaHierarchyManager.LOCATION_QQS, MediaHierarchyManager.LOCATION_LOCKSCREEN]) +@IntDef(prefix = ["LOCATION_"], value = [ + MediaHierarchyManager.LOCATION_QS, + MediaHierarchyManager.LOCATION_QQS, + MediaHierarchyManager.LOCATION_LOCKSCREEN, + MediaHierarchyManager.LOCATION_DREAM_OVERLAY]) @Retention(AnnotationRetention.SOURCE) annotation class MediaLocation diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt index 862b2797b77c..3eba3b55b7e8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt @@ -134,6 +134,23 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) fun logOpenOutputSwitcher(uid: Int, packageName: String, instanceId: InstanceId) { logger.logWithInstanceId(MediaUiEvent.OPEN_OUTPUT_SWITCHER, uid, packageName, instanceId) } + + fun logTapContentView(uid: Int, packageName: String, instanceId: InstanceId) { + logger.logWithInstanceId(MediaUiEvent.MEDIA_TAP_CONTENT_VIEW, uid, packageName, instanceId) + } + + fun logCarouselPosition(@MediaLocation location: Int) { + val event = when (location) { + MediaHierarchyManager.LOCATION_QQS -> MediaUiEvent.MEDIA_CAROUSEL_LOCATION_QQS + MediaHierarchyManager.LOCATION_QS -> MediaUiEvent.MEDIA_CAROUSEL_LOCATION_QS + MediaHierarchyManager.LOCATION_LOCKSCREEN -> + MediaUiEvent.MEDIA_CAROUSEL_LOCATION_LOCKSCREEN + MediaHierarchyManager.LOCATION_DREAM_OVERLAY -> + MediaUiEvent.MEDIA_CAROUSEL_LOCATION_DREAM + else -> throw IllegalArgumentException("Unknown media carousel location $location") + } + logger.log(event) + } } enum class MediaUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum { @@ -161,7 +178,7 @@ enum class MediaUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum { @UiEvent(doc = "An existing active media control was converted into resumable media") ACTIVE_TO_RESUME(1014), - @UiEvent(doc = "Media timed out") + @UiEvent(doc = "A media control timed out") MEDIA_TIMEOUT(1015), @UiEvent(doc = "A media control was removed from the carousel") @@ -173,35 +190,50 @@ enum class MediaUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum { @UiEvent(doc = "The user swiped away the media carousel") DISMISS_SWIPE(1018), - @UiEvent(doc = "The user opened the long press menu") + @UiEvent(doc = "The user long pressed on a media control") OPEN_LONG_PRESS(1019), - @UiEvent(doc = "The user dismissed via long press menu") + @UiEvent(doc = "The user dismissed a media control via its long press menu") DISMISS_LONG_PRESS(1020), - @UiEvent(doc = "The user opened settings from long press menu") + @UiEvent(doc = "The user opened media settings from a media control's long press menu") OPEN_SETTINGS_LONG_PRESS(1021), - @UiEvent(doc = "The user opened settings from the carousel") + @UiEvent(doc = "The user opened media settings from the media carousel") OPEN_SETTINGS_CAROUSEL(1022), - @UiEvent(doc = "The play/pause button was tapped") + @UiEvent(doc = "The play/pause button on a media control was tapped") TAP_ACTION_PLAY_PAUSE(1023), - @UiEvent(doc = "The previous button was tapped") + @UiEvent(doc = "The previous button on a media control was tapped") TAP_ACTION_PREV(1024), - @UiEvent(doc = "The next button was tapped") + @UiEvent(doc = "The next button on a media control was tapped") TAP_ACTION_NEXT(1025), - @UiEvent(doc = "A custom or generic action button was tapped") + @UiEvent(doc = "A custom or generic action button on a media control was tapped") TAP_ACTION_OTHER(1026), - @UiEvent(doc = "The user seeked using the seekbar") + @UiEvent(doc = "The user seeked on a media control using the seekbar") ACTION_SEEK(1027), @UiEvent(doc = "The user opened the output switcher from a media control") - OPEN_OUTPUT_SWITCHER(1028); + OPEN_OUTPUT_SWITCHER(1028), + + @UiEvent(doc = "The user tapped on a media control view") + MEDIA_TAP_CONTENT_VIEW(1036), + + @UiEvent(doc = "The media carousel moved to QQS") + MEDIA_CAROUSEL_LOCATION_QQS(1037), + + @UiEvent(doc = "THe media carousel moved to QS") + MEDIA_CAROUSEL_LOCATION_QS(1038), + + @UiEvent(doc = "The media carousel moved to the lockscreen") + MEDIA_CAROUSEL_LOCATION_LOCKSCREEN(1039), + + @UiEvent(doc = "The media carousel moved to the dream state") + MEDIA_CAROUSEL_LOCATION_DREAM(1040); override fun getId() = metricId }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index 807f0f1bb0ba..ec2a950051b7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -721,7 +721,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, boolean isVolumeControlEnabled(@NonNull MediaDevice device) { return isPlayBackInfoLocal() - || mLocalMediaManager.isMediaSessionAvailableForVolumeControl(); + || device.getDeviceType() != MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt index 22bc5572f5a5..2783532aff97 100644 --- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt @@ -52,7 +52,9 @@ class MediaMuteAwaitConnectionManager constructor( // There should only be one device that's mutedUntilConnection at a time, so we can // safely override any previous value. currentMutedDevice = device - localMediaManager.dispatchAboutToConnectDeviceChanged(device.name, device.getIcon()) + localMediaManager.dispatchAboutToConnectDeviceAdded( + device.address, device.name, device.getIcon() + ) } } @@ -63,7 +65,7 @@ class MediaMuteAwaitConnectionManager constructor( ) { if (currentMutedDevice == device && USAGE_MEDIA in mutedUsages) { currentMutedDevice = null - localMediaManager.dispatchAboutToConnectDeviceChanged(null, null) + localMediaManager.dispatchAboutToConnectDeviceRemoved() } } } @@ -76,8 +78,8 @@ class MediaMuteAwaitConnectionManager constructor( val currentDevice = audioManager.mutingExpectedDevice if (currentDevice != null) { currentMutedDevice = currentDevice - localMediaManager.dispatchAboutToConnectDeviceChanged( - currentDevice.name, currentDevice.getIcon() + localMediaManager.dispatchAboutToConnectDeviceAdded( + currentDevice.address, currentDevice.name, currentDevice.getIcon() ) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt index 3c6805b4e881..cd86fff1c6f8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt @@ -19,6 +19,7 @@ package com.android.systemui.media.taptotransfer.sender import android.app.StatusBarManager import android.content.Context import android.media.MediaRoute2Info +import android.util.Log import android.view.View import androidx.annotation.StringRes import com.android.internal.logging.UiEventLogger @@ -221,7 +222,12 @@ enum class ChipStateSender( */ fun getSenderStateFromId( @StatusBarManager.MediaTransferSenderState displayState: Int, - ): ChipStateSender = values().first { it.stateInt == displayState } + ): ChipStateSender? = try { + values().first { it.stateInt == displayState } + } catch (e: NoSuchElementException) { + Log.e(TAG, "Could not find requested state $displayState", e) + null + } /** * Returns the state int from [StatusBarManager] associated with the given sender state @@ -238,3 +244,5 @@ enum class ChipStateSender( // process and we should keep the user informed about it as long as possible (but don't allow it to // continue indefinitely). private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L + +private const val TAG = "ChipStateSender" diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index c01d6dcd7d64..c6c9aca0b161 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -466,7 +466,7 @@ public class PeopleSpaceUtils { } } } catch (SQLException e) { - Log.e(TAG, "Failed to query contact: " + e); + Log.e(TAG, "Failed to query contact", e); } finally { if (cursor != null) { cursor.close(); @@ -527,7 +527,7 @@ public class PeopleSpaceUtils { lookupKeysWithBirthdaysToday.add(lookupKey); } } catch (SQLException e) { - Log.e(TAG, "Failed to query birthdays: " + e); + Log.e(TAG, "Failed to query birthdays", e); } finally { if (cursor != null) { cursor.close(); diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index f6e1cd49eb40..1a7bd8cb6cf9 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -275,7 +275,7 @@ public class PeopleSpaceWidgetManager { updateSingleConversationWidgets(widgetIds); } } catch (Exception e) { - Log.e(TAG, "Exception: " + e); + Log.e(TAG, "failed to update widgets", e); } } @@ -348,7 +348,7 @@ public class PeopleSpaceWidgetManager { try { return getTileForExistingWidgetThrowing(appWidgetId); } catch (Exception e) { - Log.e(TAG, "Failed to retrieve conversation for tile: " + e); + Log.e(TAG, "failed to retrieve tile for widget ID " + appWidgetId, e); return null; } } @@ -423,7 +423,7 @@ public class PeopleSpaceWidgetManager { // Add current state. return getTileWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED); } catch (RemoteException e) { - Log.e(TAG, "Could not retrieve data: " + e); + Log.e(TAG, "getTileFromPersistentStorage failing", e); return null; } } @@ -441,12 +441,16 @@ public class PeopleSpaceWidgetManager { Log.d(TAG, "Notification removed, key: " + sbn.getKey()); } } + if (DEBUG) Log.d(TAG, "Fetching notifications"); + Collection<NotificationEntry> notifications = mNotifCollection.getAllNotifs(); mBgExecutor.execute( - () -> updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction)); + () -> updateWidgetsWithNotificationChangedInBackground( + sbn, notificationAction, notifications)); } private void updateWidgetsWithNotificationChangedInBackground(StatusBarNotification sbn, - PeopleSpaceUtils.NotificationAction action) { + PeopleSpaceUtils.NotificationAction action, + Collection<NotificationEntry> notifications) { try { PeopleTileKey key = new PeopleTileKey( sbn.getShortcutId(), sbn.getUser().getIdentifier(), sbn.getPackageName()); @@ -469,23 +473,23 @@ public class PeopleSpaceWidgetManager { Log.d(TAG, "Widgets by URI to be updated:" + tilesUpdatedByUri.toString()); } tilesUpdated.addAll(tilesUpdatedByUri); - updateWidgetIdsBasedOnNotifications(tilesUpdated); + updateWidgetIdsBasedOnNotifications(tilesUpdated, notifications); } } catch (Exception e) { - Log.e(TAG, "Throwing exception: " + e); + Log.e(TAG, "updateWidgetsWithNotificationChangedInBackground failing", e); } } /** Updates {@code widgetIdsToUpdate} with {@code action}. */ - private void updateWidgetIdsBasedOnNotifications(Set<String> widgetIdsToUpdate) { + private void updateWidgetIdsBasedOnNotifications(Set<String> widgetIdsToUpdate, + Collection<NotificationEntry> ungroupedNotifications) { if (widgetIdsToUpdate.isEmpty()) { if (DEBUG) Log.d(TAG, "No widgets to update, returning."); return; } try { - if (DEBUG) Log.d(TAG, "Fetching grouped notifications"); Map<PeopleTileKey, Set<NotificationEntry>> groupedNotifications = - getGroupedConversationNotifications(); + groupConversationNotifications(ungroupedNotifications); widgetIdsToUpdate .stream() @@ -495,7 +499,7 @@ public class PeopleSpaceWidgetManager { id -> getAugmentedTileForExistingWidget(id, groupedNotifications))) .forEach((id, tile) -> updateAppWidgetOptionsAndViewOptional(id, tile)); } catch (Exception e) { - Log.e(TAG, "Exception updating widgets: " + e); + Log.e(TAG, "updateWidgetIdsBasedOnNotifications failing", e); } } @@ -510,7 +514,7 @@ public class PeopleSpaceWidgetManager { "Augmenting tile from NotificationEntryManager widget: " + key.toString()); } Map<PeopleTileKey, Set<NotificationEntry>> notifications = - getGroupedConversationNotifications(); + groupConversationNotifications(mNotifCollection.getAllNotifs()); String contactUri = null; if (tile.getContactUri() != null) { contactUri = tile.getContactUri().toString(); @@ -518,9 +522,10 @@ public class PeopleSpaceWidgetManager { return augmentTileFromNotifications(tile, key, contactUri, notifications, appWidgetId); } - /** Returns active and pending notifications grouped by {@link PeopleTileKey}. */ - public Map<PeopleTileKey, Set<NotificationEntry>> getGroupedConversationNotifications() { - Collection<NotificationEntry> notifications = mNotifCollection.getAllNotifs(); + /** Groups active and pending notifications grouped by {@link PeopleTileKey}. */ + public Map<PeopleTileKey, Set<NotificationEntry>> groupConversationNotifications( + Collection<NotificationEntry> notifications + ) { if (DEBUG) Log.d(TAG, "Number of total notifications: " + notifications.size()); Map<PeopleTileKey, Set<NotificationEntry>> groupedNotifications = notifications @@ -846,7 +851,7 @@ public class PeopleSpaceWidgetManager { Collections.singletonList(tile.getId()), tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); } catch (Exception e) { - Log.w(TAG, "Exception caching shortcut:" + e); + Log.w(TAG, "failed to cache shortcut", e); } PeopleSpaceTile finalTile = tile; mBgExecutor.execute( @@ -954,7 +959,7 @@ public class PeopleSpaceWidgetManager { UserHandle.of(key.getUserId()), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); } catch (Exception e) { - Log.d(TAG, "Exception uncaching shortcut:" + e); + Log.d(TAG, "failed to uncache shortcut", e); } } @@ -1037,7 +1042,7 @@ public class PeopleSpaceWidgetManager { packageName, userHandle.getIdentifier(), shortcutId); tile = PeopleSpaceUtils.getTile(channel, mLauncherApps); } catch (Exception e) { - Log.w(TAG, "Exception getting tiles: " + e); + Log.w(TAG, "failed to get conversation or tile", e); return null; } if (tile == null) { @@ -1086,7 +1091,7 @@ public class PeopleSpaceWidgetManager { } } catch (PackageManager.NameNotFoundException e) { // Delete data for uninstalled widgets. - Log.e(TAG, "Package no longer found for tile: " + e); + Log.e(TAG, "package no longer found for tile", e); JobScheduler jobScheduler = mContext.getSystemService(JobScheduler.class); if (jobScheduler != null && jobScheduler.getPendingJob(PeopleBackupFollowUpJob.JOB_ID) != null) { @@ -1296,7 +1301,7 @@ public class PeopleSpaceWidgetManager { try { editor.putString(newId, (String) entry.getValue()); } catch (Exception e) { - Log.e(TAG, "Malformed entry value: " + entry.getValue()); + Log.e(TAG, "malformed entry value: " + entry.getValue(), e); } editor.remove(key); break; @@ -1306,7 +1311,7 @@ public class PeopleSpaceWidgetManager { try { oldWidgetIds = (Set<String>) entry.getValue(); } catch (Exception e) { - Log.e(TAG, "Malformed entry value: " + entry.getValue()); + Log.e(TAG, "malformed entry value: " + entry.getValue(), e); editor.remove(key); break; } @@ -1337,7 +1342,7 @@ public class PeopleSpaceWidgetManager { try { oldWidgetIds = (Set<String>) entry.getValue(); } catch (Exception e) { - Log.e(TAG, "Malformed entry value: " + entry.getValue()); + Log.e(TAG, "malformed entry value: " + entry.getValue(), e); followUpEditor.remove(key); continue; } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 2435497193e4..3e00a5f74d8f 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -18,6 +18,7 @@ package com.android.systemui.power; import static android.app.PendingIntent.FLAG_IMMUTABLE; +import android.app.Dialog; import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationManager; @@ -60,20 +61,25 @@ import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.utils.PowerUtil; import com.android.systemui.R; import com.android.systemui.SystemUIApplication; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.NotificationChannels; import com.android.systemui.volume.Events; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.text.NumberFormat; import java.util.Locale; import java.util.Objects; import javax.inject.Inject; +import dagger.Lazy; + /** */ @SysUISingleton @@ -164,11 +170,15 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { private ActivityStarter mActivityStarter; private final BroadcastSender mBroadcastSender; + private final Lazy<BatteryController> mBatteryControllerLazy; + private final DialogLaunchAnimator mDialogLaunchAnimator; + /** */ @Inject public PowerNotificationWarnings(Context context, ActivityStarter activityStarter, - BroadcastSender broadcastSender) { + BroadcastSender broadcastSender, Lazy<BatteryController> batteryControllerLazy, + DialogLaunchAnimator dialogLaunchAnimator) { mContext = context; mNoMan = mContext.getSystemService(NotificationManager.class); mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE); @@ -176,6 +186,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { mReceiver.init(); mActivityStarter = activityStarter; mBroadcastSender = broadcastSender; + mBatteryControllerLazy = batteryControllerLazy; + mDialogLaunchAnimator = dialogLaunchAnimator; mUseSevereDialog = mContext.getResources().getBoolean(R.bool.config_severe_battery_dialog); } @@ -685,8 +697,19 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { } d.setShowForAllUsers(true); d.setOnDismissListener((dialog) -> mSaverConfirmation = null); - d.show(); + WeakReference<View> ref = mBatteryControllerLazy.get().getLastPowerSaverStartView(); + if (ref != null && ref.get() != null && ref.get().isAggregatedVisible()) { + mDialogLaunchAnimator.showFromView(d, ref.get()); + } else { + d.show(); + } mSaverConfirmation = d; + mBatteryControllerLazy.get().clearLastPowerSaverStartView(); + } + + @VisibleForTesting + Dialog getSaverConfirmationDialog() { + return mSaverConfirmation; } private boolean isEnglishLocale() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index 7d8a28fc011e..1004fcae3827 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -116,6 +116,11 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements public void handleSetListening(boolean listening) { super.handleSetListening(listening); mSetting.setListening(listening); + if (!listening) { + // If we stopped listening, it means that the tile is not visible. In that case, we + // don't need to save the view anymore + mBatteryController.clearLastPowerSaverStartView(); + } } @Override @@ -128,7 +133,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements if (getState().state == Tile.STATE_UNAVAILABLE) { return; } - mBatteryController.setPowerSaveMode(!mPowerSave); + mBatteryController.setPowerSaveMode(!mPowerSave, view); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index 41e468d382a3..50ee1f7ba97a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -246,7 +246,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { new ClipData.Item(uri)); sharingIntent.setClipData(clipdata); sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); - sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + // Make sure pending intents for the system user are still unique across users // by setting the (otherwise unused) request code to the current user id. @@ -256,6 +258,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + // cancel current pending intent (if any) since clipData isn't used for matching PendingIntent pendingIntent = PendingIntent.getActivityAsUser( context, 0, sharingChooserIntent, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index d785059e3de7..27586b4f5caa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.SecureSettings; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -84,6 +85,7 @@ public class NotificationLockscreenUserManagerImpl implements private final DeviceProvisionedController mDeviceProvisionedController; private final KeyguardStateController mKeyguardStateController; + private final SecureSettings mSecureSettings; private final Object mLock = new Object(); // Lazy @@ -187,6 +189,7 @@ public class NotificationLockscreenUserManagerImpl implements protected NotificationPresenter mPresenter; protected ContentObserver mLockscreenSettingsObserver; protected ContentObserver mSettingsObserver; + private boolean mHideSilentNotificationsOnLockscreen; private NotificationEntryManager getEntryManager() { if (mEntryManager == null) { @@ -208,6 +211,7 @@ public class NotificationLockscreenUserManagerImpl implements @Main Handler mainHandler, DeviceProvisionedController deviceProvisionedController, KeyguardStateController keyguardStateController, + SecureSettings secureSettings, DumpManager dumpManager) { mContext = context; mMainHandler = mainHandler; @@ -222,6 +226,7 @@ public class NotificationLockscreenUserManagerImpl implements mKeyguardManager = keyguardManager; mBroadcastDispatcher = broadcastDispatcher; mDeviceProvisionedController = deviceProvisionedController; + mSecureSettings = secureSettings; mKeyguardStateController = keyguardStateController; dumpManager.registerDumpable(this); @@ -256,12 +261,18 @@ public class NotificationLockscreenUserManagerImpl implements }; mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, + mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, mLockscreenSettingsObserver, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + true, + mLockscreenSettingsObserver, + UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS), true, mLockscreenSettingsObserver, UserHandle.USER_ALL); @@ -272,7 +283,7 @@ public class NotificationLockscreenUserManagerImpl implements if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), + mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), false, mSettingsObserver, UserHandle.USER_ALL); @@ -366,7 +377,7 @@ public class NotificationLockscreenUserManagerImpl implements } } boolean exceedsPriorityThreshold; - if (hideSilentNotificationsOnLockscreen()) { + if (mHideSilentNotificationsOnLockscreen) { exceedsPriorityThreshold = entry.getBucket() == BUCKET_MEDIA_CONTROLS || (entry.getBucket() != BUCKET_SILENT @@ -377,11 +388,6 @@ public class NotificationLockscreenUserManagerImpl implements return mShowLockscreenNotifications && exceedsPriorityThreshold; } - private boolean hideSilentNotificationsOnLockscreen() { - return whitelistIpcs(() -> Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1) == 0); - } - private void setShowLockscreenNotifications(boolean show) { mShowLockscreenNotifications = show; } @@ -391,7 +397,7 @@ public class NotificationLockscreenUserManagerImpl implements } protected void updateLockscreenNotificationSetting() { - final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(), + final boolean show = mSecureSettings.getIntForUser( Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUserId) != 0; @@ -400,10 +406,13 @@ public class NotificationLockscreenUserManagerImpl implements final boolean allowedByDpm = (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; + mHideSilentNotificationsOnLockscreen = mSecureSettings.getIntForUser( + Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1, mCurrentUserId) == 0; + setShowLockscreenNotifications(show && allowedByDpm); if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { - final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(), + final boolean remoteInput = mSecureSettings.getIntForUser( Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, 0, mCurrentUserId) != 0; @@ -426,8 +435,7 @@ public class NotificationLockscreenUserManagerImpl implements } if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { - final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( - mContext.getContentResolver(), + final boolean allowedByUser = 0 != mSecureSettings.getIntForUser( Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); @@ -492,8 +500,7 @@ public class NotificationLockscreenUserManagerImpl implements } if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) { - final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( - mContext.getContentResolver(), + final boolean allowedByUser = 0 != mSecureSettings.getIntForUser( Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle); final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 7239d0cc361b..1df40915a52c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -102,12 +102,14 @@ public class NotificationMediaManager implements Dumpable { KeyguardStateController.class); private final KeyguardBypassController mKeyguardBypassController; private static final HashSet<Integer> PAUSED_MEDIA_STATES = new HashSet<>(); + private static final HashSet<Integer> CONNECTING_MEDIA_STATES = new HashSet<>(); static { PAUSED_MEDIA_STATES.add(PlaybackState.STATE_NONE); PAUSED_MEDIA_STATES.add(PlaybackState.STATE_STOPPED); PAUSED_MEDIA_STATES.add(PlaybackState.STATE_PAUSED); PAUSED_MEDIA_STATES.add(PlaybackState.STATE_ERROR); - PAUSED_MEDIA_STATES.add(PlaybackState.STATE_CONNECTING); + CONNECTING_MEDIA_STATES.add(PlaybackState.STATE_CONNECTING); + CONNECTING_MEDIA_STATES.add(PlaybackState.STATE_BUFFERING); } private final NotificationVisibilityProvider mVisibilityProvider; @@ -363,7 +365,17 @@ public class NotificationMediaManager implements Dumpable { * @return true if playing */ public static boolean isPlayingState(int state) { - return !PAUSED_MEDIA_STATES.contains(state); + return !PAUSED_MEDIA_STATES.contains(state) + && !CONNECTING_MEDIA_STATES.contains(state); + } + + /** + * Check if a state should be considered as connecting + * @param state a PlaybackState + * @return true if connecting or buffering + */ + public static boolean isConnectingState(int state) { + return CONNECTING_MEDIA_STATES.contains(state); } public void setUpWithPresenter(NotificationPresenter presenter) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index eaa66bbc15ac..633786ffa787 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -594,16 +594,13 @@ public class NotificationShelf extends ActivatableNotificationView implements } else { shouldClipOwnTop = view.showingPulsing(); } - if (viewEnd > notificationClipEnd && !shouldClipOwnTop - && (mAmbientState.isShadeExpanded() || !isPinned)) { - int clipBottomAmount = (int) (viewEnd - notificationClipEnd); - if (isPinned) { - clipBottomAmount = Math.min(view.getIntrinsicHeight() - view.getCollapsedHeight(), - clipBottomAmount); + if (!isPinned) { + if (viewEnd > notificationClipEnd && !shouldClipOwnTop) { + int clipBottomAmount = (int) (viewEnd - notificationClipEnd); + view.setClipBottomAmount(clipBottomAmount); + } else { + view.setClipBottomAmount(0); } - view.setClipBottomAmount(clipBottomAmount); - } else { - view.setClipBottomAmount(0); } if (shouldClipOwnTop) { return (int) (viewEnd - getTranslationY()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java index 4a6d7e184ec2..8d7fc98164c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java @@ -21,8 +21,6 @@ import android.widget.TextView; import com.android.settingslib.WirelessUtils; -import java.util.List; - /** Shows the operator name */ public class OperatorNameView extends TextView { private boolean mDemoMode; @@ -43,8 +41,10 @@ public class OperatorNameView extends TextView { mDemoMode = demoMode; } - void update(boolean showOperatorName, boolean hasMobile, - List<OperatorNameViewController.SubInfo> subs) { + void update(boolean showOperatorName, + boolean hasMobile, + OperatorNameViewController.SubInfo sub + ) { setVisibility(showOperatorName ? VISIBLE : GONE); boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext); @@ -55,24 +55,21 @@ public class OperatorNameView extends TextView { } if (!mDemoMode) { - updateText(subs); + updateText(sub); } } - void updateText(List<OperatorNameViewController.SubInfo> subs) { + void updateText(OperatorNameViewController.SubInfo subInfo) { + CharSequence carrierName = null; CharSequence displayText = null; - final int N = subs.size(); - for (int i = 0; i < N; i++) { - OperatorNameViewController.SubInfo subInfo = subs.get(i); - CharSequence carrierName = subs.get(i).getCarrierName(); - if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) { - if (subInfo.stateInService()) { - displayText = subInfo.getCarrierName(); - break; - } + if (subInfo != null) { + carrierName = subInfo.getCarrierName(); + } + if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) { + if (subInfo.stateInService()) { + displayText = carrierName; } } - setText(displayText); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java index 8a4c4b5ac5c6..8afc72f08656 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.os.Bundle; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.view.View; @@ -30,12 +31,11 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; +import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.ViewController; -import java.util.ArrayList; -import java.util.List; - import javax.inject.Inject; /** Controller for {@link OperatorNameView}. */ @@ -47,19 +47,22 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> private final TunerService mTunerService; private final TelephonyManager mTelephonyManager; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final CarrierConfigTracker mCarrierConfigTracker; private OperatorNameViewController(OperatorNameView view, DarkIconDispatcher darkIconDispatcher, NetworkController networkController, TunerService tunerService, TelephonyManager telephonyManager, - KeyguardUpdateMonitor keyguardUpdateMonitor) { + KeyguardUpdateMonitor keyguardUpdateMonitor, + CarrierConfigTracker carrierConfigTracker) { super(view); mDarkIconDispatcher = darkIconDispatcher; mNetworkController = networkController; mTunerService = tunerService; mTelephonyManager = telephonyManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mCarrierConfigTracker = carrierConfigTracker; } @Override @@ -79,24 +82,22 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> } private void update() { - mView.update(mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0, - mTelephonyManager.isDataCapable(), getSubInfos()); + SubInfo defaultSubInfo = getDefaultSubInfo(); + boolean showOperatorName = + mCarrierConfigTracker + .getShowOperatorNameInStatusBarConfig(defaultSubInfo.getSubId()) + && (mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0); + mView.update(showOperatorName, mTelephonyManager.isDataCapable(), getDefaultSubInfo()); } - private List<SubInfo> getSubInfos() { - List<SubInfo> result = new ArrayList<>(); - List<SubscriptionInfo> subscritionInfos = - mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); - - for (SubscriptionInfo subscriptionInfo : subscritionInfos) { - int subId = subscriptionInfo.getSubscriptionId(); - result.add(new SubInfo( - subscriptionInfo.getCarrierName(), - mKeyguardUpdateMonitor.getSimState(subId), - mKeyguardUpdateMonitor.getServiceState(subId))); - } - - return result; + private SubInfo getDefaultSubInfo() { + int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId(); + SubscriptionInfo sI = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(defaultSubId); + return new SubInfo( + sI.getSubscriptionId(), + sI.getCarrierName(), + mKeyguardUpdateMonitor.getSimState(defaultSubId), + mKeyguardUpdateMonitor.getServiceState(defaultSubId)); } /** Factory for constructing an {@link OperatorNameViewController}. */ @@ -106,22 +107,32 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> private final TunerService mTunerService; private final TelephonyManager mTelephonyManager; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final CarrierConfigTracker mCarrierConfigTracker; @Inject - public Factory(DarkIconDispatcher darkIconDispatcher, NetworkController networkController, - TunerService tunerService, TelephonyManager telephonyManager, - KeyguardUpdateMonitor keyguardUpdateMonitor) { + public Factory(DarkIconDispatcher darkIconDispatcher, + NetworkController networkController, + TunerService tunerService, + TelephonyManager telephonyManager, + KeyguardUpdateMonitor keyguardUpdateMonitor, + CarrierConfigTracker carrierConfigTracker) { mDarkIconDispatcher = darkIconDispatcher; mNetworkController = networkController; mTunerService = tunerService; mTelephonyManager = telephonyManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mCarrierConfigTracker = carrierConfigTracker; } /** Create an {@link OperatorNameViewController}. */ public OperatorNameViewController create(OperatorNameView view) { - return new OperatorNameViewController(view, mDarkIconDispatcher, mNetworkController, - mTunerService, mTelephonyManager, mKeyguardUpdateMonitor); + return new OperatorNameViewController(view, + mDarkIconDispatcher, + mNetworkController, + mTunerService, + mTelephonyManager, + mKeyguardUpdateMonitor, + mCarrierConfigTracker); } } @@ -152,7 +163,7 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> new KeyguardUpdateMonitorCallback() { @Override public void onRefreshCarrierInfo() { - mView.updateText(getSubInfos()); + mView.updateText(getDefaultSubInfo()); } }; @@ -176,17 +187,26 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> }; static class SubInfo { + private final int mSubId; private final CharSequence mCarrierName; private final int mSimState; private final ServiceState mServiceState; - private SubInfo(CharSequence carrierName, - int simState, ServiceState serviceState) { + private SubInfo( + int subId, + CharSequence carrierName, + int simState, + ServiceState serviceState) { + mSubId = subId; mCarrierName = carrierName; mSimState = simState; mServiceState = serviceState; } + int getSubId() { + return mSubId; + } + boolean simReady() { return mSimState == TelephonyManager.SIM_STATE_READY; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java index c640ab6c3a90..7d0f00abcc98 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java @@ -270,8 +270,8 @@ public class NotificationBackgroundView extends View { /** Set the current expand animation size. */ public void setExpandAnimationSize(int width, int height) { - mExpandAnimationHeight = width; - mExpandAnimationWidth = height; + mExpandAnimationHeight = height; + mExpandAnimationWidth = width; invalidate(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java index b95d153e8bd1..7f3381ccd38a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java @@ -34,10 +34,13 @@ public class ExpandableViewState extends ViewState { private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; + private static final int TAG_ANIMATOR_BOTTOM_INSET = R.id.bottom_inset_animator_tag; private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; + private static final int TAG_END_BOTTOM_INSET = R.id.bottom_inset_animator_end_value_tag; private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag; private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag; + private static final int TAG_START_BOTTOM_INSET = R.id.bottom_inset_animator_start_value_tag; // These are flags such that we can create masks for filtering. @@ -96,12 +99,17 @@ public class ExpandableViewState extends ViewState { public boolean headsUpIsVisible; /** - * How much the child overlaps with the previous child on top. This is used to - * show the background properly when the child on top is translating away. + * How much the child overlaps on top with the child above. */ public int clipTopAmount; /** + * How much the child overlaps on bottom with the child above. This is used to + * show the background properly when the child on top is translating away. + */ + public int clipBottomAmount; + + /** * The index of the view, only accounting for views not equal to GONE */ public int notGoneIndex; @@ -138,8 +146,8 @@ public class ExpandableViewState extends ViewState { if (view instanceof ExpandableView) { ExpandableView expandableView = (ExpandableView) view; - int height = expandableView.getActualHeight(); - int newHeight = this.height; + final int height = expandableView.getActualHeight(); + final int newHeight = this.height; // apply height if (height != newHeight) { @@ -157,10 +165,14 @@ public class ExpandableViewState extends ViewState { expandableView.setBelowSpeedBump(this.belowSpeedBump); // apply clipping - float oldClipTopAmount = expandableView.getClipTopAmount(); + final float oldClipTopAmount = expandableView.getClipTopAmount(); if (oldClipTopAmount != this.clipTopAmount) { expandableView.setClipTopAmount(this.clipTopAmount); } + final float oldClipBottomAmount = expandableView.getClipBottomAmount(); + if (oldClipBottomAmount != this.clipBottomAmount) { + expandableView.setClipBottomAmount(this.clipBottomAmount); + } expandableView.setTransformingInShelf(false); expandableView.setInShelf(inShelf); @@ -187,13 +199,20 @@ public class ExpandableViewState extends ViewState { abortAnimation(child, TAG_ANIMATOR_HEIGHT); } - // start top inset animation + // start clip top animation if (this.clipTopAmount != expandableView.getClipTopAmount()) { - startInsetAnimation(expandableView, properties); + startClipAnimation(expandableView, properties, /* clipTop */true); } else { abortAnimation(child, TAG_ANIMATOR_TOP_INSET); } + // start clip bottom animation + if (this.clipBottomAmount != expandableView.getClipBottomAmount()) { + startClipAnimation(expandableView, properties, /* clipTop */ false); + } else { + abortAnimation(child, TAG_ANIMATOR_BOTTOM_INSET); + } + // start dimmed animation expandableView.setDimmed(this.dimmed, animationFilter.animateDimmed); @@ -301,16 +320,20 @@ public class ExpandableViewState extends ViewState { child.setActualHeightAnimating(true); } - private void startInsetAnimation(final ExpandableView child, AnimationProperties properties) { - Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET); - Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET); - int newEndValue = this.clipTopAmount; + private void startClipAnimation(final ExpandableView child, AnimationProperties properties, + boolean clipTop) { + Integer previousStartValue = getChildTag(child, + clipTop ? TAG_START_TOP_INSET : TAG_START_BOTTOM_INSET); + Integer previousEndValue = getChildTag(child, + clipTop ? TAG_END_TOP_INSET : TAG_END_BOTTOM_INSET); + int newEndValue = clipTop ? this.clipTopAmount : this.clipBottomAmount; if (previousEndValue != null && previousEndValue == newEndValue) { return; } - ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET); + ValueAnimator previousAnimator = getChildTag(child, + clipTop ? TAG_ANIMATOR_TOP_INSET : TAG_ANIMATOR_BOTTOM_INSET); AnimationFilter filter = properties.getAnimationFilter(); - if (!filter.animateTopInset) { + if (clipTop && !filter.animateTopInset || !clipTop) { // just a local update was performed if (previousAnimator != null) { // we need to increase all animation keyframes of the previous animator by the @@ -319,22 +342,28 @@ public class ExpandableViewState extends ViewState { int relativeDiff = newEndValue - previousEndValue; int newStartValue = previousStartValue + relativeDiff; values[0].setIntValues(newStartValue, newEndValue); - child.setTag(TAG_START_TOP_INSET, newStartValue); - child.setTag(TAG_END_TOP_INSET, newEndValue); + child.setTag(clipTop ? TAG_START_TOP_INSET : TAG_START_BOTTOM_INSET, newStartValue); + child.setTag(clipTop ? TAG_END_TOP_INSET : TAG_END_BOTTOM_INSET, newEndValue); previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); return; } else { // no new animation needed, let's just apply the value - child.setClipTopAmount(newEndValue); + if (clipTop) { + child.setClipTopAmount(newEndValue); + } else { + child.setClipBottomAmount(newEndValue); + } return; } } - ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { + ValueAnimator animator = ValueAnimator.ofInt( + clipTop ? child.getClipTopAmount() : child.getClipBottomAmount(), newEndValue); + animator.addUpdateListener(animation -> { + if (clipTop) { child.setClipTopAmount((int) animation.getAnimatedValue()); + } else { + child.setClipBottomAmount((int) animation.getAnimatedValue()); } }); animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); @@ -353,15 +382,16 @@ public class ExpandableViewState extends ViewState { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - child.setTag(TAG_ANIMATOR_TOP_INSET, null); - child.setTag(TAG_START_TOP_INSET, null); - child.setTag(TAG_END_TOP_INSET, null); + child.setTag(clipTop ? TAG_ANIMATOR_TOP_INSET : TAG_ANIMATOR_BOTTOM_INSET, null); + child.setTag(clipTop ? TAG_START_TOP_INSET : TAG_START_BOTTOM_INSET, null); + child.setTag(clipTop ? TAG_END_TOP_INSET : TAG_END_BOTTOM_INSET, null); } }); startAnimator(animator, listener); - child.setTag(TAG_ANIMATOR_TOP_INSET, animator); - child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount()); - child.setTag(TAG_END_TOP_INSET, newEndValue); + child.setTag(clipTop ? TAG_ANIMATOR_TOP_INSET:TAG_ANIMATOR_BOTTOM_INSET, animator); + child.setTag(clipTop ? TAG_START_TOP_INSET: TAG_START_BOTTOM_INSET, + clipTop ? child.getClipTopAmount() : child.getClipBottomAmount()); + child.setTag(clipTop ? TAG_END_TOP_INSET: TAG_END_BOTTOM_INSET, newEndValue); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt index 6c6ed850f45a..d68f37103510 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt @@ -139,6 +139,8 @@ constructor( height += spaceNeeded count += 1 } else { + val gapBeforeFirstViewInShelf = current.calculateGapHeight(stack, previous, count) + height += gapBeforeFirstViewInShelf height += shelfHeight log { "returning height with shelf -> $height" } return height @@ -178,7 +180,9 @@ constructor( if (visibleIndex != 0) { size += notificationPadding } - size += calculateGapHeight(stack, previousView, visibleIndex) + val gapHeight = calculateGapHeight(stack, previousView, visibleIndex) + log { "\ti=$visibleIndex gapHeight=$gapHeight"} + size += gapHeight return size } 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 e1f8c35c3755..c0971337a19f 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 @@ -192,6 +192,7 @@ public class StackScrollAlgorithm { float clipStart = 0; int childCount = algorithmState.visibleChildren.size(); boolean firstHeadsUp = true; + float firstHeadsUpEnd = 0; for (int i = 0; i < childCount; i++) { ExpandableView child = algorithmState.visibleChildren.get(i); ExpandableViewState state = child.getViewState(); @@ -203,14 +204,18 @@ public class StackScrollAlgorithm { float newNotificationEnd = newYTranslation + newHeight; boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && child.isPinned(); if (mClipNotificationScrollToTop - && (!state.inShelf || (isHeadsUp && !firstHeadsUp)) - && newYTranslation < clipStart + && ((isHeadsUp && !firstHeadsUp) || child.isHeadsUpAnimatingAway()) + && newNotificationEnd > firstHeadsUpEnd && !ambientState.isShadeExpanded()) { - // The previous view is overlapping on top, clip! - float overlapAmount = clipStart - newYTranslation; - state.clipTopAmount = (int) overlapAmount; + // The bottom of this view is peeking out from under the previous view. + // Clip the part that is peeking out. + float overlapAmount = newNotificationEnd - firstHeadsUpEnd; + state.clipBottomAmount = (int) overlapAmount; } else { - state.clipTopAmount = 0; + state.clipBottomAmount = 0; + } + if (firstHeadsUp) { + firstHeadsUpEnd = newNotificationEnd; } if (isHeadsUp) { firstHeadsUp = false; @@ -635,8 +640,6 @@ public class StackScrollAlgorithm { // Ensure that a headsUp doesn't vertically extend further than the heads-up at // the top most z-position childState.height = row.getIntrinsicHeight(); - childState.yTranslation = Math.min(topState.yTranslation + topState.height - - childState.height, childState.yTranslation); } // heads up notification show and this row is the top entry of heads up diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 600732373cae..6887741f37bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -405,7 +405,7 @@ public class NotificationPanelViewController extends PanelViewController { * Determines if QS should be already expanded when expanding shade. * Used for split shade, two finger gesture as well as accessibility shortcut to QS. */ - private boolean mQsExpandImmediate; + @VisibleForTesting boolean mQsExpandImmediate; private boolean mTwoFingerQsExpandPossible; private String mHeaderDebugInfo; @@ -430,7 +430,6 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mExpandingFromHeadsUp; private boolean mCollapsedOnDown; private int mPositionMinSideMargin; - private int mLastOrientation = -1; private boolean mClosingWithAlphaFadeOut; private boolean mHeadsUpAnimatingAway; private boolean mLaunchingAffordance; @@ -955,7 +954,6 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); mPreviewContainer = mView.findViewById(R.id.preview_container); mKeyguardBottomArea.setPreviewContainer(mPreviewContainer); - mLastOrientation = mResources.getConfiguration().orientation; initBottomArea(); @@ -1994,9 +1992,6 @@ public class NotificationPanelViewController extends PanelViewController { if (!isFullyCollapsed()) { return; } - if (mShouldUseSplitNotificationShade) { - mQsExpandImmediate = true; - } mExpectingSynthesizedDown = true; onTrackingStarted(); updatePanelExpanded(); @@ -3172,6 +3167,9 @@ public class NotificationPanelViewController extends PanelViewController { mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); super.onTrackingStarted(); mScrimController.onTrackingStarted(); + if (mShouldUseSplitNotificationShade) { + mQsExpandImmediate = true; + } if (mQsFullyExpanded) { mQsExpandImmediate = true; setShowShelfOnly(true); @@ -4876,7 +4874,6 @@ public class NotificationPanelViewController extends PanelViewController { public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mAffordanceHelper.onConfigurationChanged(); - mLastOrientation = newConfig.orientation; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 6fe92fafc075..87ca942edff2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -489,7 +489,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte ExpandableNotificationRow row, boolean animate, boolean isActivityIntent) { - mLogger.logStartNotificationIntent(entry.getKey(), intent); + mLogger.logStartNotificationIntent(entry.getKey()); try { Runnable onFinishAnimationCallback = animate ? () -> mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry) @@ -513,8 +513,10 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mKeyguardStateController.isShowing(), eventTime) : getActivityOptions(mCentralSurfaces.getDisplayId(), adapter); - return intent.sendAndReturnResult(mContext, 0, fillInIntent, null, + int result = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, null, null, options); + mLogger.logSendPendingIntent(entry.getKey(), intent, result); + return result; }); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt index d118747a0365..2fbe520a4b61 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt @@ -32,7 +32,7 @@ class StatusBarNotificationActivityStarterLogger @Inject constructor( buffer.log(TAG, DEBUG, { str1 = key }, { - "(1/4) onNotificationClicked: $str1" + "(1/5) onNotificationClicked: $str1" }) } @@ -40,7 +40,7 @@ class StatusBarNotificationActivityStarterLogger @Inject constructor( buffer.log(TAG, DEBUG, { str1 = key }, { - "(2/4) handleNotificationClickAfterKeyguardDismissed: $str1" + "(2/5) handleNotificationClickAfterKeyguardDismissed: $str1" }) } @@ -48,16 +48,25 @@ class StatusBarNotificationActivityStarterLogger @Inject constructor( buffer.log(TAG, DEBUG, { str1 = key }, { - "(3/4) handleNotificationClickAfterPanelCollapsed: $str1" + "(3/5) handleNotificationClickAfterPanelCollapsed: $str1" }) } - fun logStartNotificationIntent(key: String, pendingIntent: PendingIntent) { + fun logStartNotificationIntent(key: String) { + buffer.log(TAG, INFO, { + str1 = key + }, { + "(4/5) startNotificationIntent: $str1" + }) + } + + fun logSendPendingIntent(key: String, pendingIntent: PendingIntent, result: Int) { buffer.log(TAG, INFO, { str1 = key str2 = pendingIntent.intent.toString() + int1 = result }, { - "(4/4) Starting $str2 for notification $str1" + "(5/5) Started intent $str2 for notification $str1 with result code $int1" }) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 6c6ec192646d..06532c4f9d17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -61,6 +61,7 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.settings.SecureSettings; import java.util.concurrent.Executor; @@ -263,6 +264,7 @@ public abstract class StatusBarViewModule { NetworkController networkController, StatusBarStateController statusBarStateController, CommandQueue commandQueue, + CarrierConfigTracker carrierConfigTracker, CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, OperatorNameViewController.Factory operatorNameViewControllerFactory, SecureSettings secureSettings, @@ -282,6 +284,7 @@ public abstract class StatusBarViewModule { networkController, statusBarStateController, commandQueue, + carrierConfigTracker, collapsedStatusBarFragmentLogger, operatorNameViewControllerFactory, secureSettings, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 8194957c52fc..9e48b763f4f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -32,6 +32,7 @@ import android.database.ContentObserver; import android.os.Bundle; import android.os.Parcelable; import android.provider.Settings; +import android.telephony.SubscriptionManager; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; @@ -69,6 +70,9 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.policy.EncryptionHelper; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.CarrierConfigTracker; +import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListener; +import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener; import com.android.systemui.util.settings.SecureSettings; import java.util.ArrayList; @@ -115,6 +119,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final NotificationIconAreaController mNotificationIconAreaController; private final PanelExpansionStateManager mPanelExpansionStateManager; private final StatusBarIconController mStatusBarIconController; + private final CarrierConfigTracker mCarrierConfigTracker; private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; private final SecureSettings mSecureSettings; private final Executor mMainExecutor; @@ -137,6 +142,28 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private OperatorNameViewController mOperatorNameViewController; private StatusBarSystemEventAnimator mSystemEventAnimator; + private final CarrierConfigChangedListener mCarrierConfigCallback = + new CarrierConfigChangedListener() { + @Override + public void onCarrierConfigChanged() { + if (mOperatorNameViewController == null) { + initOperatorName(); + } else { + // Already initialized, KeyguardUpdateMonitorCallback will handle the update + } + } + }; + + private final DefaultDataSubscriptionChangedListener mDefaultDataListener = + new DefaultDataSubscriptionChangedListener() { + @Override + public void onDefaultSubscriptionChanged(int subId) { + if (mOperatorNameViewController == null) { + initOperatorName(); + } + } + }; + @SuppressLint("ValidFragment") public CollapsedStatusBarFragment( StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory, @@ -153,6 +180,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue NetworkController networkController, StatusBarStateController statusBarStateController, CommandQueue commandQueue, + CarrierConfigTracker carrierConfigTracker, CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, OperatorNameViewController.Factory operatorNameViewControllerFactory, SecureSettings secureSettings, @@ -172,6 +200,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mNetworkController = networkController; mStatusBarStateController = statusBarStateController; mCommandQueue = commandQueue; + mCarrierConfigTracker = carrierConfigTracker; mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger; mOperatorNameViewControllerFactory = operatorNameViewControllerFactory; mSecureSettings = secureSettings; @@ -212,6 +241,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue initNotificationIconArea(); mSystemEventAnimator = new StatusBarSystemEventAnimator(mSystemIconArea, getResources()); + mCarrierConfigTracker.addCallback(mCarrierConfigCallback); + mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); } @VisibleForTesting @@ -283,6 +314,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue if (mNetworkController.hasEmergencyCryptKeeperText()) { mNetworkController.removeCallback(mSignalCallback); } + mCarrierConfigTracker.removeCallback(mCarrierConfigCallback); + mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener); } /** Initializes views related to the notification icon area. */ @@ -569,11 +602,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void initOperatorName() { - if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) { + int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + if (mCarrierConfigTracker.getShowOperatorNameInStatusBarConfig(subId)) { ViewStub stub = mStatusBar.findViewById(R.id.operator_name); mOperatorNameViewController = mOperatorNameViewControllerFactory.create((OperatorNameView) stub.inflate()); mOperatorNameViewController.init(); + // This view should not be visible on lock-screen + if (mKeyguardStateController.isShowing()) { + hideOperatorName(false); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 95a7316f7a58..ecaa28b0d4eb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy; import android.annotation.Nullable; +import android.view.View; import com.android.systemui.Dumpable; import com.android.systemui.demomode.DemoMode; @@ -24,6 +25,7 @@ import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChang import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.ref.WeakReference; public interface BatteryController extends DemoMode, Dumpable, CallbackController<BatteryStateChangeCallback> { @@ -35,7 +37,32 @@ public interface BatteryController extends DemoMode, Dumpable, /** * Sets if the current device is in power save mode. */ - void setPowerSaveMode(boolean powerSave); + default void setPowerSaveMode(boolean powerSave) { + setPowerSaveMode(powerSave, null); + } + + /** + * Sets if the current device is in power save mode. + * + * Can pass the view that triggered the request. + */ + void setPowerSaveMode(boolean powerSave, @Nullable View view); + + /** + * Gets a reference to the last view used when called {@link #setPowerSaveMode}. + */ + @Nullable + default WeakReference<View> getLastPowerSaverStartView() { + return null; + } + + /** + * Clears the last view used when called {@link #setPowerSaveMode}. + * + * Immediately after calling this, a call to {@link #getLastPowerSaverStartView()} should return + * {@code null}. + */ + default void clearLastPowerSaverStartView() {} /** * Returns {@code true} if the device is currently plugged in. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index 9e2c478fbd69..f4e83dd71cb7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.policy; import static android.os.BatteryManager.EXTRA_PRESENT; +import android.annotation.WorkerThread; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -28,6 +29,7 @@ import android.os.Handler; import android.os.PowerManager; import android.os.PowerSaveState; import android.util.Log; +import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -42,11 +44,14 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.power.EnhancedEstimates; +import com.android.systemui.util.Assert; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; /** * Default implementation of a {@link BatteryController}. This controller monitors for battery @@ -85,6 +90,11 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC private Estimate mEstimate; private boolean mFetchingEstimate = false; + // Use AtomicReference because we may request it from a different thread + // Use WeakReference because we are keeping a reference to a View that's not as long lived + // as this controller. + private AtomicReference<WeakReference<View>> mPowerSaverStartView = new AtomicReference<>(); + @VisibleForTesting public BatteryControllerImpl( Context context, @@ -126,7 +136,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } mDemoModeController.addCallback(this); updatePowerSave(); - updateEstimate(); + updateEstimateInBackground(); } @Override @@ -141,11 +151,22 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } @Override - public void setPowerSaveMode(boolean powerSave) { + public void setPowerSaveMode(boolean powerSave, View view) { + if (powerSave) mPowerSaverStartView.set(new WeakReference<>(view)); BatterySaverUtils.setPowerSaveMode(mContext, powerSave, /*needFirstTimeWarning*/ true); } @Override + public WeakReference<View> getLastPowerSaverStartView() { + return mPowerSaverStartView.get(); + } + + @Override + public void clearLastPowerSaverStartView() { + mPowerSaverStartView.set(null); + } + + @Override public void addCallback(@NonNull BatteryController.BatteryStateChangeCallback cb) { synchronized (mChangeCallbacks) { mChangeCallbacks.add(cb); @@ -320,7 +341,9 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } } + @WorkerThread private void updateEstimate() { + Assert.isNotMainThread(); // if the estimate has been cached we can just use that, otherwise get a new one and // throw it in the cache. mEstimate = Estimate.getCachedEstimateIfAvailable(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 9e1e87b9856f..4c43734836c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -31,6 +31,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.text.Spannable; import android.text.SpannableStringBuilder; +import android.text.TextUtils; import android.text.format.DateFormat; import android.text.style.CharacterStyle; import android.text.style.RelativeSizeSpan; @@ -291,7 +292,13 @@ public class Clock extends TextView implements final void updateClock() { if (mDemoMode) return; mCalendar.setTimeInMillis(System.currentTimeMillis()); - setText(getSmallTime()); + CharSequence smallTime = getSmallTime(); + // Setting text actually triggers a layout pass (because the text view is set to + // wrap_content width and TextView always relayouts for this). Avoid needless + // relayout if the text didn't actually change. + if (!TextUtils.equals(smallTime, getText())) { + setText(smallTime); + } setContentDescription(mContentDescriptionFormat.format(mCalendar.getTime())); } diff --git a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java index 14190fa752c3..5f7d74542fff 100644 --- a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java +++ b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java @@ -23,43 +23,73 @@ import android.content.IntentFilter; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; +import android.util.ArraySet; import android.util.SparseBooleanArray; +import androidx.annotation.NonNull; + +import com.android.internal.telephony.TelephonyIntents; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.policy.CallbackController; + +import java.util.Set; import javax.inject.Inject; /** - * Tracks the Carrier Config values. + * Tracks CarrierConfigs for each subId, as well as the default configuration. CarrierConfigurations + * do not trigger a device configuration event, so any UI that relies on carrier configurations must + * register with the tracker to get proper updates. + * + * The tracker also listens for `TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED` + * + * @see CarrierConfigChangedListener to listen for updates */ @SysUISingleton -public class CarrierConfigTracker extends BroadcastReceiver { +public class CarrierConfigTracker + extends BroadcastReceiver + implements CallbackController<CarrierConfigTracker.CarrierConfigChangedListener> { private final SparseBooleanArray mCallStrengthConfigs = new SparseBooleanArray(); private final SparseBooleanArray mNoCallingConfigs = new SparseBooleanArray(); private final SparseBooleanArray mCarrierProvisionsWifiMergedNetworks = new SparseBooleanArray(); + private final SparseBooleanArray mShowOperatorNameConfigs = new SparseBooleanArray(); private final CarrierConfigManager mCarrierConfigManager; + private final Set<CarrierConfigChangedListener> mListeners = new ArraySet<>(); + private final Set<DefaultDataSubscriptionChangedListener> mDataListeners = + new ArraySet<>(); private boolean mDefaultCallStrengthConfigLoaded; private boolean mDefaultCallStrengthConfig; private boolean mDefaultNoCallingConfigLoaded; private boolean mDefaultNoCallingConfig; private boolean mDefaultCarrierProvisionsWifiMergedNetworksLoaded; private boolean mDefaultCarrierProvisionsWifiMergedNetworks; + private boolean mDefaultShowOperatorNameConfigLoaded; + private boolean mDefaultShowOperatorNameConfig; @Inject - public CarrierConfigTracker(Context context, BroadcastDispatcher broadcastDispatcher) { - mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class); - broadcastDispatcher.registerReceiver( - this, new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); + public CarrierConfigTracker( + CarrierConfigManager carrierConfigManager, + BroadcastDispatcher broadcastDispatcher) { + mCarrierConfigManager = carrierConfigManager; + IntentFilter filter = new IntentFilter(); + filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); + filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); + broadcastDispatcher.registerReceiver(this, filter); } @Override public void onReceive(Context context, Intent intent) { - if (!CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) { - return; + String action = intent.getAction(); + if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { + updateFromNewCarrierConfig(intent); + } else if (TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) { + updateDefaultDataSubscription(intent); } + } + private void updateFromNewCarrierConfig(Intent intent) { final int subId = intent.getIntExtra( CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, SubscriptionManager.INVALID_SUBSCRIPTION_ID); @@ -84,6 +114,29 @@ public class CarrierConfigTracker extends BroadcastReceiver { mCarrierProvisionsWifiMergedNetworks.put(subId, config.getBoolean( CarrierConfigManager.KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL)); } + synchronized (mShowOperatorNameConfigs) { + mShowOperatorNameConfigs.put(subId, config.getBoolean( + CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL)); + } + + notifyCarrierConfigChanged(); + } + + private void updateDefaultDataSubscription(Intent intent) { + int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, -1); + notifyDefaultDataSubscriptionChanged(subId); + } + + private void notifyCarrierConfigChanged() { + for (CarrierConfigChangedListener l : mListeners) { + l.onCarrierConfigChanged(); + } + } + + private void notifyDefaultDataSubscriptionChanged(int subId) { + for (DefaultDataSubscriptionChangedListener l : mDataListeners) { + l.onDefaultSubscriptionChanged(subId); + } } /** @@ -139,4 +192,73 @@ public class CarrierConfigTracker extends BroadcastReceiver { } return mDefaultCarrierProvisionsWifiMergedNetworks; } + + /** + * Returns the KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL value for the default config + */ + public boolean getShowOperatorNameInStatusBarConfigDefault() { + if (!mDefaultShowOperatorNameConfigLoaded) { + mDefaultShowOperatorNameConfig = CarrierConfigManager.getDefaultConfig().getBoolean( + CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL); + mDefaultShowOperatorNameConfigLoaded = true; + } + + return mDefaultShowOperatorNameConfig; + } + + /** + * Returns the KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL value for the given subId, or the + * default value if no override exists + * + * @param subId the subscription id for which to query the config + */ + public boolean getShowOperatorNameInStatusBarConfig(int subId) { + if (mShowOperatorNameConfigs.indexOfKey(subId) >= 0) { + return mShowOperatorNameConfigs.get(subId); + } else { + return getShowOperatorNameInStatusBarConfigDefault(); + } + } + + @Override + public void addCallback(@NonNull CarrierConfigChangedListener listener) { + mListeners.add(listener); + } + + @Override + public void removeCallback(@NonNull CarrierConfigChangedListener listener) { + mListeners.remove(listener); + } + + /** */ + public void addDefaultDataSubscriptionChangedListener( + @NonNull DefaultDataSubscriptionChangedListener listener) { + mDataListeners.add(listener); + } + + /** */ + public void removeDataSubscriptionChangedListener( + DefaultDataSubscriptionChangedListener listener) { + mDataListeners.remove(listener); + } + + /** + * Called when carrier config changes + */ + public interface CarrierConfigChangedListener { + /** */ + void onCarrierConfigChanged(); + } + + /** + * Called when the default data subscription changes. Listeners may want to query + * subId-dependent configuration values when this event happens + */ + public interface DefaultDataSubscriptionChangedListener { + /** + * @param subId the new default data subscription id per + * {@link SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX} + */ + void onDefaultSubscriptionChanged(int subId); + } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index 4d34aa38b3cd..b70220d09749 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -576,7 +576,9 @@ public class BubblesManager implements Dumpable { @Override public void onEntryRemoved(NotificationEntry entry, @NotifCollection.CancellationReason int reason) { - BubblesManager.this.onEntryRemoved(entry); + if (reason == REASON_APP_CANCEL || reason == REASON_APP_CANCEL_ALL) { + BubblesManager.this.onEntryRemoved(entry); + } } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt index 98d57a3c5da8..23129d247ad5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt @@ -21,7 +21,8 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper -class ViewHierarchyAnimatorTest : SysuiTestCase() { +class +ViewHierarchyAnimatorTest : SysuiTestCase() { companion object { private const val TEST_DURATION = 1000L private val TEST_INTERPOLATOR = Interpolators.LINEAR @@ -522,6 +523,38 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() { } @Test + fun cleansUpListenersCorrectly() { + val firstChild = View(mContext) + firstChild.layoutParams = LinearLayout.LayoutParams(50 /* width */, 100 /* height */) + rootView.addView(firstChild) + val secondChild = View(mContext) + secondChild.layoutParams = LinearLayout.LayoutParams(50 /* width */, 100 /* height */) + rootView.addView(secondChild) + rootView.measure( + View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) + ) + rootView.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */) + + val success = ViewHierarchyAnimator.animateNextUpdate(rootView) + // Change all bounds. + rootView.measure( + View.MeasureSpec.makeMeasureSpec(150, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) + ) + rootView.layout(0 /* l */, 0 /* t */, 150 /* r */, 100 /* b */) + + assertTrue(success) + assertNotNull(rootView.getTag(R.id.tag_layout_listener)) + assertNotNull(firstChild.getTag(R.id.tag_layout_listener)) + assertNotNull(secondChild.getTag(R.id.tag_layout_listener)) + endAnimation(rootView) + assertNull(rootView.getTag(R.id.tag_layout_listener)) + assertNull(firstChild.getTag(R.id.tag_layout_listener)) + assertNull(secondChild.getTag(R.id.tag_layout_listener)) + } + + @Test fun doesNotAnimateInvisibleViews() { rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */) @@ -625,27 +658,6 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() { } @Test - fun doesNotAnimateExcludedBounds() { - rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */) - - val success = ViewHierarchyAnimator.animate( - rootView, - bounds = setOf(ViewHierarchyAnimator.Bound.LEFT, ViewHierarchyAnimator.Bound.TOP), - interpolator = TEST_INTERPOLATOR - ) - // Change all bounds. - rootView.layout(0 /* l */, 20 /* t */, 70 /* r */, 80 /* b */) - - assertTrue(success) - assertNotNull(rootView.getTag(R.id.tag_animator)) - advanceAnimation(rootView, 0.5f) - checkBounds(rootView, l = 5, t = 15, r = 70, b = 80) - endAnimation(rootView) - assertNull(rootView.getTag(R.id.tag_animator)) - checkBounds(rootView, l = 0, t = 20, r = 70, b = 80) - } - - @Test fun stopsAnimatingAfterSingleLayout() { rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt index 26296d67c71a..67fe0445e225 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt @@ -52,6 +52,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { @Mock lateinit var panel: MediaControlPanel @Mock lateinit var visualStabilityProvider: VisualStabilityProvider @Mock lateinit var mediaHostStatesManager: MediaHostStatesManager + @Mock lateinit var mediaHostState: MediaHostState @Mock lateinit var activityStarter: ActivityStarter @Mock @Main private lateinit var executor: DelayableExecutor @Mock lateinit var mediaDataManager: MediaDataManager @@ -188,4 +189,40 @@ class MediaCarouselControllerTest : SysuiTestCase() { verify(logger).logCarouselSettings() } + + @Test + fun testLocationChangeQs_logged() { + mediaCarouselController.onDesiredLocationChanged( + MediaHierarchyManager.LOCATION_QS, + mediaHostState, + animate = false) + verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QS) + } + + @Test + fun testLocationChangeQqs_logged() { + mediaCarouselController.onDesiredLocationChanged( + MediaHierarchyManager.LOCATION_QQS, + mediaHostState, + animate = false) + verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QQS) + } + + @Test + fun testLocationChangeLockscreen_logged() { + mediaCarouselController.onDesiredLocationChanged( + MediaHierarchyManager.LOCATION_LOCKSCREEN, + mediaHostState, + animate = false) + verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_LOCKSCREEN) + } + + @Test + fun testLocationChangeDream_logged() { + mediaCarouselController.onDesiredLocationChanged( + MediaHierarchyManager.LOCATION_DREAM_OVERLAY, + mediaHostState, + animate = false) + verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_DREAM_OVERLAY) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index f2e3edbc0761..538a9c763438 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.media +import android.app.PendingIntent import org.mockito.Mockito.`when` as whenever import android.content.Intent import android.graphics.Color @@ -104,6 +105,7 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory @Mock private lateinit var mediaCarouselController: MediaCarouselController @Mock private lateinit var falsingManager: FalsingManager + @Mock private lateinit var transitionParent: ViewGroup private lateinit var appIcon: ImageView private lateinit var albumView: ImageView private lateinit var titleText: TextView @@ -241,6 +243,10 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(viewHolder.seamlessText).thenReturn(seamlessText) whenever(viewHolder.seekBar).thenReturn(seekBar) + // Transition View + whenever(view.parent).thenReturn(transitionParent) + whenever(view.rootView).thenReturn(transitionParent) + // Action buttons whenever(viewHolder.actionPlayPause).thenReturn(actionPlayPause) whenever(viewHolder.getAction(R.id.actionPlayPause)).thenReturn(actionPlayPause) @@ -749,6 +755,20 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test + fun tapContentView_isLogged() { + val pendingIntent = mock(PendingIntent::class.java) + val captor = ArgumentCaptor.forClass(View.OnClickListener::class.java) + val data = mediaData.copy(clickIntent = pendingIntent) + player.attachPlayer(viewHolder) + player.bindPlayer(data, KEY) + verify(viewHolder.player).setOnClickListener(captor.capture()) + + captor.value.onClick(viewHolder.player) + + verify(logger).logTapContentView(anyInt(), eq(PACKAGE), eq(instanceId)) + } + + @Test fun logSeek() { player.attachPlayer(viewHolder) player.bindPlayer(mediaData, KEY) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index b9ff8775f2b8..1921cb624fde 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -726,6 +726,25 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test + fun testPlaybackActions_connecting() { + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + val stateActions = PlaybackState.ACTION_PLAY + val stateBuilder = PlaybackState.Builder() + .setState(PlaybackState.STATE_BUFFERING, 0, 10f) + .setActions(stateActions) + whenever(controller.playbackState).thenReturn(stateBuilder.build()) + + addNotificationAndLoad() + + assertThat(mediaDataCaptor.value!!.semanticActions).isNotNull() + val actions = mediaDataCaptor.value!!.semanticActions!! + + assertThat(actions.playOrPause).isNotNull() + assertThat(actions.playOrPause!!.contentDescription).isEqualTo( + context.getString(R.string.controls_media_button_connecting)) + } + + @Test fun testPlaybackActions_reservedSpace() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt index e6f48ecd37de..10eeb11faa05 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt @@ -265,20 +265,58 @@ public class MediaDeviceManagerTest : SysuiTestCase() { } @Test - fun onAboutToConnectDeviceChangedWithNonNullParams() { + fun onAboutToConnectDeviceAdded_findsDeviceInfoFromAddress() { manager.onMediaDataLoaded(KEY, null, mediaData) // Run and reset the executors and listeners so we only focus on new events. fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() reset(listener) + // Ensure we'll get device info when using the address + val fullMediaDevice = mock(MediaDevice::class.java) + val address = "fakeAddress" + val nameFromDevice = "nameFromDevice" + val iconFromDevice = mock(Drawable::class.java) + whenever(lmm.getMediaDeviceById(eq(address))).thenReturn(fullMediaDevice) + whenever(fullMediaDevice.name).thenReturn(nameFromDevice) + whenever(fullMediaDevice.iconWithoutBackground).thenReturn(iconFromDevice) + + // WHEN the about-to-connect device changes to non-null val deviceCallback = captureCallback() + val nameFromParam = "nameFromParam" + val iconFromParam = mock(Drawable::class.java) + deviceCallback.onAboutToConnectDeviceAdded(address, nameFromParam, iconFromParam) + assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1) + + // THEN the about-to-connect device based on the address is returned + val data = captureDeviceData(KEY) + assertThat(data.enabled).isTrue() + assertThat(data.name).isEqualTo(nameFromDevice) + assertThat(data.name).isNotEqualTo(nameFromParam) + assertThat(data.icon).isEqualTo(iconFromDevice) + assertThat(data.icon).isNotEqualTo(iconFromParam) + } + + @Test + fun onAboutToConnectDeviceAdded_cantFindDeviceInfoFromAddress() { + manager.onMediaDataLoaded(KEY, null, mediaData) + // Run and reset the executors and listeners so we only focus on new events. + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + reset(listener) + + // Ensure we can't get device info based on the address + val address = "fakeAddress" + whenever(lmm.getMediaDeviceById(eq(address))).thenReturn(null) + // WHEN the about-to-connect device changes to non-null + val deviceCallback = captureCallback() val name = "AboutToConnectDeviceName" val mockIcon = mock(Drawable::class.java) - deviceCallback.onAboutToConnectDeviceChanged(name, mockIcon) + deviceCallback.onAboutToConnectDeviceAdded(address, name, mockIcon) assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1) - // THEN the about-to-connect device is returned + + // THEN the about-to-connect device based on the parameters is returned val data = captureDeviceData(KEY) assertThat(data.enabled).isTrue() assertThat(data.name).isEqualTo(name) @@ -286,21 +324,21 @@ public class MediaDeviceManagerTest : SysuiTestCase() { } @Test - fun onAboutToConnectDeviceChangedWithNullParams() { + fun onAboutToConnectDeviceAddedThenRemoved_usesNormalDevice() { manager.onMediaDataLoaded(KEY, null, mediaData) fakeBgExecutor.runAllReady() val deviceCallback = captureCallback() // First set a non-null about-to-connect device - deviceCallback.onAboutToConnectDeviceChanged( - "AboutToConnectDeviceName", mock(Drawable::class.java) + deviceCallback.onAboutToConnectDeviceAdded( + "fakeAddress", "AboutToConnectDeviceName", mock(Drawable::class.java) ) // Run and reset the executors and listeners so we only focus on new events. fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() reset(listener) - // WHEN the about-to-connect device changes to null - deviceCallback.onAboutToConnectDeviceChanged(null, null) + // WHEN hasDevice switches to false + deviceCallback.onAboutToConnectDeviceRemoved() assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1) // THEN the normal device is returned val data = captureDeviceData(KEY) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt index 88c451499d21..27c039dcf29c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt @@ -24,7 +24,7 @@ import android.media.AudioDeviceAttributes import android.media.AudioDeviceInfo import android.media.AudioManager import android.media.AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION -import android.test.suitebuilder.annotation.SmallTest +import androidx.test.filters.SmallTest import com.android.settingslib.media.DeviceIconUtil import com.android.settingslib.media.LocalMediaManager import com.android.systemui.R @@ -95,7 +95,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { muteAwaitConnectionManager.startListening() - verify(localMediaManager, never()).dispatchAboutToConnectDeviceChanged(any(), any()) + verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any()) } @Test @@ -104,7 +104,9 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { muteAwaitConnectionManager.startListening() - verify(localMediaManager).dispatchAboutToConnectDeviceChanged(eq(DEVICE_NAME), eq(icon)) + verify(localMediaManager).dispatchAboutToConnectDeviceAdded( + eq(DEVICE_ADDRESS), eq(DEVICE_NAME), eq(icon) + ) } @Test @@ -114,7 +116,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_UNKNOWN)) - verify(localMediaManager, never()).dispatchAboutToConnectDeviceChanged(any(), any()) + verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any()) } @Test @@ -125,7 +127,9 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_MEDIA)) - verify(localMediaManager).dispatchAboutToConnectDeviceChanged(eq(DEVICE_NAME), eq(icon)) + verify(localMediaManager).dispatchAboutToConnectDeviceAdded( + eq(DEVICE_ADDRESS), eq(DEVICE_NAME), eq(icon) + ) } @Test @@ -135,7 +139,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_MEDIA)) - verify(localMediaManager, never()).dispatchAboutToConnectDeviceChanged(any(), any()) + verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any()) } @Test @@ -155,7 +159,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { ) muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, otherDevice, intArrayOf(USAGE_MEDIA)) - verify(localMediaManager, never()).dispatchAboutToConnectDeviceChanged(any(), any()) + verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any()) } @Test @@ -167,7 +171,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_UNKNOWN)) - verify(localMediaManager, never()).dispatchAboutToConnectDeviceChanged(any(), any()) + verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any()) } @Test @@ -179,7 +183,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_MEDIA)) - verify(localMediaManager).dispatchAboutToConnectDeviceChanged(eq(null), eq(null)) + verify(localMediaManager).dispatchAboutToConnectDeviceRemoved() } private fun getMuteAwaitListener(): AudioManager.MuteAwaitConnectionCallback { @@ -191,11 +195,12 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { } } +private const val DEVICE_ADDRESS = "DeviceAddress" private const val DEVICE_NAME = "DeviceName" private val DEVICE = AudioDeviceAttributes( AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_USB_HEADSET, - "address", + DEVICE_ADDRESS, DEVICE_NAME, listOf(), listOf(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt index ef5315428a60..9a01464fc869 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt @@ -277,6 +277,17 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { } @Test + fun commandQueueCallback_invalidStateParam_noChipShown() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + 100, + routeInfo, + null + ) + + verify(windowManager, never()).addView(any(), any()) + } + + @Test fun receivesNewStateFromCommandQueue_isLogged() { commandQueueCallback.updateMediaTapToTransferSenderDisplay( StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST, diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java index a156820ad141..1ffa9dd57aa9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java @@ -25,29 +25,48 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.Notification; import android.app.NotificationManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.IntentFilter; import android.os.BatteryManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; import android.test.suitebuilder.annotation.SmallTest; - -import androidx.test.runner.AndroidJUnit4; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.NotificationChannels; 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; + +import java.lang.ref.WeakReference; @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper public class PowerNotificationWarningsTest extends SysuiTestCase { public static final String FORMATTED_45M = "0h 45m"; @@ -55,14 +74,34 @@ public class PowerNotificationWarningsTest extends SysuiTestCase { private final NotificationManager mMockNotificationManager = mock(NotificationManager.class); private PowerNotificationWarnings mPowerNotificationWarnings; + @Mock + private BatteryController mBatteryController; + @Mock + private DialogLaunchAnimator mDialogLaunchAnimator; + @Mock + private View mView; + + private BroadcastReceiver mReceiver; + @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + Context wrapper = new ContextWrapper(mContext) { + @Override + public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, + IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) { + mReceiver = receiver; + return null; + } + }; + // Test Instance. mContext.addMockSystemService(NotificationManager.class, mMockNotificationManager); ActivityStarter starter = mDependency.injectMockDependency(ActivityStarter.class); BroadcastSender broadcastSender = mDependency.injectMockDependency(BroadcastSender.class); - mPowerNotificationWarnings = new PowerNotificationWarnings(mContext, starter, - broadcastSender); + mPowerNotificationWarnings = new PowerNotificationWarnings(wrapper, starter, + broadcastSender, () -> mBatteryController, mDialogLaunchAnimator); BatteryStateSnapshot snapshot = new BatteryStateSnapshot(100, false, false, 1, BatteryManager.BATTERY_HEALTH_GOOD, 5, 15); mPowerNotificationWarnings.updateSnapshot(snapshot); @@ -168,4 +207,52 @@ public class PowerNotificationWarningsTest extends SysuiTestCase { mPowerNotificationWarnings.mUsbHighTempDialog.dismiss(); } + + @Test + public void testDialogStartedFromLauncher_viewVisible() { + when(mBatteryController.getLastPowerSaverStartView()) + .thenReturn(new WeakReference<>(mView)); + when(mView.isAggregatedVisible()).thenReturn(true); + + Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION); + intent.putExtras(new Bundle()); + + mReceiver.onReceive(mContext, intent); + + verify(mDialogLaunchAnimator).showFromView(any(), eq(mView)); + + mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss(); + } + + @Test + public void testDialogStartedNotFromLauncher_viewNotVisible() { + when(mBatteryController.getLastPowerSaverStartView()) + .thenReturn(new WeakReference<>(mView)); + when(mView.isAggregatedVisible()).thenReturn(false); + + Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION); + intent.putExtras(new Bundle()); + + mReceiver.onReceive(mContext, intent); + + verify(mDialogLaunchAnimator, never()).showFromView(any(), any()); + + assertThat(mPowerNotificationWarnings.getSaverConfirmationDialog().isShowing()).isTrue(); + mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss(); + } + + @Test + public void testDialogShownNotFromLauncher() { + when(mBatteryController.getLastPowerSaverStartView()).thenReturn(null); + + Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION); + intent.putExtras(new Bundle()); + + mReceiver.onReceive(mContext, intent); + + verify(mDialogLaunchAnimator, never()).showFromView(any(), any()); + + assertThat(mPowerNotificationWarnings.getSaverConfirmationDialog().isShowing()).isTrue(); + mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt index 1bf83513d472..3d9205ee0354 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt @@ -21,6 +21,7 @@ import android.os.Handler import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper +import android.view.View import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.systemui.SysuiTestCase @@ -38,6 +39,9 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -63,6 +67,8 @@ class BatterySaverTileTest : SysuiTestCase() { private lateinit var qsLogger: QSLogger @Mock private lateinit var batteryController: BatteryController + @Mock + private lateinit var view: View private lateinit var secureSettings: SecureSettings private lateinit var testableLooper: TestableLooper private lateinit var tile: BatterySaverTile @@ -105,4 +111,26 @@ class BatterySaverTileTest : SysuiTestCase() { assertEquals(USER + 1, tile.mSetting.currentUser) } + + @Test + fun testClickingPowerSavePassesView() { + tile.onPowerSaveChanged(true) + tile.handleClick(view) + + tile.onPowerSaveChanged(false) + tile.handleClick(view) + + verify(batteryController).setPowerSaveMode(true, view) + verify(batteryController).setPowerSaveMode(false, view) + } + + @Test + fun testStopListeningClearsViewInController() { + clearInvocations(batteryController) + tile.handleSetListening(true) + verify(batteryController, never()).clearLastPowerSaverStartView() + + tile.handleSetListening(false) + verify(batteryController).clearLastPowerSaverStartView() + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 48f820626fac..7687d1204541 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -66,6 +66,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.FakeSettings; import com.google.android.collect.Lists; @@ -109,6 +110,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { private UserInfo mCurrentUser; private UserInfo mSecondaryUser; private UserInfo mWorkUser; + private FakeSettings mSettings; private TestNotificationLockscreenUserManager mLockscreenUserManager; private NotificationEntry mCurrentUserNotif; private NotificationEntry mSecondaryUserNotif; @@ -120,6 +122,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); int currentUserId = ActivityManager.getCurrentUser(); + mSettings = new FakeSettings(); + mSettings.setUserId(ActivityManager.getCurrentUser()); mCurrentUser = new UserInfo(currentUserId, "", 0); mSecondaryUser = new UserInfo(currentUserId + 1, "", 0); mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0, @@ -157,48 +161,45 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testLockScreenShowNotificationsFalse() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications()); } @Test public void testLockScreenShowNotificationsTrue() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications()); } @Test public void testLockScreenAllowPrivateNotificationsTrue() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id)); } @Test public void testLockScreenAllowPrivateNotificationsFalse() { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id)); } @Test public void testLockScreenAllowsWorkPrivateNotificationsFalse() { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mWorkUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id)); } @Test public void testLockScreenAllowsWorkPrivateNotificationsTrue() { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mWorkUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id)); } @@ -206,8 +207,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testCurrentUserPrivateNotificationsNotRedacted() { // GIVEN current user doesn't allow private notifications to show - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN current user's notification is redacted @@ -217,8 +218,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testCurrentUserPrivateNotificationsRedacted() { // GIVEN current user allows private notifications to show - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mCurrentUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN current user's notification isn't redacted @@ -228,8 +229,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkPrivateNotificationsRedacted() { // GIVEN work profile doesn't private notifications to show - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mWorkUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN work profile notification is redacted @@ -239,8 +240,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkPrivateNotificationsNotRedacted() { // GIVEN work profile allows private notifications to show - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mWorkUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN work profile notification isn't redacted @@ -250,12 +251,11 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() { // GIVEN work profile allows private notifications to show but the other users don't - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id); - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mSecondaryUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); @@ -270,12 +270,11 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkProfileRedacted_otherUsersNotRedacted() { // GIVEN work profile doesn't allow private notifications to show but the other users do - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id); - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id); - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mSecondaryUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); @@ -291,10 +290,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { public void testSecondaryUserNotRedacted_currentUserRedacted() { // GIVEN secondary profile allows private notifications to show but the current user // doesn't allow private notifications to show - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mSecondaryUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); @@ -328,10 +326,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testShowSilentNotifications_settingSaysShow() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); NotificationEntry entry = new NotificationEntryBuilder() .setImportance(IMPORTANCE_LOW) @@ -343,10 +340,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testShowSilentNotifications_settingSaysHide() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); final Notification notification = mock(Notification.class); when(notification.isForegroundService()).thenReturn(true); @@ -360,10 +356,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testShowSilentNotificationsPeopleBucket_settingSaysHide() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); final Notification notification = mock(Notification.class); when(notification.isForegroundService()).thenReturn(true); @@ -377,10 +372,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testShowSilentNotificationsMediaBucket_settingSaysHide() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); final Notification notification = mock(Notification.class); when(notification.isForegroundService()).thenReturn(true); @@ -396,8 +390,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testKeyguardNotificationSuppressors() { // GIVEN a notification that should be shown on the lockscreen - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); final NotificationEntry entry = new NotificationEntryBuilder() .setImportance(IMPORTANCE_HIGH) .build(); @@ -433,6 +427,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { Handler.createAsync(Looper.myLooper()), mDeviceProvisionedController, mKeyguardStateController, + mSettings, mock(DumpManager.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt index 9a4e10cec159..497a857d1deb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt @@ -22,8 +22,6 @@ import android.view.View.VISIBLE import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.statusbar.StatusBarState.KEYGUARD -import com.android.systemui.statusbar.StatusBarState.SHADE import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow @@ -142,11 +140,13 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { } @Test - fun computeHeight_returnsLessThanAvailableSpaceUsedToCalculateMaxNotifications() { + fun computeHeight_returnsAtMostSpaceAvailable_withGapBeforeShelf() { val rowHeight = ROW_HEIGHT val shelfHeight = SHELF_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + NOTIFICATION_PADDING val availableSpace = totalSpaceForEachRow * 2 + + // All rows in separate sections (default setup). val rows = listOf( createMockRow(rowHeight), @@ -157,6 +157,28 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { assertThat(maxNotifications).isEqualTo(2) val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) + assertThat(height).isAtMost(availableSpace + GAP_HEIGHT + SHELF_HEIGHT) + } + + @Test + fun computeHeight_returnsAtMostSpaceAvailable_noGapBeforeShelf() { + val rowHeight = ROW_HEIGHT + val shelfHeight = SHELF_HEIGHT + val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + NOTIFICATION_PADDING + val availableSpace = totalSpaceForEachRow * 1 + + // Both rows are in the same section. + whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) + .thenReturn(0f) + val rows = + listOf( + createMockRow(rowHeight), + createMockRow(rowHeight)) + + val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) + assertThat(maxNotifications).isEqualTo(1) + + val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) assertThat(height).isAtMost(availableSpace + SHELF_HEIGHT) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index 7ef656c780a6..8f3df09d913d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -947,6 +947,15 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { verify(mKeyguardStatusBarViewController).setAlpha(statusBarAlpha); } + @Test + public void testQsToBeImmediatelyExpandedInSplitShade() { + enableSplitShade(/* enabled= */ true); + + mNotificationPanelViewController.onTrackingStarted(); + + assertThat(mNotificationPanelViewController.mQsExpandImmediate).isTrue(); + } + private void triggerPositionClockAndNotifications() { mNotificationPanelViewController.closeQs(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 509509401d13..497f7fba2dbf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -59,6 +59,7 @@ import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentCom import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; @@ -90,6 +91,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private OperatorNameViewController mOperatorNameViewController; private SecureSettings mSecureSettings; private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + private final CarrierConfigTracker mCarrierConfigTracker = mock(CarrierConfigTracker.class); @Mock private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory; @@ -373,6 +375,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mNetworkController, mStatusBarStateController, mCommandQueue, + mCarrierConfigTracker, new CollapsedStatusBarFragmentLogger( new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)), new DisableFlagsLogger() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java index 2577dbdbb593..fec2123b304a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java @@ -18,6 +18,10 @@ package com.android.systemui.statusbar.policy; import static android.os.BatteryManager.EXTRA_PRESENT; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker; + import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -30,20 +34,24 @@ import android.os.PowerSaveState; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.View; +import com.android.dx.mockito.inline.extended.StaticInOrder; +import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.power.EnhancedEstimates; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; - +import org.mockito.MockitoSession; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -53,11 +61,19 @@ public class BatteryControllerTest extends SysuiTestCase { @Mock private PowerManager mPowerManager; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private DemoModeController mDemoModeController; + @Mock private View mView; private BatteryControllerImpl mBatteryController; + private MockitoSession mMockitoSession; + @Before - public void setUp() { + public void setUp() throws IllegalStateException { MockitoAnnotations.initMocks(this); + mMockitoSession = mockitoSession() + .initMocks(this) + .mockStatic(BatterySaverUtils.class) + .startMocking(); + mBatteryController = new BatteryControllerImpl(getContext(), mock(EnhancedEstimates.class), mPowerManager, @@ -65,9 +81,15 @@ public class BatteryControllerTest extends SysuiTestCase { mDemoModeController, new Handler(), new Handler()); + // Can throw if updateEstimate is called on the main thread mBatteryController.init(); } + @After + public void tearDown() { + mMockitoSession.finishMocking(); + } + @Test public void testBatteryInitialized() { Assert.assertTrue(mBatteryController.mHasReceivedBattery); @@ -135,4 +157,43 @@ public class BatteryControllerTest extends SysuiTestCase { // THEN it is informed about the battery state verify(cb, atLeastOnce()).onBatteryUnknownStateChanged(true); } + + @Test + public void testBatteryUtilsCalledOnSetPowerSaveMode() { + mBatteryController.setPowerSaveMode(true, mView); + mBatteryController.setPowerSaveMode(false, mView); + + StaticInOrder inOrder = inOrder(staticMockMarker(BatterySaverUtils.class)); + inOrder.verify(() -> BatterySaverUtils.setPowerSaveMode(getContext(), true, true)); + inOrder.verify(() -> BatterySaverUtils.setPowerSaveMode(getContext(), false, true)); + } + + @Test + public void testSaveViewReferenceWhenSettingPowerSaveMode() { + mBatteryController.setPowerSaveMode(false, mView); + + Assert.assertNull(mBatteryController.getLastPowerSaverStartView()); + + mBatteryController.setPowerSaveMode(true, mView); + + Assert.assertSame(mView, mBatteryController.getLastPowerSaverStartView().get()); + } + + @Test + public void testClearViewReference() { + mBatteryController.setPowerSaveMode(true, mView); + mBatteryController.clearLastPowerSaverStartView(); + + Assert.assertNull(mBatteryController.getLastPowerSaverStartView()); + } + + @Test + public void testBatteryEstimateFetch_doesNotThrow() throws IllegalStateException { + mBatteryController.getEstimatedTimeRemainingString( + (String estimate) -> { + // don't care about the result + }); + TestableLooper.get(this).processAllMessages(); + // Should not throw an exception + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java index e66491e4cbd1..e660e1f2d845 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java @@ -16,6 +16,7 @@ package com.android.systemui.util.settings; +import android.annotation.UserIdInt; import android.content.ContentResolver; import android.database.ContentObserver; import android.net.Uri; @@ -34,6 +35,8 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>(); public static final Uri CONTENT_URI = Uri.parse("content://settings/fake"); + @UserIdInt + private int mUserId = UserHandle.USER_CURRENT; public FakeSettings() { } @@ -85,9 +88,13 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti return Uri.withAppendedPath(CONTENT_URI, name); } + public void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + @Override public int getUserId() { - return UserHandle.USER_CURRENT; + return mUserId; } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java index 50c1e73f6aac..9ca4db4c1843 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java @@ -16,6 +16,7 @@ package com.android.systemui.utils.leaks; import android.os.Bundle; import android.testing.LeakCheck; +import android.view.View; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; @@ -47,6 +48,11 @@ public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCal } @Override + public void setPowerSaveMode(boolean powerSave, View view) { + + } + + @Override public boolean isPluggedIn() { return false; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index 40657fb61412..ce7924a2a4a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -807,7 +807,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { assertTrue(mBubbleController.hasBubbles()); // Removes the notification - mEntryListener.onEntryRemoved(mRow, 0); + mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL); assertFalse(mBubbleController.hasBubbles()); } @@ -938,7 +938,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // WHEN the summary is cancelled by the app - mEntryListener.onEntryRemoved(groupSummary.getEntry(), 0); + mEntryListener.onEntryRemoved(groupSummary.getEntry(), REASON_APP_CANCEL); // THEN the summary and its children are removed from bubble data assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 62ba0c821f4e..b263fb377e82 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -40,6 +40,8 @@ import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; +import android.util.SparseLongArray; import android.view.accessibility.MagnificationAnimationCallback; import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; @@ -97,20 +99,22 @@ public class MagnificationController implements WindowMagnificationManager.Callb private final boolean mSupportWindowMagnification; @GuardedBy("mLock") - private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE; + private final SparseIntArray mCurrentMagnificationModeArray = new SparseIntArray(); @GuardedBy("mLock") - private int mLastActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + private final SparseIntArray mLastMagnificationActivatedModeArray = new SparseIntArray(); // Track the active user to reset the magnification and get the associated user settings. private @UserIdInt int mUserId = UserHandle.USER_SYSTEM; @GuardedBy("mLock") private final SparseBooleanArray mIsImeVisibleArray = new SparseBooleanArray(); - private long mWindowModeEnabledTime = 0; - private long mFullScreenModeEnabledTime = 0; + @GuardedBy("mLock") + private final SparseLongArray mWindowModeEnabledTimeArray = new SparseLongArray(); + @GuardedBy("mLock") + private final SparseLongArray mFullScreenModeEnabledTimeArray = new SparseLongArray(); @GuardedBy("mLock") - @Nullable - private WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks - mAccessibilityCallbacksDelegate; + private final SparseArray<WindowManagerInternal.AccessibilityControllerInternal + .UiChangesForAccessibilityCallbacks> mAccessibilityCallbacksDelegateArray = + new SparseArray<>(); /** * A callback to inform the magnification transition result on the given display. @@ -333,20 +337,20 @@ public class MagnificationController implements WindowMagnificationManager.Callb } @GuardedBy("mLock") - private void setActivatedModeAndSwitchDelegate(int mode) { - mActivatedMode = mode; - assignMagnificationWindowManagerDelegateByMode(mode); + private void setCurrentMagnificationModeAndSwitchDelegate(int displayId, int mode) { + mCurrentMagnificationModeArray.put(displayId, mode); + assignMagnificationWindowManagerDelegateByMode(displayId, mode); } - private void assignMagnificationWindowManagerDelegateByMode(int mode) { - synchronized (mLock) { - if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) { - mAccessibilityCallbacksDelegate = getFullScreenMagnificationController(); - } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { - mAccessibilityCallbacksDelegate = getWindowMagnificationMgr(); - } else { - mAccessibilityCallbacksDelegate = null; - } + @GuardedBy("mLock") + private void assignMagnificationWindowManagerDelegateByMode(int displayId, int mode) { + if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) { + mAccessibilityCallbacksDelegateArray.put(displayId, + getFullScreenMagnificationController()); + } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { + mAccessibilityCallbacksDelegateArray.put(displayId, getWindowMagnificationMgr()); + } else { + mAccessibilityCallbacksDelegateArray.delete(displayId); } } @@ -356,7 +360,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks delegate; synchronized (mLock) { - delegate = mAccessibilityCallbacksDelegate; + delegate = mAccessibilityCallbacksDelegateArray.get(displayId); } if (delegate != null) { delegate.onRectangleOnScreenRequested(displayId, left, top, right, bottom); @@ -378,25 +382,26 @@ public class MagnificationController implements WindowMagnificationManager.Callb } } - // TODO : supporting multi-display (b/182227245). @Override public void onWindowMagnificationActivationState(int displayId, boolean activated) { if (activated) { - mWindowModeEnabledTime = SystemClock.uptimeMillis(); - synchronized (mLock) { - setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); - mLastActivatedMode = mActivatedMode; + mWindowModeEnabledTimeArray.put(displayId, SystemClock.uptimeMillis()); + setCurrentMagnificationModeAndSwitchDelegate(displayId, + ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + mLastMagnificationActivatedModeArray.put(displayId, + ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); } logMagnificationModeWithImeOnIfNeeded(displayId); disableFullScreenMagnificationIfNeeded(displayId); } else { - logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, - SystemClock.uptimeMillis() - mWindowModeEnabledTime); - + long duration; synchronized (mLock) { - setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_NONE); + setCurrentMagnificationModeAndSwitchDelegate(displayId, + ACCESSIBILITY_MAGNIFICATION_MODE_NONE); + duration = SystemClock.uptimeMillis() - mWindowModeEnabledTimeArray.get(displayId); } + logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, duration); } updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); } @@ -437,21 +442,24 @@ public class MagnificationController implements WindowMagnificationManager.Callb @Override public void onFullScreenMagnificationActivationState(int displayId, boolean activated) { if (activated) { - mFullScreenModeEnabledTime = SystemClock.uptimeMillis(); - synchronized (mLock) { - setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - mLastActivatedMode = mActivatedMode; + mFullScreenModeEnabledTimeArray.put(displayId, SystemClock.uptimeMillis()); + setCurrentMagnificationModeAndSwitchDelegate(displayId, + ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + mLastMagnificationActivatedModeArray.put(displayId, + ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); } logMagnificationModeWithImeOnIfNeeded(displayId); disableWindowMagnificationIfNeeded(displayId); } else { - logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, - SystemClock.uptimeMillis() - mFullScreenModeEnabledTime); - + long duration; synchronized (mLock) { - setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_NONE); + setCurrentMagnificationModeAndSwitchDelegate(displayId, + ACCESSIBILITY_MAGNIFICATION_MODE_NONE); + duration = SystemClock.uptimeMillis() + - mFullScreenModeEnabledTimeArray.get(displayId); } + logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, duration); } updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); } @@ -477,9 +485,10 @@ public class MagnificationController implements WindowMagnificationManager.Callb * Returns the last activated magnification mode. If there is no activated magnifier before, it * returns fullscreen mode by default. */ - public int getLastActivatedMode() { + public int getLastMagnificationActivatedMode(int displayId) { synchronized (mLock) { - return mLastActivatedMode; + return mLastMagnificationActivatedModeArray.get(displayId, + ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); } } @@ -522,7 +531,10 @@ public class MagnificationController implements WindowMagnificationManager.Callb synchronized (mLock) { fullMagnificationController = mFullScreenMagnificationController; windowMagnificationManager = mWindowMagnificationMgr; - mLastActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + mAccessibilityCallbacksDelegateArray.clear(); + mCurrentMagnificationModeArray.clear(); + mLastMagnificationActivatedModeArray.clear(); + mIsImeVisibleArray.clear(); } mScaleProvider.onUserChanged(userId); @@ -547,6 +559,10 @@ public class MagnificationController implements WindowMagnificationManager.Callb if (mWindowMagnificationMgr != null) { mWindowMagnificationMgr.onDisplayRemoved(displayId); } + mAccessibilityCallbacksDelegateArray.delete(displayId); + mCurrentMagnificationModeArray.delete(displayId); + mLastMagnificationActivatedModeArray.delete(displayId); + mIsImeVisibleArray.delete(displayId); } mScaleProvider.onDisplayRemoved(displayId); } @@ -587,16 +603,17 @@ public class MagnificationController implements WindowMagnificationManager.Callb } private void logMagnificationModeWithImeOnIfNeeded(int displayId) { - final int mode; + final int currentActivateMode; synchronized (mLock) { + currentActivateMode = mCurrentMagnificationModeArray.get(displayId, + ACCESSIBILITY_MAGNIFICATION_MODE_NONE); if (!mIsImeVisibleArray.get(displayId, false) - || mActivatedMode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) { + || currentActivateMode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) { return; } - mode = mActivatedMode; } - logMagnificationModeWithIme(mode); + logMagnificationModeWithIme(currentActivateMode); } /** diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java index 3e07b095fd29..a356ae6799d7 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java @@ -341,7 +341,8 @@ public class MagnificationProcessor { ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)) { return MAGNIFICATION_MODE_FULLSCREEN; } else { - return (mController.getLastActivatedMode() == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) + return (mController.getLastMagnificationActivatedMode(displayId) + == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) ? MAGNIFICATION_MODE_WINDOW : MAGNIFICATION_MODE_FULLSCREEN; } diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java index 7e3ede15aa04..d75d6484bec3 100644 --- a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java +++ b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java @@ -34,6 +34,7 @@ import java.util.HashSet; import java.util.List; import java.util.Queue; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -374,7 +375,8 @@ public class BackupTransportClient { private <T> T getFutureResult(AndroidFuture<T> future) { try { return future.get(600, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { + } catch (InterruptedException | ExecutionException | TimeoutException + | CancellationException e) { Slog.w(TAG, "Failed to get result from transport:", e); return null; } finally { @@ -403,7 +405,11 @@ public class BackupTransportClient { void cancelActiveFutures() { synchronized (mActiveFuturesLock) { for (AndroidFuture<?> future : mActiveFutures) { - future.cancel(true); + try { + future.cancel(true); + } catch (CancellationException ignored) { + // This is expected, so ignore the exception. + } } mActiveFutures.clear(); } diff --git a/services/core/Android.bp b/services/core/Android.bp index 5406f711b73d..89c8ca567dd9 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -120,7 +120,6 @@ java_library_static { "java/com/android/server/am/EventLogTags.logtags", "java/com/android/server/wm/EventLogTags.logtags", "java/com/android/server/policy/EventLogTags.logtags", - ":services.connectivity-tiramisu-sources", ], libs: [ @@ -174,9 +173,6 @@ java_library_static { "overlayable_policy_aidl-java", "SurfaceFlingerProperties", "com.android.sysprop.watchdog", - // This is used for services.connectivity-tiramisu-sources. - // TODO: delete when NetworkStatsService is moved to the mainline module. - "net-utils-device-common-bpf", ], javac_shard_size: 50, } diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index f34c5062144e..06f698efde2b 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -1208,6 +1208,11 @@ public abstract class PackageManagerInternal { public abstract SharedUserApi getSharedUserApi(int sharedUserAppId); /** + * Returns if the given uid is privileged or not. + */ + public abstract boolean isUidPrivileged(int uid); + + /** * Initiates a package state mutation request, returning the current state as known by * PackageManager. This allows the later commit request to compare the initial values and * determine if any state was changed or any packages were updated since the whole request diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index e6bf109ff84c..7ab3008585ce 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -2132,15 +2132,19 @@ class StorageManagerService extends IStorageManager.Stub } } - PackageMonitor monitor = new PackageMonitor() { + if (mPackageMonitorsForUser.get(userId) == null) { + PackageMonitor monitor = new PackageMonitor() { @Override public void onPackageRemoved(String packageName, int uid) { updateLegacyStorageApps(packageName, uid, false); } }; - // TODO(b/149391976): Use different handler? - monitor.register(mContext, user, true, mHandler); - mPackageMonitorsForUser.put(userId, monitor); + // TODO(b/149391976): Use different handler? + monitor.register(mContext, user, true, mHandler); + mPackageMonitorsForUser.put(userId, monitor); + } else { + Slog.w(TAG, "PackageMonitor is already registered for: " + userId); + } } private static long getLastAccessTime(AppOpsManager manager, diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index e6b7a4c287cd..b059cc7e2aa2 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -2977,19 +2977,21 @@ public class AccountManagerService * outside of those expected to be injected by the AccountManager, e.g. * ANDORID_PACKAGE_NAME. */ - String token = readCachedTokenInternal( + TokenCache.Value cachedToken = readCachedTokenInternal( accounts, account, authTokenType, callerPkg, callerPkgSigDigest); - if (token != null) { + if (cachedToken != null) { logGetAuthTokenMetrics(callerPkg, account.type); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator."); } Bundle result = new Bundle(); - result.putString(AccountManager.KEY_AUTHTOKEN, token); + result.putString(AccountManager.KEY_AUTHTOKEN, cachedToken.token); + result.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, + cachedToken.expiryEpochMillis); result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); onResult(response, result); @@ -6121,7 +6123,7 @@ public class AccountManagerService } } - protected String readCachedTokenInternal( + protected TokenCache.Value readCachedTokenInternal( UserAccounts accounts, Account account, String tokenType, diff --git a/services/core/java/com/android/server/accounts/TokenCache.java b/services/core/java/com/android/server/accounts/TokenCache.java index 66e550fe3c4c..9427ee41cfdc 100644 --- a/services/core/java/com/android/server/accounts/TokenCache.java +++ b/services/core/java/com/android/server/accounts/TokenCache.java @@ -20,8 +20,6 @@ import android.accounts.Account; import android.util.LruCache; import android.util.Pair; -import com.android.internal.util.Preconditions; - import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -35,7 +33,8 @@ import java.util.Objects; private static final int MAX_CACHE_CHARS = 64000; - private static class Value { + /** Package private*/ + static class Value { public final String token; public final long expiryEpochMillis; @@ -217,12 +216,12 @@ import java.util.Objects; /** * Gets a token from the cache if possible. */ - public String get(Account account, String tokenType, String packageName, byte[] sigDigest) { + public Value get(Account account, String tokenType, String packageName, byte[] sigDigest) { Key k = new Key(account, tokenType, packageName, sigDigest); Value v = mCachedTokens.get(k); long currentTime = System.currentTimeMillis(); if (v != null && currentTime < v.expiryEpochMillis) { - return v.token; + return v; } else if (v != null) { remove(account.type, v.token); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 07c281864c9c..0da14bc2a96a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -310,7 +310,6 @@ import android.sysprop.InitProperties; import android.sysprop.VoldProperties; import android.telephony.TelephonyManager; import android.text.TextUtils; -import android.text.format.DateUtils; import android.text.style.SuggestionSpan; import android.util.ArrayMap; import android.util.ArraySet; @@ -8685,7 +8684,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private final ArrayMap<String, long[]> mErrorClusterRecords = new ArrayMap<>(); + private final DropboxRateLimiter mDropboxRateLimiter = new DropboxRateLimiter(); /** * Write a description of an error (crash, WTF, ANR) to the drop box. @@ -8720,22 +8719,8 @@ public class ActivityManagerService extends IActivityManager.Stub final String dropboxTag = processClass(process) + "_" + eventType; if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return; - // Rate-limit how often we're willing to do the heavy lifting below to - // collect and record logs; currently 5 logs per 10 second period per eventType. - final long now = SystemClock.elapsedRealtime(); - synchronized (mErrorClusterRecords) { - long[] errRecord = mErrorClusterRecords.get(eventType); - if (errRecord == null) { - errRecord = new long[2]; // [0]: startTime, [1]: count - mErrorClusterRecords.put(eventType, errRecord); - } - if (now - errRecord[0] > 10 * DateUtils.SECOND_IN_MILLIS) { - errRecord[0] = now; - errRecord[1] = 1L; - } else { - if (errRecord[1]++ >= 5) return; - } - } + // Check if we should rate limit and abort early if needed. + if (mDropboxRateLimiter.shouldRateLimit(eventType, processName)) return; final StringBuilder sb = new StringBuilder(1024); appendDropBoxProcessHeaders(process, processName, sb); diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java index 032b129c4256..64ff532b026a 100644 --- a/services/core/java/com/android/server/am/AppBatteryTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryTracker.java @@ -49,6 +49,7 @@ import android.content.Context; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; +import android.os.AppBackgroundRestrictionsInfo; import android.os.AppBatteryStatsProto; import android.os.BatteryConsumer; import android.os.BatteryConsumer.Dimensions; @@ -74,6 +75,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.am.AppBatteryTracker.AppBatteryPolicy; import com.android.server.am.AppRestrictionController.TrackerType; import com.android.server.am.AppRestrictionController.UidBatteryUsageProvider; @@ -175,6 +177,12 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> @GuardedBy("mLock") private long mLastUidBatteryUsageStartTs; + /** + * elapseRealTime of last time the AppBatteryTracker is reported to statsd. + */ + @GuardedBy("mLock") + private long mLastReportTime = 0; + // For debug only. private final SparseArray<ImmutableBatteryUsage> mDebugUidPercentages = new SparseArray<>(); @@ -228,9 +236,92 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> mBgHandler.postDelayed(mBgBatteryUsageStatsPolling, delay); } } + logAppBatteryTrackerIfNeeded(); } } + /** + * Log per-uid BatteryTrackerInfo to statsd every 24 hours (as the window specified in + * {@link AppBatteryPolicy#mBgCurrentDrainWindowMs}) + */ + private void logAppBatteryTrackerIfNeeded() { + final long now = SystemClock.elapsedRealtime(); + synchronized (mLock) { + final AppBatteryPolicy bgPolicy = mInjector.getPolicy(); + if (now - mLastReportTime < bgPolicy.mBgCurrentDrainWindowMs) { + return; + } else { + mLastReportTime = now; + } + } + updateBatteryUsageStatsIfNecessary(mInjector.currentTimeMillis(), true); + synchronized (mLock) { + for (int i = 0, size = mUidBatteryUsageInWindow.size(); i < size; i++) { + final int uid = mUidBatteryUsageInWindow.keyAt(i); + if (!UserHandle.isCore(uid) && !UserHandle.isApp(uid)) { + continue; + } + if (BATTERY_USAGE_NONE.equals(mUidBatteryUsageInWindow.valueAt(i))) { + continue; + } + FrameworkStatsLog.write(FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO, + uid, + FrameworkStatsLog + .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_UNKNOWN, + FrameworkStatsLog + .APP_BACKGROUND_RESTRICTIONS_INFO__THRESHOLD__THRESHOLD_UNKNOWN, + FrameworkStatsLog + .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__UNKNOWN_TRACKER, + null /*byte[] fgs_tracker_info*/, + getBatteryTrackerInfoProtoLocked(uid) /*byte[] battery_tracker_info*/, + null /*byte[] broadcast_events_tracker_info*/, + null /*byte[] bind_service_events_tracker_info*/, + FrameworkStatsLog + .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_UNKNOWN, + FrameworkStatsLog + .APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__UNKNOWN, + FrameworkStatsLog + .APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_UNKNOWN, + isLowRamDeviceStatic()); + } + } + } + + /** + * Get the BatteryTrackerInfo proto of a UID. + * @param uid + * @return byte array of the proto. + */ + @NonNull byte[] getBatteryTrackerInfoProtoLocked(int uid) { + final ImmutableBatteryUsage temp = mUidBatteryUsageInWindow.get(uid); + if (temp == null) { + return new byte[0]; + } + final BatteryUsage bgUsage = temp.calcPercentage(uid, mInjector.getPolicy()); + final double allUsage = bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_UNSPECIFIED] + + bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND] + + bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_BACKGROUND] + + bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND_SERVICE] + + bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_CACHED]; + final double usageBackground = + bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_BACKGROUND]; + final double usageFgs = + bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]; + Slog.d(TAG, "getBatteryTrackerInfoProtoLocked uid:" + uid + + " allUsage:" + String.format("%4.2f%%", allUsage) + + " usageBackground:" + String.format("%4.2f%%", usageBackground) + + " usageFgs:" + String.format("%4.2f%%", usageFgs)); + final ProtoOutputStream proto = new ProtoOutputStream(); + proto.write(AppBackgroundRestrictionsInfo.BatteryTrackerInfo.BATTERY_24H, + allUsage * 10000); + proto.write(AppBackgroundRestrictionsInfo.BatteryTrackerInfo.BATTERY_USAGE_BACKGROUND, + usageBackground * 10000); + proto.write(AppBackgroundRestrictionsInfo.BatteryTrackerInfo.BATTERY_USAGE_FGS, + usageFgs * 10000); + proto.flush(); + return proto.getBytes(); + } + @Override void onUserStarted(final @UserIdInt int userId) { synchronized (mLock) { diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java index 01d8109a82a0..872531a47bc9 100644 --- a/services/core/java/com/android/server/am/BroadcastDispatcher.java +++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java @@ -580,6 +580,11 @@ public class BroadcastDispatcher { // ---------------------------------- // BroadcastQueue operation support void enqueueOrderedBroadcastLocked(BroadcastRecord r) { + if (r.receivers == null || r.receivers.isEmpty()) { + mOrderedBroadcasts.add(r); + return; + } + if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(r.intent.getAction())) { // Create one BroadcastRecord for each UID that can be deferred. final SparseArray<BroadcastRecord> deferred = diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 2ee32b6abe96..5343af25fd39 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -414,6 +414,10 @@ final class BroadcastRecord extends Binder { return ret; } + if (receivers == null) { + return ret; + } + final String action = intent.getAction(); if (!Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action) && !Intent.ACTION_BOOT_COMPLETED.equals(action)) { diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java new file mode 100644 index 000000000000..c51702359a6b --- /dev/null +++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.os.SystemClock; +import android.text.format.DateUtils; +import android.util.ArrayMap; + +import com.android.internal.annotations.GuardedBy; + +/** Rate limiter for adding errors into dropbox. */ +public class DropboxRateLimiter { + private static final long RATE_LIMIT_BUFFER_EXPIRY = 15 * DateUtils.SECOND_IN_MILLIS; + private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.SECOND_IN_MILLIS; + private static final int RATE_LIMIT_ALLOWED_ENTRIES = 5; + + @GuardedBy("mErrorClusterRecords") + private final ArrayMap<String, ErrorRecord> mErrorClusterRecords = new ArrayMap<>(); + private final Clock mClock; + + private long mLastMapCleanUp = 0L; + + public DropboxRateLimiter() { + this(new DefaultClock()); + } + + public DropboxRateLimiter(Clock clock) { + mClock = clock; + } + + /** The interface clock to use for tracking the time elapsed. */ + public interface Clock { + /** How long in millis has passed since the device came online. */ + long uptimeMillis(); + } + + /** Determines whether dropbox entries of a specific tag and process should be rate limited. */ + public boolean shouldRateLimit(String eventType, String processName) { + // Rate-limit how often we're willing to do the heavy lifting to collect and record logs. + final long now = mClock.uptimeMillis(); + synchronized (mErrorClusterRecords) { + // Remove expired records if enough time has passed since the last cleanup. + maybeRemoveExpiredRecords(now); + + ErrorRecord errRecord = mErrorClusterRecords.get(errorKey(eventType, processName)); + if (errRecord == null) { + errRecord = new ErrorRecord(now, 1); + mErrorClusterRecords.put(errorKey(eventType, processName), errRecord); + } else if (now - errRecord.getStartTime() > RATE_LIMIT_BUFFER_DURATION) { + errRecord.setStartTime(now); + errRecord.setCount(1); + } else { + errRecord.incrementCount(); + if (errRecord.getCount() > RATE_LIMIT_ALLOWED_ENTRIES) return true; + } + } + return false; + } + + private void maybeRemoveExpiredRecords(long now) { + if (now - mLastMapCleanUp <= RATE_LIMIT_BUFFER_EXPIRY) return; + + for (int i = mErrorClusterRecords.size() - 1; i >= 0; i--) { + if (now - mErrorClusterRecords.valueAt(i).getStartTime() > RATE_LIMIT_BUFFER_EXPIRY) { + mErrorClusterRecords.removeAt(i); + } + } + + mLastMapCleanUp = now; + } + + String errorKey(String eventType, String processName) { + return eventType + processName; + } + + private class ErrorRecord { + long mStartTime; + int mCount; + + ErrorRecord(long startTime, int count) { + mStartTime = startTime; + mCount = count; + } + + public void setStartTime(long startTime) { + mStartTime = startTime; + } + + public void setCount(int count) { + mCount = count; + } + + public void incrementCount() { + mCount++; + } + + public long getStartTime() { + return mStartTime; + } + + public int getCount() { + return mCount; + } + } + + private static class DefaultClock implements Clock { + public long uptimeMillis() { + return SystemClock.uptimeMillis(); + } + } +} diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index d22a562651dc..04fcda7835fa 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -327,15 +327,29 @@ public class SpatializerHelper { setDispatchAvailableState(false); } - if (able && enabledAvailable.first) { + boolean enabled = able && enabledAvailable.first; + if (enabled) { loglogi("Enabling Spatial Audio since enabled for media device:" + ROUTING_DEVICES[0]); } else { loglogi("Disabling Spatial Audio since disabled for media device:" + ROUTING_DEVICES[0]); } - setDispatchFeatureEnabledState(able && enabledAvailable.first, - "onRoutingUpdated"); + if (mSpat != null) { + byte level = enabled ? (byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL + : (byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; + loglogi("Setting spatialization level to: " + level); + try { + mSpat.setLevel(level); + } catch (RemoteException e) { + Log.e(TAG, "Can't set spatializer level", e); + mState = STATE_NOT_SUPPORTED; + mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; + enabled = false; + } + } + + setDispatchFeatureEnabledState(enabled, "onRoutingUpdated"); if (mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED && mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_DISABLED) { @@ -635,7 +649,6 @@ public class SpatializerHelper { init(true); } setSpatializerEnabledInt(true); - onRoutingUpdated(); } else { setSpatializerEnabledInt(false); } @@ -657,6 +670,7 @@ public class SpatializerHelper { case STATE_DISABLED_AVAILABLE: if (enabled) { createSpat(); + onRoutingUpdated(); break; } else { // already in disabled state @@ -823,14 +837,13 @@ public class SpatializerHelper { mSpatHeadTrackingCallback = new SpatializerHeadTrackingCallback(); mSpat = AudioSystem.getSpatializer(mSpatCallback); try { - mSpat.setLevel((byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL); mIsHeadTrackingSupported = mSpat.isHeadTrackingSupported(); //TODO: register heatracking callback only when sensors are registered if (mIsHeadTrackingSupported) { mSpat.registerHeadTrackingCallback(mSpatHeadTrackingCallback); } } catch (RemoteException e) { - Log.e(TAG, "Can't set spatializer level", e); + Log.e(TAG, "Can't configure head tracking", e); mState = STATE_NOT_SUPPORTED; mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 35217dba7c28..4c9b28b1bd18 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -2313,6 +2313,13 @@ public class Vpn { "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270", (profile.mppe ? "+mppe" : "nomppe"), }; + if (profile.mppe) { + // Disallow PAP authentication when MPPE is requested, as MPPE cannot work + // with PAP anyway, and users may not expect PAP (plain text) to be used when + // MPPE was requested. + mtpd = Arrays.copyOf(mtpd, mtpd.length + 1); + mtpd[mtpd.length - 1] = "-pap"; + } break; case VpnProfile.TYPE_L2TP_IPSEC_PSK: case VpnProfile.TYPE_L2TP_IPSEC_RSA: diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 5a105f551ab2..5fcdc8a08b5d 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -70,6 +70,8 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayGroupListener; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; +import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation; +import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; @@ -708,9 +710,6 @@ public final class DisplayManagerService extends SystemService { synchronized (mSyncRoot) { final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId); if (display != null) { - // Do not let constrain be overwritten by override from WindowManager. - info.shouldConstrainMetricsForLauncher = - display.getDisplayInfoLocked().shouldConstrainMetricsForLauncher; if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) { handleLogicalDisplayChangedLocked(display); } @@ -2212,21 +2211,6 @@ public final class DisplayManagerService extends SystemService { } } - void setShouldConstrainMetricsForLauncher(boolean constrain) { - // Apply constrain for every display. - synchronized (mSyncRoot) { - int[] displayIds = mLogicalDisplayMapper.getDisplayIdsLocked(Process.myUid()); - for (int i : displayIds) { - final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(i); - if (display == null) { - return; - } - display.getDisplayInfoLocked().shouldConstrainMetricsForLauncher = constrain; - setDisplayInfoOverrideFromWindowManagerInternal(i, display.getDisplayInfoLocked()); - } - } - } - void setDockedAndIdleEnabled(boolean enabled, int displayId) { synchronized (mSyncRoot) { final DisplayPowerController displayPowerController = mDisplayPowerControllers.get( diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index bfdac5781b75..7dce2380407e 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -60,8 +60,6 @@ class DisplayManagerShellCommand extends ShellCommand { return setDisplayModeDirectorLoggingEnabled(false); case "dwb-set-cct": return setAmbientColorTemperatureOverride(); - case "constrain-launcher-metrics": - return setConstrainLauncherMetrics(); case "set-user-preferred-display-mode": return setUserPreferredDisplayMode(); case "clear-user-preferred-display-mode": @@ -112,9 +110,6 @@ class DisplayManagerShellCommand extends ShellCommand { pw.println(" Disable display mode director logging."); pw.println(" dwb-set-cct CCT"); pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable)."); - pw.println(" constrain-launcher-metrics [true|false]"); - pw.println(" Sets if Display#getRealSize and getRealMetrics should be constrained for "); - pw.println(" Launcher."); pw.println(" set-user-preferred-display-mode WIDTH HEIGHT REFRESH-RATE " + "DISPLAY_ID (optional)"); pw.println(" Sets the user preferred display mode which has fields WIDTH, HEIGHT and " @@ -205,17 +200,6 @@ class DisplayManagerShellCommand extends ShellCommand { return 0; } - private int setConstrainLauncherMetrics() { - String constrainText = getNextArg(); - if (constrainText == null) { - getErrPrintWriter().println("Error: no value specified"); - return 1; - } - boolean constrain = Boolean.parseBoolean(constrainText); - mService.setShouldConstrainMetricsForLauncher(constrain); - return 0; - } - private int setUserPreferredDisplayMode() { final String widthText = getNextArg(); if (widthText == null) { diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index b7ad4ed4f98d..a640497d7e10 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -233,8 +233,6 @@ final class LogicalDisplay { info.displayCutout = mOverrideDisplayInfo.displayCutout; info.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi; info.roundedCorners = mOverrideDisplayInfo.roundedCorners; - info.shouldConstrainMetricsForLauncher = - mOverrideDisplayInfo.shouldConstrainMetricsForLauncher; } mInfo.set(info); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index ccb27eef2075..576a5ff4305e 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -396,7 +396,9 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { try { String iso3Language = new String(message.getParams(), 0, 3, "US-ASCII"); Locale currentLocale = mService.getContext().getResources().getConfiguration().locale; - if (currentLocale.getISO3Language().equals(iso3Language)) { + String curIso3Language = mService.localeToMenuLanguage(currentLocale); + HdmiLogger.debug("handleSetMenuLanguage " + iso3Language + " cur:" + curIso3Language); + if (curIso3Language.equals(iso3Language)) { // Do not switch language if the new language is the same as the current one. // This helps avoid accidental country variant switching from en_US to en_AU // due to the limitation of CEC. See the warning below. @@ -408,7 +410,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { final List<LocaleInfo> localeInfos = LocalePicker.getAllAssetLocales( mService.getContext(), false); for (LocaleInfo localeInfo : localeInfos) { - if (localeInfo.getLocale().getISO3Language().equals(iso3Language)) { + if (mService.localeToMenuLanguage(localeInfo.getLocale()).equals(iso3Language)) { // WARNING: CEC adopts ISO/FDIS-2 for language code, while Android requires // additional country variant to pinpoint the locale. This keeps the right // locale from being chosen. 'eng' in the CEC command, for instance, diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 385aa698543d..603d0128cc44 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -74,7 +74,6 @@ import android.os.InputEventInjectionSync; import android.os.LocaleList; import android.os.Looper; import android.os.Message; -import android.os.MessageQueue; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -112,6 +111,7 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; @@ -147,9 +147,7 @@ import java.util.Map; import java.util.Objects; import java.util.OptionalInt; -/* - * Wraps the C++ InputManager and provides its callbacks. - */ +/** The system implementation of {@link IInputManager} that manages input devices. */ public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor { static final String TAG = "InputManager"; @@ -183,8 +181,7 @@ public class InputManagerService extends IInputManager.Stub /** TODO(b/169067926): Remove this. */ private static final boolean UNTRUSTED_TOUCHES_TOAST = false; - // Pointer to native input manager service object. - private final long mPtr; + private final NativeInputManagerService mNative; private final Context mContext; private final InputManagerHandler mHandler; @@ -299,91 +296,6 @@ public class InputManagerService extends IInputManager.Stub @GuardedBy("mInputMonitors") final Map<IBinder, GestureMonitorSpyWindow> mInputMonitors = new HashMap<>(); - private static native long nativeInit(InputManagerService service, - Context context, MessageQueue messageQueue); - private static native void nativeStart(long ptr); - private static native void nativeSetDisplayViewports(long ptr, - DisplayViewport[] viewports); - - private static native int nativeGetScanCodeState(long ptr, - int deviceId, int sourceMask, int scanCode); - private static native int nativeGetKeyCodeState(long ptr, - int deviceId, int sourceMask, int keyCode); - private static native int nativeGetSwitchState(long ptr, - int deviceId, int sourceMask, int sw); - private static native boolean nativeHasKeys(long ptr, - int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists); - private static native int nativeGetKeyCodeForKeyLocation(long ptr, int deviceId, - int locationKeyCode); - private static native InputChannel nativeCreateInputChannel(long ptr, String name); - private static native InputChannel nativeCreateInputMonitor(long ptr, int displayId, - String name, int pid); - private static native void nativeRemoveInputChannel(long ptr, IBinder connectionToken); - private static native void nativePilferPointers(long ptr, IBinder token); - private static native void nativeSetInputFilterEnabled(long ptr, boolean enable); - private static native boolean nativeSetInTouchMode(long ptr, boolean inTouchMode, int pid, - int uid, boolean hasPermission); - private static native void nativeSetMaximumObscuringOpacityForTouch(long ptr, float opacity); - private static native void nativeSetBlockUntrustedTouchesMode(long ptr, int mode); - private static native int nativeInjectInputEvent(long ptr, InputEvent event, - boolean injectIntoUid, int uid, int syncMode, int timeoutMillis, int policyFlags); - private static native VerifiedInputEvent nativeVerifyInputEvent(long ptr, InputEvent event); - private static native void nativeToggleCapsLock(long ptr, int deviceId); - private static native void nativeDisplayRemoved(long ptr, int displayId); - private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen); - private static native void nativeSetSystemUiLightsOut(long ptr, boolean lightsOut); - private static native void nativeSetFocusedApplication(long ptr, - int displayId, InputApplicationHandle application); - private static native void nativeSetFocusedDisplay(long ptr, int displayId); - private static native boolean nativeTransferTouchFocus(long ptr, - IBinder fromChannelToken, IBinder toChannelToken, boolean isDragDrop); - private static native boolean nativeTransferTouch(long ptr, IBinder destChannelToken); - private static native void nativeSetPointerSpeed(long ptr, int speed); - private static native void nativeSetPointerAcceleration(long ptr, float acceleration); - private static native void nativeSetShowTouches(long ptr, boolean enabled); - private static native void nativeSetInteractive(long ptr, boolean interactive); - private static native void nativeReloadCalibration(long ptr); - private static native void nativeVibrate(long ptr, int deviceId, long[] pattern, - int[] amplitudes, int repeat, int token); - private static native void nativeVibrateCombined(long ptr, int deviceId, long[] pattern, - SparseArray<int[]> amplitudes, int repeat, int token); - private static native void nativeCancelVibrate(long ptr, int deviceId, int token); - private static native boolean nativeIsVibrating(long ptr, int deviceId); - private static native int[] nativeGetVibratorIds(long ptr, int deviceId); - private static native int nativeGetBatteryCapacity(long ptr, int deviceId); - private static native int nativeGetBatteryStatus(long ptr, int deviceId); - private static native List<Light> nativeGetLights(long ptr, int deviceId); - private static native int nativeGetLightPlayerId(long ptr, int deviceId, int lightId); - private static native int nativeGetLightColor(long ptr, int deviceId, int lightId); - private static native void nativeSetLightPlayerId(long ptr, int deviceId, int lightId, - int playerId); - private static native void nativeSetLightColor(long ptr, int deviceId, int lightId, int color); - private static native void nativeReloadKeyboardLayouts(long ptr); - private static native void nativeReloadDeviceAliases(long ptr); - private static native String nativeDump(long ptr); - private static native void nativeMonitor(long ptr); - private static native boolean nativeIsInputDeviceEnabled(long ptr, int deviceId); - private static native void nativeEnableInputDevice(long ptr, int deviceId); - private static native void nativeDisableInputDevice(long ptr, int deviceId); - private static native void nativeSetPointerIconType(long ptr, int iconId); - private static native void nativeReloadPointerIcons(long ptr); - private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon); - private static native void nativeRequestPointerCapture(long ptr, IBinder windowToken, - boolean enabled); - private static native boolean nativeCanDispatchToDisplay(long ptr, int deviceId, int displayId); - private static native void nativeNotifyPortAssociationsChanged(long ptr); - private static native void nativeChangeUniqueIdAssociation(long ptr); - private static native void nativeNotifyPointerDisplayIdChanged(long ptr); - private static native void nativeSetDisplayEligibilityForPointerCapture(long ptr, int displayId, - boolean enabled); - private static native void nativeSetMotionClassifierEnabled(long ptr, boolean enabled); - private static native InputSensorInfo[] nativeGetSensorList(long ptr, int deviceId); - private static native boolean nativeFlushSensor(long ptr, int deviceId, int sensorType); - private static native boolean nativeEnableSensor(long ptr, int deviceId, int sensorType, - int samplingPeriodUs, int maxBatchReportLatencyUs); - private static native void nativeDisableSensor(long ptr, int deviceId, int sensorType); - private static native void nativeCancelCurrentTouch(long ptr); - // Maximum number of milliseconds to wait for input event injection. private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000; @@ -450,18 +362,47 @@ public class InputManagerService extends IInputManager.Stub /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */ final boolean mUseDevInputEventForAudioJack; + /** Point of injection for test dependencies. */ + @VisibleForTesting + static class Injector { + private final Context mContext; + private final Looper mLooper; + + Injector(Context context, Looper looper) { + mContext = context; + mLooper = looper; + } + + Context getContext() { + return mContext; + } + + Looper getLooper() { + return mLooper; + } + + NativeInputManagerService getNativeService(InputManagerService service) { + return new NativeInputManagerService.NativeImpl(service, mContext, mLooper.getQueue()); + } + } + public InputManagerService(Context context) { - this.mContext = context; - this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); + this(new Injector(context, DisplayThread.get().getLooper())); + } + + @VisibleForTesting + InputManagerService(Injector injector) { + mContext = injector.getContext(); + mHandler = new InputManagerHandler(injector.getLooper()); + mNative = injector.getNativeService(this); mStaticAssociations = loadStaticInputPortAssociations(); mUseDevInputEventForAudioJack = - context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); + mContext.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" + mUseDevInputEventForAudioJack); - mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); - String doubleTouchGestureEnablePath = context.getResources().getString( + String doubleTouchGestureEnablePath = mContext.getResources().getString( R.string.config_doubleTouchGestureEnableFile); mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null : new File(doubleTouchGestureEnablePath); @@ -504,9 +445,9 @@ public class InputManagerService extends IInputManager.Stub public void start() { Slog.i(TAG, "Starting input manager"); - nativeStart(mPtr); + mNative.start(); - // Add ourself to the Watchdog monitors. + // Add ourselves to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); registerPointerSpeedSettingObserver(); @@ -599,14 +540,14 @@ public class InputManagerService extends IInputManager.Stub if (DEBUG) { Slog.d(TAG, "Reloading keyboard layouts."); } - nativeReloadKeyboardLayouts(mPtr); + mNative.reloadKeyboardLayouts(); } private void reloadDeviceAliases() { if (DEBUG) { Slog.d(TAG, "Reloading device names."); } - nativeReloadDeviceAliases(mPtr); + mNative.reloadDeviceAliases(); } private void setDisplayViewportsInternal(List<DisplayViewport> viewports) { @@ -615,7 +556,7 @@ public class InputManagerService extends IInputManager.Stub for (int i = viewports.size() - 1; i >= 0; --i) { vArray[i] = viewports.get(i); } - nativeSetDisplayViewports(mPtr, vArray); + mNative.setDisplayViewports(vArray); if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) { final AdditionalDisplayInputProperties properties = @@ -642,7 +583,7 @@ public class InputManagerService extends IInputManager.Stub * @return The key state. */ public int getKeyCodeState(int deviceId, int sourceMask, int keyCode) { - return nativeGetKeyCodeState(mPtr, deviceId, sourceMask, keyCode); + return mNative.getKeyCodeState(deviceId, sourceMask, keyCode); } /** @@ -655,7 +596,7 @@ public class InputManagerService extends IInputManager.Stub * @return The key state. */ public int getScanCodeState(int deviceId, int sourceMask, int scanCode) { - return nativeGetScanCodeState(mPtr, deviceId, sourceMask, scanCode); + return mNative.getScanCodeState(deviceId, sourceMask, scanCode); } /** @@ -668,7 +609,7 @@ public class InputManagerService extends IInputManager.Stub * @return The switch state. */ public int getSwitchState(int deviceId, int sourceMask, int switchCode) { - return nativeGetSwitchState(mPtr, deviceId, sourceMask, switchCode); + return mNative.getSwitchState(deviceId, sourceMask, switchCode); } /** @@ -691,7 +632,7 @@ public class InputManagerService extends IInputManager.Stub throw new IllegalArgumentException("keyExists must be at least as large as keyCodes"); } - return nativeHasKeys(mPtr, deviceId, sourceMask, keyCodes, keyExists); + return mNative.hasKeys(deviceId, sourceMask, keyCodes, keyExists); } /** @@ -707,7 +648,7 @@ public class InputManagerService extends IInputManager.Stub if (locationKeyCode <= KEYCODE_UNKNOWN || locationKeyCode > KeyEvent.getMaxKeyCode()) { return KEYCODE_UNKNOWN; } - return nativeGetKeyCodeForKeyLocation(mPtr, deviceId, locationKeyCode); + return mNative.getKeyCodeForKeyLocation(deviceId, locationKeyCode); } /** @@ -720,7 +661,7 @@ public class InputManagerService extends IInputManager.Stub public boolean transferTouch(IBinder destChannelToken) { // TODO(b/162194035): Replace this with a SPY window Objects.requireNonNull(destChannelToken, "destChannelToken must not be null"); - return nativeTransferTouch(mPtr, destChannelToken); + return mNative.transferTouch(destChannelToken); } /** @@ -736,7 +677,7 @@ public class InputManagerService extends IInputManager.Stub throw new IllegalArgumentException("displayId must >= 0."); } - return nativeCreateInputMonitor(mPtr, displayId, inputChannelName, Binder.getCallingPid()); + return mNative.createInputMonitor(displayId, inputChannelName, Binder.getCallingPid()); } @NonNull @@ -818,7 +759,7 @@ public class InputManagerService extends IInputManager.Stub * @param name The name of this input channel */ public InputChannel createInputChannel(String name) { - return nativeCreateInputChannel(mPtr, name); + return mNative.createInputChannel(name); } /** @@ -827,7 +768,7 @@ public class InputManagerService extends IInputManager.Stub */ public void removeInputChannel(IBinder connectionToken) { Objects.requireNonNull(connectionToken, "connectionToken must not be null"); - nativeRemoveInputChannel(mPtr, connectionToken); + mNative.removeInputChannel(connectionToken); } /** @@ -869,7 +810,7 @@ public class InputManagerService extends IInputManager.Stub } } - nativeSetInputFilterEnabled(mPtr, filter != null); + mNative.setInputFilterEnabled(filter != null); } } @@ -893,7 +834,7 @@ public class InputManagerService extends IInputManager.Stub * @return {@code true} if the touch mode was successfully changed, {@code false} otherwise */ public boolean setInTouchMode(boolean inTouchMode, int pid, int uid, boolean hasPermission) { - return nativeSetInTouchMode(mPtr, inTouchMode, pid, uid, hasPermission); + return mNative.setInTouchMode(inTouchMode, pid, uid, hasPermission); } @Override // Binder call @@ -918,7 +859,7 @@ public class InputManagerService extends IInputManager.Stub final boolean injectIntoUid = targetUid != Process.INVALID_UID; final int result; try { - result = nativeInjectInputEvent(mPtr, event, injectIntoUid, + result = mNative.injectInputEvent(event, injectIntoUid, targetUid, mode, INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT); } finally { @@ -962,7 +903,7 @@ public class InputManagerService extends IInputManager.Stub @Override // Binder call public VerifiedInputEvent verifyInputEvent(InputEvent event) { Objects.requireNonNull(event, "event must not be null"); - return nativeVerifyInputEvent(mPtr, event); + return mNative.verifyInputEvent(event); } /** @@ -985,7 +926,7 @@ public class InputManagerService extends IInputManager.Stub // Binder call @Override public boolean isInputDeviceEnabled(int deviceId) { - return nativeIsInputDeviceEnabled(mPtr, deviceId); + return mNative.isInputDeviceEnabled(deviceId); } // Binder call @@ -995,7 +936,7 @@ public class InputManagerService extends IInputManager.Stub "enableInputDevice()")) { throw new SecurityException("Requires DISABLE_INPUT_DEVICE permission"); } - nativeEnableInputDevice(mPtr, deviceId); + mNative.enableInputDevice(deviceId); } // Binder call @@ -1005,7 +946,7 @@ public class InputManagerService extends IInputManager.Stub "disableInputDevice()")) { throw new SecurityException("Requires DISABLE_INPUT_DEVICE permission"); } - nativeDisableInputDevice(mPtr, deviceId); + mNative.disableInputDevice(deviceId); } /** @@ -1251,7 +1192,7 @@ public class InputManagerService extends IInputManager.Stub try { if (mDataStore.setTouchCalibration(inputDeviceDescriptor, surfaceRotation, calibration)) { - nativeReloadCalibration(mPtr); + mNative.reloadCalibration(); } } finally { mDataStore.saveIfNeeded(); @@ -1763,11 +1704,11 @@ public class InputManagerService extends IInputManager.Stub } public void setFocusedApplication(int displayId, InputApplicationHandle application) { - nativeSetFocusedApplication(mPtr, displayId, application); + mNative.setFocusedApplication(displayId, application); } public void setFocusedDisplay(int displayId) { - nativeSetFocusedDisplay(mPtr, displayId); + mNative.setFocusedDisplay(displayId); } /** Clean up input window handles of the given display. */ @@ -1777,22 +1718,22 @@ public class InputManagerService extends IInputManager.Stub mPointerIconDisplayContext = null; } - nativeDisplayRemoved(mPtr, displayId); + mNative.displayRemoved(displayId); } @Override public void requestPointerCapture(IBinder inputChannelToken, boolean enabled) { Objects.requireNonNull(inputChannelToken, "event must not be null"); - nativeRequestPointerCapture(mPtr, inputChannelToken, enabled); + mNative.requestPointerCapture(inputChannelToken, enabled); } public void setInputDispatchMode(boolean enabled, boolean frozen) { - nativeSetInputDispatchMode(mPtr, enabled, frozen); + mNative.setInputDispatchMode(enabled, frozen); } public void setSystemUiLightsOut(boolean lightsOut) { - nativeSetSystemUiLightsOut(mPtr, lightsOut); + mNative.setSystemUiLightsOut(lightsOut); } /** @@ -1811,7 +1752,7 @@ public class InputManagerService extends IInputManager.Stub */ public boolean transferTouchFocus(@NonNull InputChannel fromChannel, @NonNull InputChannel toChannel, boolean isDragDrop) { - return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken(), + return mNative.transferTouchFocus(fromChannel.getToken(), toChannel.getToken(), isDragDrop); } @@ -1832,7 +1773,7 @@ public class InputManagerService extends IInputManager.Stub @NonNull IBinder toChannelToken) { Objects.nonNull(fromChannelToken); Objects.nonNull(toChannelToken); - return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken, + return mNative.transferTouchFocus(fromChannelToken, toChannelToken, false /* isDragDrop */); } @@ -1858,7 +1799,7 @@ public class InputManagerService extends IInputManager.Stub private void setPointerSpeedUnchecked(int speed) { speed = Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED), InputManager.MAX_POINTER_SPEED); - nativeSetPointerSpeed(mPtr, speed); + mNative.setPointerSpeed(speed); } private void setPointerAcceleration(float acceleration, int displayId) { @@ -1881,7 +1822,7 @@ public class InputManagerService extends IInputManager.Stub @GuardedBy("mAdditionalDisplayInputPropertiesLock") private void updatePointerAccelerationLocked(float acceleration) { - nativeSetPointerAcceleration(mPtr, acceleration); + mNative.setPointerAcceleration(acceleration); } private void setPointerIconVisible(boolean visible, int displayId) { @@ -1906,12 +1847,12 @@ public class InputManagerService extends IInputManager.Stub private void updatePointerIconVisibleLocked(boolean visible) { if (visible) { if (mIconType == PointerIcon.TYPE_CUSTOM) { - nativeSetCustomPointerIcon(mPtr, mIcon); + mNative.setCustomPointerIcon(mIcon); } else { - nativeSetPointerIconType(mPtr, mIconType); + mNative.setPointerIconType(mIconType); } } else { - nativeSetPointerIconType(mPtr, PointerIcon.TYPE_NULL); + mNative.setPointerIconType(PointerIcon.TYPE_NULL); } } @@ -1938,7 +1879,7 @@ public class InputManagerService extends IInputManager.Stub private void updateShowTouchesFromSettings() { int setting = getShowTouchesSetting(0); - nativeSetShowTouches(mPtr, setting != 0); + mNative.setShowTouches(setting != 0); } private void registerShowTouchesSettingObserver() { @@ -1957,7 +1898,7 @@ public class InputManagerService extends IInputManager.Stub mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, 0, UserHandle.USER_CURRENT); PointerIcon.setUseLargeIcons(accessibilityConfig == 1); - nativeReloadPointerIcons(mPtr); + mNative.reloadPointerIcons(); } private void registerAccessibilityLargePointerSettingObserver() { @@ -1985,7 +1926,7 @@ public class InputManagerService extends IInputManager.Stub (enabled ? "Enabling" : "Disabling") + " motion classifier because " + reason + ": feature " + (featureEnabledFlag ? "enabled" : "disabled") + ", long press timeout = " + timeout); - nativeSetMotionClassifierEnabled(mPtr, enabled); + mNative.setMotionClassifierEnabled(enabled); } private void registerLongPressTimeoutObserver() { @@ -2013,7 +1954,7 @@ public class InputManagerService extends IInputManager.Stub private void updateBlockUntrustedTouchesModeFromSettings() { final int mode = InputManager.getInstance().getBlockUntrustedTouchesMode(mContext); - nativeSetBlockUntrustedTouchesMode(mPtr, mode); + mNative.setBlockUntrustedTouchesMode(mode); } private void registerMaximumObscuringOpacityForTouchSettingObserver() { @@ -2035,7 +1976,7 @@ public class InputManagerService extends IInputManager.Stub + ", it should be >= 0 and <= 1, rejecting update."); return; } - nativeSetMaximumObscuringOpacityForTouch(mPtr, opacity); + mNative.setMaximumObscuringOpacityForTouch(opacity); } private int getShowTouchesSetting(int defaultValue) { @@ -2061,7 +2002,7 @@ public class InputManagerService extends IInputManager.Stub } } // TODO(b/215597605): trigger MousePositionTracker update - nativeNotifyPointerDisplayIdChanged(mPtr); + mNative.notifyPointerDisplayIdChanged(); } private int getVirtualMousePointerDisplayId() { @@ -2071,7 +2012,7 @@ public class InputManagerService extends IInputManager.Stub } private void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible) { - nativeSetDisplayEligibilityForPointerCapture(mPtr, displayId, isEligible); + mNative.setDisplayEligibilityForPointerCapture(displayId, isEligible); } private static class VibrationInfo { @@ -2170,7 +2111,7 @@ public class InputManagerService extends IInputManager.Stub VibratorToken v = getVibratorToken(deviceId, token); synchronized (v) { v.mVibrating = true; - nativeVibrate(mPtr, deviceId, info.getPattern(), info.getAmplitudes(), + mNative.vibrate(deviceId, info.getPattern(), info.getAmplitudes(), info.getRepeatIndex(), v.mTokenValue); } } @@ -2178,13 +2119,13 @@ public class InputManagerService extends IInputManager.Stub // Binder call @Override public int[] getVibratorIds(int deviceId) { - return nativeGetVibratorIds(mPtr, deviceId); + return mNative.getVibratorIds(deviceId); } // Binder call @Override public boolean isVibrating(int deviceId) { - return nativeIsVibrating(mPtr, deviceId); + return mNative.isVibrating(deviceId); } // Binder call @@ -2202,7 +2143,7 @@ public class InputManagerService extends IInputManager.Stub if (effect instanceof CombinedVibration.Mono) { CombinedVibration.Mono mono = (CombinedVibration.Mono) effect; VibrationInfo info = new VibrationInfo(mono.getEffect()); - nativeVibrate(mPtr, deviceId, info.getPattern(), info.getAmplitudes(), + mNative.vibrate(deviceId, info.getPattern(), info.getAmplitudes(), info.getRepeatIndex(), v.mTokenValue); } else if (effect instanceof CombinedVibration.Stereo) { CombinedVibration.Stereo stereo = (CombinedVibration.Stereo) effect; @@ -2221,7 +2162,7 @@ public class InputManagerService extends IInputManager.Stub } amplitudes.put(effects.keyAt(i), info.getAmplitudes()); } - nativeVibrateCombined(mPtr, deviceId, pattern, amplitudes, repeat, + mNative.vibrateCombined(deviceId, pattern, amplitudes, repeat, v.mTokenValue); } } @@ -2252,7 +2193,7 @@ public class InputManagerService extends IInputManager.Stub private void cancelVibrateIfNeeded(VibratorToken v) { synchronized (v) { if (v.mVibrating) { - nativeCancelVibrate(mPtr, v.mDeviceId, v.mTokenValue); + mNative.cancelVibrate(v.mDeviceId, v.mTokenValue); v.mVibrating = false; } } @@ -2348,13 +2289,13 @@ public class InputManagerService extends IInputManager.Stub // Binder call @Override public int getBatteryStatus(int deviceId) { - return nativeGetBatteryStatus(mPtr, deviceId); + return mNative.getBatteryStatus(deviceId); } // Binder call @Override public int getBatteryCapacity(int deviceId) { - return nativeGetBatteryCapacity(mPtr, deviceId); + return mNative.getBatteryCapacity(deviceId); } // Binder call @@ -2370,10 +2311,10 @@ public class InputManagerService extends IInputManager.Stub final AdditionalDisplayInputProperties properties = mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId); if (properties == null || properties.pointerIconVisible) { - nativeSetPointerIconType(mPtr, mIconType); + mNative.setPointerIconType(mIconType); } } else { - nativeSetPointerIconType(mPtr, mIconType); + mNative.setPointerIconType(mIconType); } } } @@ -2391,10 +2332,10 @@ public class InputManagerService extends IInputManager.Stub if (properties == null || properties.pointerIconVisible) { // Only set the icon if it is not currently hidden; otherwise, it will be set // once it's no longer hidden. - nativeSetCustomPointerIcon(mPtr, mIcon); + mNative.setCustomPointerIcon(mIcon); } } else { - nativeSetCustomPointerIcon(mPtr, mIcon); + mNative.setCustomPointerIcon(mIcon); } } } @@ -2418,7 +2359,7 @@ public class InputManagerService extends IInputManager.Stub synchronized (mAssociationsLock) { mRuntimeAssociations.put(inputPort, displayPort); } - nativeNotifyPortAssociationsChanged(mPtr); + mNative.notifyPortAssociationsChanged(); } /** @@ -2439,7 +2380,7 @@ public class InputManagerService extends IInputManager.Stub synchronized (mAssociationsLock) { mRuntimeAssociations.remove(inputPort); } - nativeNotifyPortAssociationsChanged(mPtr); + mNative.notifyPortAssociationsChanged(); } @Override // Binder call @@ -2456,7 +2397,7 @@ public class InputManagerService extends IInputManager.Stub synchronized (mAssociationsLock) { mUniqueIdAssociations.put(inputPort, displayUniqueId); } - nativeChangeUniqueIdAssociation(mPtr); + mNative.changeUniqueIdAssociation(); } @Override // Binder call @@ -2472,12 +2413,12 @@ public class InputManagerService extends IInputManager.Stub synchronized (mAssociationsLock) { mUniqueIdAssociations.remove(inputPort); } - nativeChangeUniqueIdAssociation(mPtr); + mNative.changeUniqueIdAssociation(); } @Override // Binder call public InputSensorInfo[] getSensorList(int deviceId) { - return nativeGetSensorList(mPtr, deviceId); + return mNative.getSensorList(deviceId); } @Override // Binder call @@ -2538,7 +2479,7 @@ public class InputManagerService extends IInputManager.Stub int callingPid = Binder.getCallingPid(); SensorEventListenerRecord listener = mSensorEventListeners.get(callingPid); if (listener != null) { - return nativeFlushSensor(mPtr, deviceId, sensorType); + return mNative.flushSensor(deviceId, sensorType); } return false; } @@ -2548,7 +2489,7 @@ public class InputManagerService extends IInputManager.Stub public boolean enableSensor(int deviceId, int sensorType, int samplingPeriodUs, int maxBatchReportLatencyUs) { synchronized (mInputDevicesLock) { - return nativeEnableSensor(mPtr, deviceId, sensorType, samplingPeriodUs, + return mNative.enableSensor(deviceId, sensorType, samplingPeriodUs, maxBatchReportLatencyUs); } } @@ -2556,7 +2497,7 @@ public class InputManagerService extends IInputManager.Stub @Override // Binder call public void disableSensor(int deviceId, int sensorType) { synchronized (mInputDevicesLock) { - nativeDisableSensor(mPtr, deviceId, sensorType); + mNative.disableSensor(deviceId, sensorType); } } @@ -2595,7 +2536,7 @@ public class InputManagerService extends IInputManager.Stub */ @Override // Binder call public List<Light> getLights(int deviceId) { - return nativeGetLights(mPtr, deviceId); + return mNative.getLights(deviceId); } /** @@ -2608,11 +2549,11 @@ public class InputManagerService extends IInputManager.Stub + "lightState " + lightState); } if (light.getType() == Light.LIGHT_TYPE_PLAYER_ID) { - nativeSetLightPlayerId(mPtr, deviceId, light.getId(), lightState.getPlayerId()); + mNative.setLightPlayerId(deviceId, light.getId(), lightState.getPlayerId()); } else { // Set ARGB format color to input device light // Refer to https://developer.android.com/reference/kotlin/android/graphics/Color - nativeSetLightColor(mPtr, deviceId, light.getId(), lightState.getColor()); + mNative.setLightColor(deviceId, light.getId(), lightState.getColor()); } } @@ -2620,7 +2561,7 @@ public class InputManagerService extends IInputManager.Stub * Set multiple light states with multiple light ids for a specific input device. */ private void setLightStatesInternal(int deviceId, int[] lightIds, LightState[] lightStates) { - final List<Light> lights = nativeGetLights(mPtr, deviceId); + final List<Light> lights = mNative.getLights(deviceId); SparseArray<Light> lightArray = new SparseArray<>(); for (int i = 0; i < lights.size(); i++) { lightArray.put(lights.get(i).getId(), lights.get(i)); @@ -2656,8 +2597,8 @@ public class InputManagerService extends IInputManager.Stub @Override public @Nullable LightState getLightState(int deviceId, int lightId) { synchronized (mLightLock) { - int color = nativeGetLightColor(mPtr, deviceId, lightId); - int playerId = nativeGetLightPlayerId(mPtr, deviceId, lightId); + int color = mNative.getLightColor(deviceId, lightId); + int playerId = mNative.getLightPlayerId(deviceId, lightId); return new LightState(color, playerId); } @@ -2709,7 +2650,7 @@ public class InputManagerService extends IInputManager.Stub throw new SecurityException("Requires MONITOR_INPUT permission"); } - nativeCancelCurrentTouch(mPtr); + mNative.cancelCurrentTouch(); } @Override @@ -2717,7 +2658,7 @@ public class InputManagerService extends IInputManager.Stub if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; pw.println("INPUT MANAGER (dumpsys input)\n"); - String dumpStr = nativeDump(mPtr); + String dumpStr = mNative.dump(); if (dumpStr != null) { pw.println(dumpStr); } @@ -2826,7 +2767,7 @@ public class InputManagerService extends IInputManager.Stub synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ } synchronized (mInputMonitors) { /* Test if blocked by input monitor lock. */ } synchronized (mAdditionalDisplayInputPropertiesLock) { /* Test if blocked by props lock */ } - nativeMonitor(mPtr); + mNative.monitor(); } // Native callback. @@ -3153,7 +3094,7 @@ public class InputManagerService extends IInputManager.Stub * @return True if the device could dispatch to the given display, false otherwise. */ public boolean canDispatchToDisplay(int deviceId, int displayId) { - return nativeCanDispatchToDisplay(mPtr, deviceId, displayId); + return mNative.canDispatchToDisplay(deviceId, displayId); } // Native callback. @@ -3504,7 +3445,7 @@ public class InputManagerService extends IInputManager.Stub synchronized (mInputFilterLock) { if (!mDisconnected) { - nativeInjectInputEvent(mPtr, event, false /* injectIntoUid */, -1 /* uid */, + mNative.injectInputEvent(event, false /* injectIntoUid */, -1 /* uid */, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0 /* timeout */, policyFlags | WindowManagerPolicy.FLAG_FILTERED); } @@ -3524,7 +3465,7 @@ public class InputManagerService extends IInputManager.Stub @Override public void pilferPointers() { - nativePilferPointers(mPtr, mInputChannelToken); + mNative.pilferPointers(mInputChannelToken); } @Override @@ -3702,12 +3643,12 @@ public class InputManagerService extends IInputManager.Stub @Override public void setInteractive(boolean interactive) { - nativeSetInteractive(mPtr, interactive); + mNative.setInteractive(interactive); } @Override public void toggleCapsLock(int deviceId) { - nativeToggleCapsLock(mPtr, deviceId); + mNative.toggleCapsLock(deviceId); } @Override @@ -3778,7 +3719,7 @@ public class InputManagerService extends IInputManager.Stub @Override public void pilferPointers(IBinder token) { - nativePilferPointers(mPtr, token); + mNative.pilferPointers(token); } } diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java new file mode 100644 index 000000000000..7178d20786e3 --- /dev/null +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.input; + +import android.content.Context; +import android.hardware.display.DisplayViewport; +import android.hardware.input.InputSensorInfo; +import android.hardware.lights.Light; +import android.os.IBinder; +import android.os.MessageQueue; +import android.util.SparseArray; +import android.view.InputApplicationHandle; +import android.view.InputChannel; +import android.view.InputEvent; +import android.view.PointerIcon; +import android.view.VerifiedInputEvent; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.List; + +/** + * An interface for the native methods of InputManagerService. We use a public interface so that + * this can be mocked for testing by Mockito. + */ +@VisibleForTesting +public interface NativeInputManagerService { + + void start(); + + void setDisplayViewports(DisplayViewport[] viewports); + + int getScanCodeState(int deviceId, int sourceMask, int scanCode); + + int getKeyCodeState(int deviceId, int sourceMask, int keyCode); + + int getSwitchState(int deviceId, int sourceMask, int sw); + + boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists); + + int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode); + + InputChannel createInputChannel(String name); + + InputChannel createInputMonitor(int displayId, String name, int pid); + + void removeInputChannel(IBinder connectionToken); + + void pilferPointers(IBinder token); + + void setInputFilterEnabled(boolean enable); + + boolean setInTouchMode(boolean inTouchMode, int pid, int uid, boolean hasPermission); + + void setMaximumObscuringOpacityForTouch(float opacity); + + void setBlockUntrustedTouchesMode(int mode); + + int injectInputEvent(InputEvent event, boolean injectIntoUid, int uid, int syncMode, + int timeoutMillis, int policyFlags); + + VerifiedInputEvent verifyInputEvent(InputEvent event); + + void toggleCapsLock(int deviceId); + + void displayRemoved(int displayId); + + void setInputDispatchMode(boolean enabled, boolean frozen); + + void setSystemUiLightsOut(boolean lightsOut); + + void setFocusedApplication(int displayId, InputApplicationHandle application); + + void setFocusedDisplay(int displayId); + + boolean transferTouchFocus(IBinder fromChannelToken, IBinder toChannelToken, + boolean isDragDrop); + + boolean transferTouch(IBinder destChannelToken); + + void setPointerSpeed(int speed); + + void setPointerAcceleration(float acceleration); + + void setShowTouches(boolean enabled); + + void setInteractive(boolean interactive); + + void reloadCalibration(); + + void vibrate(int deviceId, long[] pattern, int[] amplitudes, int repeat, int token); + + void vibrateCombined(int deviceId, long[] pattern, SparseArray<int[]> amplitudes, + int repeat, int token); + + void cancelVibrate(int deviceId, int token); + + boolean isVibrating(int deviceId); + + int[] getVibratorIds(int deviceId); + + int getBatteryCapacity(int deviceId); + + int getBatteryStatus(int deviceId); + + List<Light> getLights(int deviceId); + + int getLightPlayerId(int deviceId, int lightId); + + int getLightColor(int deviceId, int lightId); + + void setLightPlayerId(int deviceId, int lightId, int playerId); + + void setLightColor(int deviceId, int lightId, int color); + + void reloadKeyboardLayouts(); + + void reloadDeviceAliases(); + + String dump(); + + void monitor(); + + boolean isInputDeviceEnabled(int deviceId); + + void enableInputDevice(int deviceId); + + void disableInputDevice(int deviceId); + + void setPointerIconType(int iconId); + + void reloadPointerIcons(); + + void setCustomPointerIcon(PointerIcon icon); + + void requestPointerCapture(IBinder windowToken, boolean enabled); + + boolean canDispatchToDisplay(int deviceId, int displayId); + + void notifyPortAssociationsChanged(); + + void changeUniqueIdAssociation(); + + void notifyPointerDisplayIdChanged(); + + void setDisplayEligibilityForPointerCapture(int displayId, boolean enabled); + + void setMotionClassifierEnabled(boolean enabled); + + InputSensorInfo[] getSensorList(int deviceId); + + boolean flushSensor(int deviceId, int sensorType); + + boolean enableSensor(int deviceId, int sensorType, int samplingPeriodUs, + int maxBatchReportLatencyUs); + + void disableSensor(int deviceId, int sensorType); + + void cancelCurrentTouch(); + + /** The native implementation of InputManagerService methods. */ + class NativeImpl implements NativeInputManagerService { + /** Pointer to native input manager service object, used by native code. */ + @SuppressWarnings({"unused", "FieldCanBeLocal"}) + private final long mPtr; + + NativeImpl(InputManagerService service, Context context, MessageQueue messageQueue) { + mPtr = init(service, context, messageQueue); + } + + private native long init(InputManagerService service, Context context, + MessageQueue messageQueue); + + @Override + public native void start(); + + @Override + public native void setDisplayViewports(DisplayViewport[] viewports); + + @Override + public native int getScanCodeState(int deviceId, int sourceMask, int scanCode); + + @Override + public native int getKeyCodeState(int deviceId, int sourceMask, int keyCode); + + @Override + public native int getSwitchState(int deviceId, int sourceMask, int sw); + + @Override + public native boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, + boolean[] keyExists); + + @Override + public native int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode); + + @Override + public native InputChannel createInputChannel(String name); + + @Override + public native InputChannel createInputMonitor(int displayId, String name, int pid); + + @Override + public native void removeInputChannel(IBinder connectionToken); + + @Override + public native void pilferPointers(IBinder token); + + @Override + public native void setInputFilterEnabled(boolean enable); + + @Override + public native boolean setInTouchMode(boolean inTouchMode, int pid, int uid, + boolean hasPermission); + + @Override + public native void setMaximumObscuringOpacityForTouch(float opacity); + + @Override + public native void setBlockUntrustedTouchesMode(int mode); + + @Override + public native int injectInputEvent(InputEvent event, boolean injectIntoUid, int uid, + int syncMode, + int timeoutMillis, int policyFlags); + + @Override + public native VerifiedInputEvent verifyInputEvent(InputEvent event); + + @Override + public native void toggleCapsLock(int deviceId); + + @Override + public native void displayRemoved(int displayId); + + @Override + public native void setInputDispatchMode(boolean enabled, boolean frozen); + + @Override + public native void setSystemUiLightsOut(boolean lightsOut); + + @Override + public native void setFocusedApplication(int displayId, InputApplicationHandle application); + + @Override + public native void setFocusedDisplay(int displayId); + + @Override + public native boolean transferTouchFocus(IBinder fromChannelToken, IBinder toChannelToken, + boolean isDragDrop); + + @Override + public native boolean transferTouch(IBinder destChannelToken); + + @Override + public native void setPointerSpeed(int speed); + + @Override + public native void setPointerAcceleration(float acceleration); + + @Override + public native void setShowTouches(boolean enabled); + + @Override + public native void setInteractive(boolean interactive); + + @Override + public native void reloadCalibration(); + + @Override + public native void vibrate(int deviceId, long[] pattern, int[] amplitudes, int repeat, + int token); + + @Override + public native void vibrateCombined(int deviceId, long[] pattern, + SparseArray<int[]> amplitudes, + int repeat, int token); + + @Override + public native void cancelVibrate(int deviceId, int token); + + @Override + public native boolean isVibrating(int deviceId); + + @Override + public native int[] getVibratorIds(int deviceId); + + @Override + public native int getBatteryCapacity(int deviceId); + + @Override + public native int getBatteryStatus(int deviceId); + + @Override + public native List<Light> getLights(int deviceId); + + @Override + public native int getLightPlayerId(int deviceId, int lightId); + + @Override + public native int getLightColor(int deviceId, int lightId); + + @Override + public native void setLightPlayerId(int deviceId, int lightId, int playerId); + + @Override + public native void setLightColor(int deviceId, int lightId, int color); + + @Override + public native void reloadKeyboardLayouts(); + + @Override + public native void reloadDeviceAliases(); + + @Override + public native String dump(); + + @Override + public native void monitor(); + + @Override + public native boolean isInputDeviceEnabled(int deviceId); + + @Override + public native void enableInputDevice(int deviceId); + + @Override + public native void disableInputDevice(int deviceId); + + @Override + public native void setPointerIconType(int iconId); + + @Override + public native void reloadPointerIcons(); + + @Override + public native void setCustomPointerIcon(PointerIcon icon); + + @Override + public native void requestPointerCapture(IBinder windowToken, boolean enabled); + + @Override + public native boolean canDispatchToDisplay(int deviceId, int displayId); + + @Override + public native void notifyPortAssociationsChanged(); + + @Override + public native void changeUniqueIdAssociation(); + + @Override + public native void notifyPointerDisplayIdChanged(); + + @Override + public native void setDisplayEligibilityForPointerCapture(int displayId, boolean enabled); + + @Override + public native void setMotionClassifierEnabled(boolean enabled); + + @Override + public native InputSensorInfo[] getSensorList(int deviceId); + + @Override + public native boolean flushSensor(int deviceId, int sensorType); + + @Override + public native boolean enableSensor(int deviceId, int sensorType, int samplingPeriodUs, + int maxBatchReportLatencyUs); + + @Override + public native void disableSensor(int deviceId, int sensorType); + + @Override + public native void cancelCurrentTouch(); + } +} diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java index 9ef14cc38993..fc7be7ff8d1c 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerService.java +++ b/services/core/java/com/android/server/locales/LocaleManagerService.java @@ -186,7 +186,7 @@ public class LocaleManagerService extends SystemService { userId = mActivityManagerInternal.handleIncomingUser( Binder.getCallingPid(), Binder.getCallingUid(), userId, false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL, - "setApplicationLocales", appPackageName); + "setApplicationLocales", /* callerPackage= */ null); // This function handles two types of set operations: // 1.) A normal, non-privileged app setting its own locale. @@ -355,7 +355,7 @@ public class LocaleManagerService extends SystemService { userId = mActivityManagerInternal.handleIncomingUser( Binder.getCallingPid(), Binder.getCallingUid(), userId, false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL, - "getApplicationLocales", appPackageName); + "getApplicationLocales", /* callerPackage= */ null); // This function handles three types of query operations: // 1.) A normal, non-privileged app querying its own locale. diff --git a/services/core/java/com/android/server/locales/OWNERS b/services/core/java/com/android/server/locales/OWNERS index be284a766c50..4d93bff6f7d3 100644 --- a/services/core/java/com/android/server/locales/OWNERS +++ b/services/core/java/com/android/server/locales/OWNERS @@ -1,3 +1,4 @@ roosa@google.com pratyushmore@google.com goldmanj@google.com +ankitavyas@google.com diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index a73c8e0c914a..0e4bbbb7a412 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -18,12 +18,14 @@ package com.android.server.locksettings; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import android.app.ActivityManager; import android.app.admin.PasswordMetrics; import android.content.Context; import android.os.ShellCommand; import android.os.SystemProperties; +import android.os.UserHandle; import android.text.TextUtils; import android.util.Slog; @@ -48,6 +50,8 @@ class LockSettingsShellCommand extends ShellCommand { private static final String COMMAND_REMOVE_CACHE = "remove-cache"; private static final String COMMAND_SET_ROR_PROVIDER_PACKAGE = "set-resume-on-reboot-provider-package"; + private static final String COMMAND_REQUIRE_STRONG_AUTH = + "require-strong-auth"; private static final String COMMAND_HELP = "help"; private int mCurrentUserId; @@ -97,6 +101,9 @@ class LockSettingsShellCommand extends ShellCommand { case COMMAND_SET_ROR_PROVIDER_PACKAGE: runSetResumeOnRebootProviderPackage(); return 0; + case COMMAND_REQUIRE_STRONG_AUTH: + runRequireStrongAuth(); + return 0; case COMMAND_HELP: onHelp(); return 0; @@ -192,6 +199,10 @@ class LockSettingsShellCommand extends ShellCommand { pw.println(" Sets the package name for server based resume on reboot service " + "provider."); pw.println(""); + pw.println(" require-strong-auth [--user USER_ID] <reason>"); + pw.println(" Requires the strong authentication. The current supported reasons: " + + "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN."); + pw.println(""); } } @@ -288,6 +299,24 @@ class LockSettingsShellCommand extends ShellCommand { return true; } + private boolean runRequireStrongAuth() { + final String reason = mNew; + int strongAuthReason; + switch (reason) { + case "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN": + strongAuthReason = STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; + mCurrentUserId = UserHandle.USER_ALL; + break; + default: + getErrPrintWriter().println("Unsupported reason: " + reason); + return false; + } + mLockPatternUtils.requireStrongAuth(strongAuthReason, mCurrentUserId); + getOutPrintWriter().println("Require strong auth for USER_ID " + + mCurrentUserId + " because of " + mNew); + return true; + } + private boolean runClear() { LockscreenCredential none = LockscreenCredential.createNone(); if (!isNewCredentialSufficient(none)) { diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java index 8be90e0cc622..b45bfb1c2d92 100644 --- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java +++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java @@ -30,6 +30,7 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.os.logcat.ILogcatManagerService; import android.util.Slog; +import android.view.InflateException; import android.view.View; import android.widget.Button; import android.widget.TextView; @@ -56,33 +57,46 @@ public class LogAccessDialogActivity extends Activity implements private String mAlertTitle; private AlertDialog.Builder mAlertDialog; private AlertDialog mAlert; + private View mAlertView; private static final int DIALOG_TIME_OUT = Build.IS_DEBUGGABLE ? 60000 : 300000; private static final int MSG_DISMISS_DIALOG = 0; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mContext = this; - Intent intent = getIntent(); - mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); - mUid = intent.getIntExtra("com.android.server.logcat.uid", 0); - mGid = intent.getIntExtra("com.android.server.logcat.gid", 0); - mPid = intent.getIntExtra("com.android.server.logcat.pid", 0); - mFd = intent.getIntExtra("com.android.server.logcat.fd", 0); - mAlertTitle = getTitleString(mContext, mPackageName, mUid); + try { + mContext = this; + + // retrieve Intent extra information + Intent intent = getIntent(); + getIntentInfo(intent); - if (mAlertTitle != null) { + // retrieve the title string from passed intent extra + mAlertTitle = getTitleString(mContext, mPackageName, mUid); + // creaet View + mAlertView = createView(); + + // create AlertDialog mAlertDialog = new AlertDialog.Builder(this); - mAlertDialog.setView(createView()); + mAlertDialog.setView(mAlertView); + // show Alert mAlert = mAlertDialog.create(); mAlert.show(); + + // set Alert Timeout mHandler.sendEmptyMessageDelayed(MSG_DISMISS_DIALOG, DIALOG_TIME_OUT); + } catch (Exception e) { + try { + Slog.e(TAG, "onCreate failed, declining the logd access", e); + mLogcatManagerService.decline(mUid, mGid, mPid, mFd); + } catch (RemoteException ex) { + Slog.e(TAG, "Fails to call remote functions", ex); + } } } @@ -95,6 +109,19 @@ public class LogAccessDialogActivity extends Activity implements mAlert = null; } + private void getIntentInfo(Intent intent) throws Exception { + + if (intent == null) { + throw new NullPointerException("Intent is null"); + } + + mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); + mUid = intent.getIntExtra("com.android.server.logcat.uid", 0); + mGid = intent.getIntExtra("com.android.server.logcat.gid", 0); + mPid = intent.getIntExtra("com.android.server.logcat.pid", 0); + mFd = intent.getIntExtra("com.android.server.logcat.fd", 0); + } + private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { @@ -116,26 +143,41 @@ public class LogAccessDialogActivity extends Activity implements } }; - private String getTitleString(Context context, String callingPackage, int uid) { + private String getTitleString(Context context, String callingPackage, int uid) + throws Exception { + PackageManager pm = context.getPackageManager(); - try { - return context.getString( - com.android.internal.R.string.log_access_confirmation_title, - pm.getApplicationInfoAsUser(callingPackage, - PackageManager.MATCH_DIRECT_BOOT_AUTO, - UserHandle.getUserId(uid)).loadLabel(pm)); - } catch (NameNotFoundException e) { - Slog.e(TAG, "App name is unknown.", e); - return null; + if (pm == null) { + throw new NullPointerException("PackageManager is null"); } + + CharSequence appLabel = pm.getApplicationInfoAsUser(callingPackage, + PackageManager.MATCH_DIRECT_BOOT_AUTO, + UserHandle.getUserId(uid)).loadLabel(pm); + if (appLabel == null) { + throw new NameNotFoundException("Application Label is null"); + } + + return context.getString(com.android.internal.R.string.log_access_confirmation_title, + appLabel); } - private View createView() { + /** + * Returns the dialog view. + * If we cannot retrieve the package name, it returns null and we decline the full device log + * access + */ + private View createView() throws Exception { + final View view = getLayoutInflater().inflate( R.layout.log_access_user_consent_dialog_permission, null /*root*/); + if (view == null) { + throw new InflateException(); + } + ((TextView) view.findViewById(R.id.log_access_dialog_title)) - .setText(mAlertTitle); + .setText(mAlertTitle); Button button_allow = (Button) view.findViewById(R.id.log_access_dialog_allow_button); button_allow.setOnClickListener(this); @@ -144,6 +186,7 @@ public class LogAccessDialogActivity extends Activity implements button_deny.setOnClickListener(this); return view; + } @Override diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java index 015bf3c5e390..4c265ad5ff88 100644 --- a/services/core/java/com/android/server/logcat/LogcatManagerService.java +++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java @@ -102,16 +102,27 @@ public final class LogcatManagerService extends SystemService { } } - private void showDialog(int uid, int gid, int pid, int fd) { + /** + * Returns the package name. + * If we cannot retrieve the package name, it returns null and we decline the full device log + * access + */ + private String getPackageName(int uid, int gid, int pid, int fd) { + final ActivityManagerInternal activityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); + if (activityManagerInternal != null) { + String packageName = activityManagerInternal.getPackageNameByPid(pid); + if (packageName != null) { + return packageName; + } + } PackageManager pm = mContext.getPackageManager(); - String packageName = activityManagerInternal.getPackageNameByPid(pid); - if (packageName != null) { - Intent mIntent = createIntent(packageName, uid, gid, pid, fd); - mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM); - return; + if (pm == null) { + // Decline the logd access if PackageManager is null + Slog.e(TAG, "PackageManager is null, declining the logd access"); + return null; } String[] packageNames = pm.getPackagesForUid(uid); @@ -119,21 +130,19 @@ public final class LogcatManagerService extends SystemService { if (ArrayUtils.isEmpty(packageNames)) { // Decline the logd access if the app name is unknown Slog.e(TAG, "Unknown calling package name, declining the logd access"); - declineLogdAccess(uid, gid, pid, fd); - return; + return null; } String firstPackageName = packageNames[0]; - if (firstPackageName.isEmpty() || firstPackageName == null) { + if (firstPackageName == null || firstPackageName.isEmpty()) { // Decline the logd access if the package name from uid is unknown Slog.e(TAG, "Unknown calling package name, declining the logd access"); - declineLogdAccess(uid, gid, pid, fd); - return; + return null; } - final Intent mIntent = createIntent(firstPackageName, uid, gid, pid, fd); - mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM); + return firstPackageName; + } private void declineLogdAccess(int uid, int gid, int pid, int fd) { @@ -198,16 +207,23 @@ public final class LogcatManagerService extends SystemService { final int procState = LocalServices.getService(ActivityManagerInternal.class) .getUidProcessState(mUid); - // If the process is foreground, show a dialog for user consent + // If the process is foreground and we can retrieve the package name, show a dialog + // for user consent if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { - showDialog(mUid, mGid, mPid, mFd); - } else { - /** - * If the process is background, decline the logd access. - **/ - declineLogdAccess(mUid, mGid, mPid, mFd); - return; + String packageName = getPackageName(mUid, mGid, mPid, mFd); + if (packageName != null) { + final Intent mIntent = createIntent(packageName, mUid, mGid, mPid, mFd); + mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM); + return; + } } + + /** + * If the process is background or cannot retrieve the package name, + * decline the logd access. + **/ + declineLogdAccess(mUid, mGid, mPid, mFd); + return; } } } diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 728782ccee0b..d0651ed176cf 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -133,6 +133,10 @@ class BluetoothRouteProvider { mIntentFilter, null, null); } + public void stop() { + mContext.unregisterReceiver(mBroadcastReceiver); + } + /** * Transfers to a given bluetooth route. * The dedicated BT device with the route would be activated. diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index b3072667130b..e27cbeaab139 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -1150,6 +1150,8 @@ class MediaRouter2ServiceImpl { if (DEBUG) { Slog.d(TAG, userRecord + ": Disposed"); } + userRecord.mHandler.sendMessage( + obtainMessage(UserHandler::stop, userRecord.mHandler)); mUserRecords.remove(userRecord.mUserId); // Note: User already stopped (by switchUser) so no need to send stop message here. } @@ -1330,6 +1332,7 @@ class MediaRouter2ServiceImpl { private void start() { if (!mRunning) { mRunning = true; + mSystemProvider.start(); mWatcher.start(); } } @@ -1338,6 +1341,7 @@ class MediaRouter2ServiceImpl { if (mRunning) { mRunning = false; mWatcher.stop(); // also stops all providers + mSystemProvider.stop(); } } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 78781591a79e..d91bf8c00044 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -71,6 +71,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private final IAudioService mAudioService; private final Handler mHandler; private final Context mContext; + private final UserHandle mUser; private final BluetoothRouteProvider mBtRouteProvider; private static ComponentName sComponentName = new ComponentName( @@ -86,6 +87,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); int mDeviceVolume; + private final AudioManagerBroadcastReceiver mAudioReceiver = + new AudioManagerBroadcastReceiver(); + private final Object mRequestLock = new Object(); @GuardedBy("mRequestLock") private volatile SessionCreationRequest mPendingSessionCreationRequest; @@ -108,6 +112,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mIsSystemRouteProvider = true; mContext = context; + mUser = user; mHandler = new Handler(Looper.getMainLooper()); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); @@ -128,21 +133,33 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } }); updateSessionInfosIfNeeded(); + } + public void start() { IntentFilter intentFilter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION); intentFilter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION); - mContext.registerReceiverAsUser(new AudioManagerBroadcastReceiver(), user, + mContext.registerReceiverAsUser(mAudioReceiver, mUser, intentFilter, null, null); if (mBtRouteProvider != null) { mHandler.post(() -> { - mBtRouteProvider.start(user); + mBtRouteProvider.start(mUser); notifyProviderState(); }); } updateVolume(); } + public void stop() { + mContext.unregisterReceiver(mAudioReceiver); + if (mBtRouteProvider != null) { + mHandler.post(() -> { + mBtRouteProvider.stop(); + notifyProviderState(); + }); + } + } + @Override public void setCallback(Callback callback) { super.setCallback(callback); diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING index 02095eb70c0e..4ccf09e5e020 100644 --- a/services/core/java/com/android/server/net/TEST_MAPPING +++ b/services/core/java/com/android/server/net/TEST_MAPPING @@ -2,12 +2,8 @@ "presubmit-large": [ { "name": "CtsHostsideNetworkTests", - "file_patterns": ["(/|^)NetworkPolicy[^/]*\\.java"], "options": [ { - "include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests" - }, - { "exclude-annotation": "androidx.test.filters.FlakyTest" }, { diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java index 29ee28175f57..5865adb96333 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java @@ -44,7 +44,6 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.util.SparseSetArray; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -64,14 +63,18 @@ import com.android.server.pm.pkg.component.ParsedMainComponent; import com.android.server.pm.pkg.component.ParsedProvider; import com.android.server.utils.Snappable; import com.android.server.utils.SnapshotCache; -import com.android.server.utils.Snapshots; import com.android.server.utils.Watchable; import com.android.server.utils.WatchableImpl; +import com.android.server.utils.Watched; +import com.android.server.utils.WatchedArrayList; import com.android.server.utils.WatchedArrayMap; +import com.android.server.utils.WatchedArraySet; import com.android.server.utils.WatchedSparseBooleanMatrix; +import com.android.server.utils.WatchedSparseSetArray; import com.android.server.utils.Watcher; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -84,7 +87,7 @@ import java.util.concurrent.Executor; * manifests. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) -public class AppsFilter implements Watchable, Snappable { +public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable { private static final String TAG = "AppsFilter"; @@ -99,32 +102,43 @@ public class AppsFilter implements Watchable, Snappable { * application B is implicitly allowed to query for application A; regardless of any manifest * entries. */ - private final SparseSetArray<Integer> mImplicitlyQueryable = new SparseSetArray<>(); + @Watched + private final WatchedSparseSetArray<Integer> mImplicitlyQueryable; + private final SnapshotCache<WatchedSparseSetArray<Integer>> mImplicitQueryableSnapshot; /** * This contains a list of app UIDs that are implicitly queryable because another app explicitly * interacted with it, but could keep across package updates. For example, if application A * grants persistable uri permission to application B; regardless of any manifest entries. */ - private final SparseSetArray<Integer> mRetainedImplicitlyQueryable = new SparseSetArray<>(); + @Watched + private final WatchedSparseSetArray<Integer> mRetainedImplicitlyQueryable; + private final SnapshotCache<WatchedSparseSetArray<Integer>> + mRetainedImplicitlyQueryableSnapshot; /** * A mapping from the set of App IDs that query other App IDs via package name to the * list of packages that they can see. */ - private final SparseSetArray<Integer> mQueriesViaPackage = new SparseSetArray<>(); + @Watched + private final WatchedSparseSetArray<Integer> mQueriesViaPackage; + private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaPackageSnapshot; /** * A mapping from the set of App IDs that query others via component match to the list * of packages that the they resolve to. */ - private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>(); + @Watched + private final WatchedSparseSetArray<Integer> mQueriesViaComponent; + private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaComponentSnapshot; /** * A mapping from the set of App IDs that query other App IDs via library name to the * list of packages that they can see. */ - private final SparseSetArray<Integer> mQueryableViaUsesLibrary = new SparseSetArray<>(); + @Watched + private final WatchedSparseSetArray<Integer> mQueryableViaUsesLibrary; + private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueryableViaUsesLibrarySnapshot; /** * Executor for running reasonably short background tasks such as building the initial @@ -144,7 +158,9 @@ public class AppsFilter implements Watchable, Snappable { * A set of App IDs that are always queryable by any package, regardless of their manifest * content. */ - private final ArraySet<Integer> mForceQueryable = new ArraySet<>(); + @Watched + private final WatchedArraySet<Integer> mForceQueryable; + private final SnapshotCache<WatchedArraySet<Integer>> mForceQueryableSnapshot; /** * The set of package names provided by the device that should be force queryable regardless of @@ -154,14 +170,15 @@ public class AppsFilter implements Watchable, Snappable { /** True if all system apps should be made queryable by default. */ private final boolean mSystemAppsQueryable; - private final FeatureConfig mFeatureConfig; private final OverlayReferenceMapper mOverlayReferenceMapper; private final StateProvider mStateProvider; private final PackageManagerInternal mPmInternal; - private SigningDetails mSystemSigningDetails; - private Set<String> mProtectedBroadcasts = new ArraySet<>(); + + @Watched + private final WatchedArrayList<String> mProtectedBroadcasts; + private final SnapshotCache<WatchedArrayList<String>> mProtectedBroadcastsSnapshot; private final Object mCacheLock = new Object(); @@ -175,19 +192,21 @@ public class AppsFilter implements Watchable, Snappable { @GuardedBy("mCacheLock") @NonNull private final WatchedSparseBooleanMatrix mShouldFilterCache; + private final SnapshotCache<WatchedSparseBooleanMatrix> mShouldFilterCacheSnapshot; private volatile boolean mSystemReady = false; /** * A cached snapshot. */ - private final SnapshotCache<AppsFilter> mSnapshot; + private final SnapshotCache<AppsFilterImpl> mSnapshot; - private SnapshotCache<AppsFilter> makeCache() { - return new SnapshotCache<AppsFilter>(this, this) { + private SnapshotCache<AppsFilterImpl> makeCache() { + return new SnapshotCache<AppsFilterImpl>(this, this) { @Override - public AppsFilter createSnapshot() { - AppsFilter s = new AppsFilter(mSource); + public AppsFilterImpl createSnapshot() { + AppsFilterImpl s = new AppsFilterImpl(mSource); + s.mWatchable.seal(); return s; } }; @@ -197,6 +216,15 @@ public class AppsFilter implements Watchable, Snappable { * Watchable machinery */ private final WatchableImpl mWatchable = new WatchableImpl(); + /** + * The observer that watches for changes from array members + */ + private final Watcher mObserver = new Watcher() { + @Override + public void onChange(@Nullable Watchable what) { + AppsFilterImpl.this.dispatchChange(what); + } + }; /** * Ensures an observer is in the list, exactly once. The observer cannot be null. The @@ -251,7 +279,7 @@ public class AppsFilter implements Watchable, Snappable { } @VisibleForTesting(visibility = PRIVATE) - AppsFilter(StateProvider stateProvider, + AppsFilterImpl(StateProvider stateProvider, FeatureConfig featureConfig, String[] forceQueryableList, boolean systemAppsQueryable, @@ -267,31 +295,67 @@ public class AppsFilter implements Watchable, Snappable { mPmInternal = pmInternal; mBackgroundExecutor = backgroundExecutor; mShouldFilterCache = new WatchedSparseBooleanMatrix(); + mShouldFilterCacheSnapshot = new SnapshotCache.Auto<>( + mShouldFilterCache, mShouldFilterCache, "AppsFilter.mShouldFilterCache"); + mImplicitlyQueryable = new WatchedSparseSetArray<>(); + mImplicitQueryableSnapshot = new SnapshotCache.Auto<>( + mImplicitlyQueryable, mImplicitlyQueryable, "AppsFilter.mImplicitlyQueryable"); + mRetainedImplicitlyQueryable = new WatchedSparseSetArray<>(); + mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Auto<>( + mRetainedImplicitlyQueryable, mRetainedImplicitlyQueryable, + "AppsFilter.mRetainedImplicitlyQueryable"); + mQueriesViaPackage = new WatchedSparseSetArray<>(); + mQueriesViaPackageSnapshot = new SnapshotCache.Auto<>( + mQueriesViaPackage, mQueriesViaPackage, "AppsFilter.mQueriesViaPackage"); + mQueriesViaComponent = new WatchedSparseSetArray<>(); + mQueriesViaComponentSnapshot = new SnapshotCache.Auto<>( + mQueriesViaComponent, mQueriesViaComponent, "AppsFilter.mQueriesViaComponent"); + mQueryableViaUsesLibrary = new WatchedSparseSetArray<>(); + mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Auto<>( + mQueryableViaUsesLibrary, mQueryableViaUsesLibrary, + "AppsFilter.mQueryableViaUsesLibrary"); + mForceQueryable = new WatchedArraySet<>(); + mForceQueryableSnapshot = new SnapshotCache.Auto<>( + mForceQueryable, mForceQueryable, "AppsFilter.mForceQueryable"); + mProtectedBroadcasts = new WatchedArrayList<>(); + mProtectedBroadcastsSnapshot = new SnapshotCache.Auto<>( + mProtectedBroadcasts, mProtectedBroadcasts, "AppsFilter.mProtectedBroadcasts"); + + registerObservers(); + Watchable.verifyWatchedAttributes(this, mObserver); mSnapshot = makeCache(); } /** * The copy constructor is used by PackageManagerService to construct a snapshot. - * Attributes are not deep-copied since these are supposed to be immutable. - * TODO: deep-copy the attributes, if necessary. */ - private AppsFilter(AppsFilter orig) { - Snapshots.copy(mImplicitlyQueryable, orig.mImplicitlyQueryable); - Snapshots.copy(mRetainedImplicitlyQueryable, orig.mRetainedImplicitlyQueryable); - Snapshots.copy(mQueriesViaPackage, orig.mQueriesViaPackage); - Snapshots.copy(mQueriesViaComponent, orig.mQueriesViaComponent); - Snapshots.copy(mQueryableViaUsesLibrary, orig.mQueryableViaUsesLibrary); + private AppsFilterImpl(AppsFilterImpl orig) { + mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot(); + mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>(); + mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot(); + mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>(); + mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot(); + mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>(); + mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot(); + mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>(); + mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot(); + mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>(); + mForceQueryable = orig.mForceQueryableSnapshot.snapshot(); + mForceQueryableSnapshot = new SnapshotCache.Sealed<>(); + mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot(); + mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>(); mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute; - mForceQueryable.addAll(orig.mForceQueryable); - mForceQueryableByDevicePackageNames = orig.mForceQueryableByDevicePackageNames; + mForceQueryableByDevicePackageNames = + Arrays.copyOf(orig.mForceQueryableByDevicePackageNames, + orig.mForceQueryableByDevicePackageNames.length); mSystemAppsQueryable = orig.mSystemAppsQueryable; mFeatureConfig = orig.mFeatureConfig; mOverlayReferenceMapper = orig.mOverlayReferenceMapper; mStateProvider = orig.mStateProvider; mSystemSigningDetails = orig.mSystemSigningDetails; - mProtectedBroadcasts = orig.mProtectedBroadcasts; synchronized (orig.mCacheLock) { - mShouldFilterCache = orig.mShouldFilterCache.snapshot(); + mShouldFilterCache = orig.mShouldFilterCacheSnapshot.snapshot(); + mShouldFilterCacheSnapshot = new SnapshotCache.Sealed<>(); } mBackgroundExecutor = null; @@ -300,12 +364,24 @@ public class AppsFilter implements Watchable, Snappable { mSystemReady = true; } + @SuppressWarnings("GuardedBy") + private void registerObservers() { + mImplicitlyQueryable.registerObserver(mObserver); + mRetainedImplicitlyQueryable.registerObserver(mObserver); + mQueriesViaPackage.registerObserver(mObserver); + mQueriesViaComponent.registerObserver(mObserver); + mQueryableViaUsesLibrary.registerObserver(mObserver); + mForceQueryable.registerObserver(mObserver); + mProtectedBroadcasts.registerObserver(mObserver); + mShouldFilterCache.registerObserver(mObserver); + } + /** * Return a snapshot. If the cached snapshot is null, build a new one. The logic in * the function ensures that this function returns a valid snapshot even if a race * condition causes the cached snapshot to be cleared asynchronously to this method. */ - public AppsFilter snapshot() { + public AppsFilterSnapshot snapshot() { return mSnapshot.snapshot(); } @@ -366,7 +442,7 @@ public class AppsFilter implements Watchable, Snappable { @Nullable private SparseBooleanArray mLoggingEnabled = null; - private AppsFilter mAppsFilter; + private AppsFilterImpl mAppsFilter; private FeatureConfigImpl( PackageManagerInternal pmInternal, PackageManagerServiceInjector injector) { @@ -374,7 +450,7 @@ public class AppsFilter implements Watchable, Snappable { mInjector = injector; } - public void setAppsFilter(AppsFilter filter) { + public void setAppsFilter(AppsFilterImpl filter) { mAppsFilter = filter; } @@ -478,8 +554,9 @@ public class AppsFilter implements Watchable, Snappable { @Override public void updatePackageState(PackageStateInternal setting, boolean removed) { - final boolean enableLogging = setting.getPkg() != null && - !removed && (setting.getPkg().isTestOnly() || setting.getPkg().isDebuggable()); + final boolean enableLogging = setting.getPkg() != null + && !removed && (setting.getPkg().isTestOnly() + || setting.getPkg().isDebuggable()); enableLogging(setting.getAppId(), enableLogging); if (removed) { mDisabledPackages.remove(setting.getPackageName()); @@ -490,7 +567,7 @@ public class AppsFilter implements Watchable, Snappable { } /** Builder method for an AppsFilter */ - public static AppsFilter create(@NonNull PackageManagerServiceInjector injector, + public static AppsFilterImpl create(@NonNull PackageManagerServiceInjector injector, @NonNull PackageManagerInternal pmInt) { final boolean forceSystemAppsQueryable = injector.getContext().getResources() @@ -514,7 +591,7 @@ public class AppsFilter implements Watchable, Snappable { injector.getUserManagerInternal().getUserInfos()); } }; - AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig, + AppsFilterImpl appsFilter = new AppsFilterImpl(stateProvider, featureConfig, forcedQueryablePackageNames, forceSystemAppsQueryable, null, injector.getBackgroundExecutor(), pmInt); featureConfig.setAppsFilter(appsFilter); @@ -527,7 +604,7 @@ public class AppsFilter implements Watchable, Snappable { /** Returns true if the querying package may query for the potential target package */ private static boolean canQueryViaComponents(AndroidPackage querying, - AndroidPackage potentialTarget, Set<String> protectedBroadcasts) { + AndroidPackage potentialTarget, WatchedArrayList<String> protectedBroadcasts) { if (!querying.getQueriesIntents().isEmpty()) { for (Intent intent : querying.getQueriesIntents()) { if (matchesPackage(intent, potentialTarget, protectedBroadcasts)) { @@ -599,7 +676,7 @@ public class AppsFilter implements Watchable, Snappable { } private static boolean matchesPackage(Intent intent, AndroidPackage potentialTarget, - Set<String> protectedBroadcasts) { + WatchedArrayList<String> protectedBroadcasts) { if (matchesAnyComponents( intent, potentialTarget.getServices(), null /*protectedBroadcasts*/)) { return true; @@ -620,7 +697,7 @@ public class AppsFilter implements Watchable, Snappable { private static boolean matchesAnyComponents(Intent intent, List<? extends ParsedMainComponent> components, - Set<String> protectedBroadcasts) { + WatchedArrayList<String> protectedBroadcasts) { for (int i = ArrayUtils.size(components) - 1; i >= 0; i--) { ParsedMainComponent component = components.get(i); if (!component.isExported()) { @@ -634,7 +711,7 @@ public class AppsFilter implements Watchable, Snappable { } private static boolean matchesAnyFilter(Intent intent, ParsedComponent component, - Set<String> protectedBroadcasts) { + WatchedArrayList<String> protectedBroadcasts) { List<ParsedIntentInfo> intents = component.getIntents(); for (int i = ArrayUtils.size(intents) - 1; i >= 0; i--) { IntentFilter intentFilter = intents.get(i).getIntentFilter(); @@ -646,10 +723,10 @@ public class AppsFilter implements Watchable, Snappable { } private static boolean matchesIntentFilter(Intent intent, IntentFilter intentFilter, - @Nullable Set<String> protectedBroadcasts) { + @Nullable WatchedArrayList<String> protectedBroadcasts) { return intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(), - intent.getData(), intent.getCategories(), "AppsFilter", true, protectedBroadcasts) - > 0; + intent.getData(), intent.getCategories(), "AppsFilter", true, + protectedBroadcasts != null ? protectedBroadcasts.untrackedStorage() : null) > 0; } /** @@ -665,7 +742,8 @@ public class AppsFilter implements Watchable, Snappable { if (recipientUid == visibleUid) { return false; } - final boolean changed = retainOnUpdate + final boolean changed; + changed = retainOnUpdate ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid) : mImplicitlyQueryable.add(recipientUid, visibleUid); if (changed && DEBUG_LOGGING) { @@ -787,7 +865,8 @@ public class AppsFilter implements Watchable, Snappable { for (int i = existingSettings.size() - 1; i >= 0; i--) { final PackageStateInternal existingSetting = existingSettings.valueAt(i); - if (existingSetting.getAppId() == newPkgSetting.getAppId() || existingSetting.getPkg() + if (existingSetting.getAppId() == newPkgSetting.getAppId() + || existingSetting.getPkg() == null) { continue; } @@ -796,11 +875,13 @@ public class AppsFilter implements Watchable, Snappable { if (!newIsForceQueryable) { if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg, newPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(existingSetting.getAppId(), newPkgSetting.getAppId()); + mQueriesViaComponent.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); } if (canQueryViaPackage(existingPkg, newPkg) || canQueryAsInstaller(existingSetting, newPkg)) { - mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId()); + mQueriesViaPackage.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); } if (canQueryViaUsesLibrary(existingPkg, newPkg)) { mQueryableViaUsesLibrary.add(existingSetting.getAppId(), @@ -811,11 +892,13 @@ public class AppsFilter implements Watchable, Snappable { if (!mForceQueryable.contains(existingSetting.getAppId())) { if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg, existingPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(newPkgSetting.getAppId(), existingSetting.getAppId()); + mQueriesViaComponent.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); } if (canQueryViaPackage(newPkg, existingPkg) || canQueryAsInstaller(newPkgSetting, existingPkg)) { - mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId()); + mQueriesViaPackage.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); } if (canQueryViaUsesLibrary(newPkg, existingPkg)) { mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(), @@ -1048,10 +1131,10 @@ public class AppsFilter implements Watchable, Snappable { && pkgSetting.getSigningDetails().signaturesMatchExactly(sysSigningDetails); } - private ArraySet<String> collectProtectedBroadcasts( + private void collectProtectedBroadcasts( ArrayMap<String, ? extends PackageStateInternal> existingSettings, @Nullable String excludePackage) { - ArraySet<String> ret = new ArraySet<>(); + mProtectedBroadcasts.clear(); for (int i = existingSettings.size() - 1; i >= 0; i--) { PackageStateInternal setting = existingSettings.valueAt(i); if (setting.getPkg() == null || setting.getPkg().getPackageName().equals( @@ -1060,10 +1143,9 @@ public class AppsFilter implements Watchable, Snappable { } final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts(); if (!protectedBroadcasts.isEmpty()) { - ret.addAll(protectedBroadcasts); + mProtectedBroadcasts.addAll(protectedBroadcasts); } } - return ret; } /** @@ -1097,19 +1179,9 @@ public class AppsFilter implements Watchable, Snappable { } /** - * Fetches all app Ids that a given setting is currently visible to, per provided user. This - * only includes UIDs >= {@link Process#FIRST_APPLICATION_UID} as all other UIDs can already see - * all applications. - * - * If the setting is visible to all UIDs, null is returned. If an app is not visible to any - * applications, the int array will be empty. - * - * @param users the set of users that should be evaluated for this calculation - * @param existingSettings the set of all package settings that currently exist on device - * @return a SparseArray mapping userIds to a sorted int array of appIds that may view the - * provided setting or null if the app is visible to all and no allow list should be - * applied. + * See {@link AppsFilterSnapshot#getVisibilityAllowList(PackageStateInternal, int[], ArrayMap)} */ + @Override @Nullable public SparseArray<int[]> getVisibilityAllowList(PackageStateInternal setting, int[] users, ArrayMap<String, ? extends PackageStateInternal> existingSettings) { @@ -1164,7 +1236,7 @@ public class AppsFilter implements Watchable, Snappable { * Equivalent to calling {@link #addPackage(PackageStateInternal, boolean)} with * {@code isReplace} equal to {@code false}. * - * @see AppsFilter#addPackage(PackageStateInternal, boolean) + * @see AppsFilterImpl#addPackage(PackageStateInternal, boolean) */ public void addPackage(PackageStateInternal newPkgSetting) { addPackage(newPkgSetting, false /* isReplace */); @@ -1178,13 +1250,15 @@ public class AppsFilter implements Watchable, Snappable { */ public void removePackage(PackageStateInternal setting, boolean isReplace) { mStateProvider.runWithState((settings, users) -> { + final ArraySet<String> additionalChangedPackages; final int userCount = users.length; for (int u = 0; u < userCount; u++) { final int userId = users[u].id; final int removingUid = UserHandle.getUid(userId, setting.getAppId()); mImplicitlyQueryable.remove(removingUid); for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { - mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid); + mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), + removingUid); } if (isReplace) { @@ -1201,12 +1275,14 @@ public class AppsFilter implements Watchable, Snappable { if (!mQueriesViaComponentRequireRecompute) { mQueriesViaComponent.remove(setting.getAppId()); for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { - mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.getAppId()); + mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), + setting.getAppId()); } } mQueriesViaPackage.remove(setting.getAppId()); for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { - mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.getAppId()); + mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), + setting.getAppId()); } mQueryableViaUsesLibrary.remove(setting.getAppId()); for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { @@ -1216,23 +1292,26 @@ public class AppsFilter implements Watchable, Snappable { mForceQueryable.remove(setting.getAppId()); - if (setting.getPkg() != null && !setting.getPkg().getProtectedBroadcasts().isEmpty()) { + if (setting.getPkg() != null + && !setting.getPkg().getProtectedBroadcasts().isEmpty()) { final String removingPackageName = setting.getPkg().getPackageName(); - final Set<String> protectedBroadcasts = mProtectedBroadcasts; - mProtectedBroadcasts = collectProtectedBroadcasts(settings, removingPackageName); + final ArrayList<String> protectedBroadcasts = new ArrayList<>(); + protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage()); + collectProtectedBroadcasts(settings, removingPackageName); if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) { mQueriesViaComponentRequireRecompute = true; } } - ArraySet<String> additionalChangedPackages = - mOverlayReferenceMapper.removePkg(setting.getPackageName()); - + additionalChangedPackages = mOverlayReferenceMapper.removePkg(setting.getPackageName()); mFeatureConfig.updatePackageState(setting, true /*removed*/); - // After removing all traces of the package, if it's part of a shared user, re-add other - // shared user members to re-establish visibility between them and other packages. - // NOTE: this must come after all removals from data structures but before we update the + // After removing all traces of the package, if it's part of a shared user, + // re-add other + // shared user members to re-establish visibility between them and other + // packages. + // NOTE: this must come after all removals from data structures but before we + // update the // cache if (setting.hasSharedUser()) { final ArraySet<PackageStateInternal> sharedUserPackages = @@ -1285,15 +1364,11 @@ public class AppsFilter implements Watchable, Snappable { } /** - * Returns true if the calling package should not be able to see the target package, false if no - * filtering should be done. - * - * @param callingUid the uid of the caller attempting to access a package - * @param callingSetting the setting attempting to access a package or null if it could not be - * found - * @param targetPkgSetting the package being accessed - * @param userId the user in which this access is being attempted + * See + * {@link AppsFilterSnapshot#shouldFilterApplication(int, Object, PackageStateInternal, + * int)} */ + @Override public boolean shouldFilterApplication(int callingUid, @Nullable Object callingSetting, PackageStateInternal targetPkgSetting, int userId) { if (DEBUG_TRACING) { @@ -1610,7 +1685,11 @@ public class AppsFilter implements Watchable, Snappable { } } - boolean canQueryPackage(@NonNull AndroidPackage querying, String potentialTarget) { + /** + * See {@link AppsFilterSnapshot#canQueryPackage(AndroidPackage, String)} + */ + @Override + public boolean canQueryPackage(@NonNull AndroidPackage querying, String potentialTarget) { int appId = UserHandle.getAppId(querying.getUid()); if (appId < Process.FIRST_APPLICATION_UID) { return true; @@ -1665,6 +1744,11 @@ public class AppsFilter implements Watchable, Snappable { + targetPkgSetting + " " + description); } + /** + * See {@link AppsFilterSnapshot#dumpQueries(PrintWriter, Integer, DumpState, int[], + * QuadFunction)} + */ + @Override public void dumpQueries( PrintWriter pw, @Nullable Integer filteringAppId, DumpState dumpState, int[] users, QuadFunction<Integer, Integer, Integer, Boolean, String[]> getPackagesForUid) { @@ -1699,7 +1783,8 @@ public class AppsFilter implements Watchable, Snappable { } } pw.println(" system apps queryable: " + mSystemAppsQueryable); - dumpPackageSet(pw, filteringAppId, mForceQueryable, "forceQueryable", " ", expandPackages); + dumpPackageSet(pw, filteringAppId, mForceQueryable.untrackedStorage(), + "forceQueryable", " ", expandPackages); pw.println(" queries via package name:"); dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages); pw.println(" queries via component:"); @@ -1715,11 +1800,12 @@ public class AppsFilter implements Watchable, Snappable { mRetainedImplicitlyQueryable, " ", expandPackages); } pw.println(" queryable via uses-library:"); - dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", expandPackages); + dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", + expandPackages); } private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId, - SparseSetArray<Integer> queriesMap, String spacing, + WatchedSparseSetArray<Integer> queriesMap, String spacing, @Nullable ToString<Integer> toString) { for (int i = 0; i < queriesMap.size(); i++) { Integer callingId = queriesMap.keyAt(i); @@ -1748,7 +1834,7 @@ public class AppsFilter implements Watchable, Snappable { } private static <T> void dumpPackageSet(PrintWriter pw, @Nullable T filteringId, - Set<T> targetPkgSet, String subTitle, String spacing, + ArraySet<T> targetPkgSet, String subTitle, String spacing, @Nullable ToString<T> toString) { if (targetPkgSet != null && targetPkgSet.size() > 0 && (filteringId == null || targetPkgSet.contains(filteringId))) { diff --git a/services/core/java/com/android/server/pm/AppsFilterSnapshot.java b/services/core/java/com/android/server/pm/AppsFilterSnapshot.java new file mode 100644 index 000000000000..cb8c649ded00 --- /dev/null +++ b/services/core/java/com/android/server/pm/AppsFilterSnapshot.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Process; +import android.util.ArrayMap; +import android.util.SparseArray; + +import com.android.internal.util.function.QuadFunction; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageStateInternal; + +import java.io.PrintWriter; + +/** + * Read-only interface used by computer and snapshots to query the visibility of packages + */ +public interface AppsFilterSnapshot { + /** + * Fetches all app Ids that a given setting is currently visible to, per provided user. This + * only includes UIDs >= {@link Process#FIRST_APPLICATION_UID} as all other UIDs can already see + * all applications. + * + * If the setting is visible to all UIDs, null is returned. If an app is not visible to any + * applications, the int array will be empty. + * + * @param users the set of users that should be evaluated for this calculation + * @param existingSettings the set of all package settings that currently exist on device + * @return a SparseArray mapping userIds to a sorted int array of appIds that may view the + * provided setting or null if the app is visible to all and no allow list should be + * applied. + */ + SparseArray<int[]> getVisibilityAllowList(PackageStateInternal setting, int[] users, + ArrayMap<String, ? extends PackageStateInternal> existingSettings); + + /** + * Returns true if the calling package should not be able to see the target package, false if no + * filtering should be done. + * + * @param callingUid the uid of the caller attempting to access a package + * @param callingSetting the setting attempting to access a package or null if it could not be + * found + * @param targetPkgSetting the package being accessed + * @param userId the user in which this access is being attempted + */ + boolean shouldFilterApplication(int callingUid, @Nullable Object callingSetting, + PackageStateInternal targetPkgSetting, int userId); + + /** + * Returns whether the querying package is allowed to see the target package. + * + * @param querying the querying package + * @param potentialTarget the package name of the target package + */ + boolean canQueryPackage(@NonNull AndroidPackage querying, String potentialTarget); + + /** + * Dump the packages that are queryable by the querying package. + * + * @param pw the output print writer + * @param filteringAppId the querying package's app ID + * @param dumpState the state of the dumping + * @param users the users for which the packages are installed + * @param getPackagesForUid the function that produces the package names for given uids + */ + void dumpQueries(PrintWriter pw, @Nullable Integer filteringAppId, DumpState dumpState, + int[] users, + QuadFunction<Integer, Integer, Integer, Boolean, String[]> getPackagesForUid); + +} diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index df7c3ec6f260..80d61b593fd2 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -385,7 +385,7 @@ public class ComputerEngine implements Computer { private final ResolveInfo mInstantAppInstallerInfo; private final InstantAppRegistry mInstantAppRegistry; private final ApplicationInfo mLocalAndroidApplication; - private final AppsFilter mAppsFilter; + private final AppsFilterSnapshot mAppsFilter; private final WatchedArrayMap<String, Integer> mFrozenPackages; // Immutable service attribute diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index 2fe7913342a2..ec6443db9f0f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -722,6 +722,11 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { return snapshot().getSharedUser(sharedUserAppId); } + @Override + public boolean isUidPrivileged(int uid) { + return snapshot().isUidPrivileged(uid); + } + @NonNull @Override @Deprecated diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f6d8d72cc382..e1af9a7ab96f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -720,7 +720,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Watched - final AppsFilter mAppsFilter; + final AppsFilterImpl mAppsFilter; final PackageParser2.Callback mPackageParserCallback; @@ -979,7 +979,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService public final InstantAppRegistry instantAppRegistry; public final ApplicationInfo androidApplication; public final String appPredictionServicePackage; - public final AppsFilter appsFilter; + public final AppsFilterSnapshot appsFilter; public final ComponentResolverApi componentResolver; public final PackageManagerService service; public final WatchedArrayMap<String, Integer> frozenPackages; @@ -1431,7 +1431,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService RuntimePermissionsPersistence.createInstance(), i.getPermissionManagerServiceInternal(), domainVerificationService, lock), - (i, pm) -> AppsFilter.create(i, i.getLocalService(PackageManagerInternal.class)), + (i, pm) -> AppsFilterImpl.create(i, + i.getLocalService(PackageManagerInternal.class)), (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"), (i, pm) -> SystemConfig.getInstance(), (i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(), diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java index a02237fa90ce..396994b04514 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java @@ -99,7 +99,7 @@ public class PackageManagerServiceInjector { private final Singleton<UserManagerService> mUserManagerProducer; private final Singleton<Settings> mSettingsProducer; - private final Singleton<AppsFilter> mAppsFilterProducer; + private final Singleton<AppsFilterImpl> mAppsFilterProducer; private final Singleton<PlatformCompat> mPlatformCompatProducer; private final Singleton<SystemConfig> mSystemConfigProducer; @@ -148,7 +148,7 @@ public class PackageManagerServiceInjector { Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer, Producer<UserManagerService> userManagerProducer, Producer<Settings> settingsProducer, - Producer<AppsFilter> appsFilterProducer, + Producer<AppsFilterImpl> appsFilterProducer, Producer<PlatformCompat> platformCompatProducer, Producer<SystemConfig> systemConfigProducer, Producer<PackageDexOptimizer> packageDexOptimizerProducer, @@ -282,7 +282,7 @@ public class PackageManagerServiceInjector { return mSettingsProducer.get(this, mPackageManager); } - public AppsFilter getAppsFilter() { + public AppsFilterImpl getAppsFilter() { return mAppsFilterProducer.get(this, mPackageManager); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d340561c2862..ee0fdc07f841 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -5413,190 +5413,170 @@ public class UserManagerService extends IUserManager.Stub { (new Shell()).exec(this, in, out, err, args, callback, resultReceiver); } - private static final String PREFIX_HELP_COMMAND = " "; - private static final String PREFIX_HELP_DESCRIPTION = " "; - private static final String PREFIX_HELP_DESCRIPTION_EXTRA_LINES = " "; - - private static final String CMD_HELP = "help"; - private static final String CMD_LIST = "list"; - private static final String CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS = - "report-system-user-package-whitelist-problems"; - - private static final String ARG_V = "-v"; - private static final String ARG_VERBOSE = "--verbose"; - private static final String ARG_ALL = "--all"; - private static final String ARG_CRITICAL_ONLY = "--critical-only"; - private static final String ARG_MODE = "--mode"; - private final class Shell extends ShellCommand { - @Override - public void onHelp() { - final PrintWriter pw = getOutPrintWriter(); - pw.printf("User manager (user) commands:\n"); - - pw.printf("%s%s\n", PREFIX_HELP_COMMAND, CMD_HELP); - pw.printf("%sPrints this help text.\n\n", PREFIX_HELP_DESCRIPTION); - - pw.printf("%s%s [%s] [%s]\n", PREFIX_HELP_COMMAND, CMD_LIST, ARG_V, ARG_ALL); - pw.printf("%sPrints all users on the system.\n\n", PREFIX_HELP_DESCRIPTION); - - pw.printf("%s%s [%s | %s] [%s] [%s MODE]\n", PREFIX_HELP_COMMAND, - CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS, - ARG_V, ARG_VERBOSE, ARG_CRITICAL_ONLY, ARG_MODE); - - pw.printf("%sReports all issues on user-type package allowlist XML files. Options:\n", - PREFIX_HELP_DESCRIPTION); - pw.printf("%s%s | %s: shows extra info, like number of issues\n", - PREFIX_HELP_DESCRIPTION, ARG_V, ARG_VERBOSE); - pw.printf("%s%s: show only critical issues, excluding warnings\n", - PREFIX_HELP_DESCRIPTION, ARG_CRITICAL_ONLY); - pw.printf("%s%s MODE: shows what errors would be if device used mode MODE\n" - + "%s(where MODE is the allowlist mode integer as defined by " - + "config_userTypePackageWhitelistMode)\n\n", - PREFIX_HELP_DESCRIPTION, ARG_MODE, PREFIX_HELP_DESCRIPTION_EXTRA_LINES); - } - - @Override - public int onCommand(String cmd) { - if (cmd == null) { - return handleDefaultCommands(cmd); + @Override + public void onHelp() { + final PrintWriter pw = getOutPrintWriter(); + pw.println("User manager (user) commands:"); + pw.println(" help"); + pw.println(" Prints this help text."); + pw.println(); + pw.println(" list [-v | --verbose] [--all]"); + pw.println(" Prints all users on the system."); + pw.println(); + pw.println(" report-system-user-package-whitelist-problems [-v | --verbose] " + + "[--critical-only] [--mode MODE]"); + pw.println(" Reports all issues on user-type package allowlist XML files. Options:"); + pw.println(" -v | --verbose: shows extra info, like number of issues"); + pw.println(" --critical-only: show only critical issues, excluding warnings"); + pw.println(" --mode MODE: shows what errors would be if device used mode MODE"); + pw.println(" (where MODE is the allowlist mode integer as defined by " + + "config_userTypePackageWhitelistMode)"); } - try { - switch(cmd) { - case CMD_LIST: - return runList(); - case CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS: - return runReportPackageAllowlistProblems(); - default: - return handleDefaultCommands(cmd); + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); } - } catch (RemoteException e) { - getOutPrintWriter().println("Remote exception: " + e); + + try { + switch(cmd) { + case "list": + return runList(); + case "report-system-user-package-whitelist-problems": + return runReportPackageAllowlistProblems(); + default: + return handleDefaultCommands(cmd); + } + } catch (RemoteException e) { + getOutPrintWriter().println("Remote exception: " + e); + } + return -1; } - return -1; - } - private int runList() throws RemoteException { - final PrintWriter pw = getOutPrintWriter(); - boolean all = false; - boolean verbose = false; - String opt; - while ((opt = getNextOption()) != null) { - switch (opt) { - case ARG_V: - verbose = true; - break; - case ARG_ALL: - all = true; - break; - default: - pw.println("Invalid option: " + opt); - return -1; - } - } - final IActivityManager am = ActivityManager.getService(); - final List<UserInfo> users = getUsers(/* excludePartial= */ !all, - /* excludingDying=*/ false, /* excludePreCreated= */ !all); - if (users == null) { - pw.println("Error: couldn't get users"); - return 1; - } else { - final int size = users.size(); - int currentUser = UserHandle.USER_NULL; - if (verbose) { - pw.printf("%d users:\n\n", size); - currentUser = am.getCurrentUser().id; - } else { - // NOTE: the standard "list users" command is used by integration tests and - // hence should not be changed. If you need to add more info, use the - // verbose option. - pw.println("Users:"); + private int runList() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + boolean all = false; + boolean verbose = false; + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "-v": + case "--verbose": + verbose = true; + break; + case "--all": + all = true; + break; + default: + pw.println("Invalid option: " + opt); + return -1; + } } - for (int i = 0; i < size; i++) { - final UserInfo user = users.get(i); - final boolean running = am.isUserRunning(user.id, 0); - final boolean current = user.id == currentUser; - final boolean hasParent = user.profileGroupId != user.id - && user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID; + final IActivityManager am = ActivityManager.getService(); + final List<UserInfo> users = getUsers(/* excludePartial= */ !all, + /* excludeDying= */ false, /* excludePreCreated= */ !all); + if (users == null) { + pw.println("Error: couldn't get users"); + return 1; + } else { + final int size = users.size(); + int currentUser = UserHandle.USER_NULL; if (verbose) { - final DevicePolicyManagerInternal dpm = getDevicePolicyManagerInternal(); - String deviceOwner = ""; - String profileOwner = ""; - if (dpm != null) { - final long ident = Binder.clearCallingIdentity(); - // NOTE: dpm methods below CANNOT be called while holding the mUsersLock - try { - if (dpm.getDeviceOwnerUserId() == user.id) { - deviceOwner = " (device-owner)"; - } - if (dpm.getProfileOwnerAsUser(user.id) != null) { - profileOwner = " (profile-owner)"; - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - pw.printf("%d: id=%d, name=%s, type=%s, flags=%s%s%s%s%s%s%s%s%s\n", - i, - user.id, - user.name, - user.userType.replace("android.os.usertype.", ""), - UserInfo.flagsToString(user.flags), - hasParent ? " (parentId=" + user.profileGroupId + ")" : "", - running ? " (running)" : "", - user.partial ? " (partial)" : "", - user.preCreated ? " (pre-created)" : "", - user.convertedFromPreCreated ? " (converted)" : "", - deviceOwner, profileOwner, - current ? " (current)" : ""); + pw.printf("%d users:\n\n", size); + currentUser = am.getCurrentUser().id; } else { // NOTE: the standard "list users" command is used by integration tests and // hence should not be changed. If you need to add more info, use the // verbose option. - pw.printf("\t%s%s\n", user, running ? " running" : ""); + pw.println("Users:"); } + for (int i = 0; i < size; i++) { + final UserInfo user = users.get(i); + final boolean running = am.isUserRunning(user.id, 0); + final boolean current = user.id == currentUser; + final boolean hasParent = user.profileGroupId != user.id + && user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID; + if (verbose) { + final DevicePolicyManagerInternal dpm = getDevicePolicyManagerInternal(); + String deviceOwner = ""; + String profileOwner = ""; + if (dpm != null) { + final long ident = Binder.clearCallingIdentity(); + // NOTE: dpm methods below CANNOT be called while holding the mUsersLock + try { + if (dpm.getDeviceOwnerUserId() == user.id) { + deviceOwner = " (device-owner)"; + } + if (dpm.getProfileOwnerAsUser(user.id) != null) { + profileOwner = " (profile-owner)"; + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + pw.printf("%d: id=%d, name=%s, type=%s, flags=%s%s%s%s%s%s%s%s%s\n", + i, + user.id, + user.name, + user.userType.replace("android.os.usertype.", ""), + UserInfo.flagsToString(user.flags), + hasParent ? " (parentId=" + user.profileGroupId + ")" : "", + running ? " (running)" : "", + user.partial ? " (partial)" : "", + user.preCreated ? " (pre-created)" : "", + user.convertedFromPreCreated ? " (converted)" : "", + deviceOwner, profileOwner, + current ? " (current)" : ""); + } else { + // NOTE: the standard "list users" command is used by integration tests and + // hence should not be changed. If you need to add more info, use the + // verbose option. + pw.printf("\t%s%s\n", user, running ? " running" : ""); + } + } + return 0; } - return 0; } - } - private int runReportPackageAllowlistProblems() { - final PrintWriter pw = getOutPrintWriter(); - boolean verbose = false; - boolean criticalOnly = false; - int mode = UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_NONE; - String opt; - while ((opt = getNextOption()) != null) { - switch (opt) { - case ARG_V: - case ARG_VERBOSE: - verbose = true; - break; - case ARG_CRITICAL_ONLY: - criticalOnly = true; - break; - case ARG_MODE: - mode = Integer.parseInt(getNextArgRequired()); - break; - default: - pw.println("Invalid option: " + opt); - return -1; + private int runReportPackageAllowlistProblems() { + final PrintWriter pw = getOutPrintWriter(); + boolean verbose = false; + boolean criticalOnly = false; + int mode = UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_NONE; + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "-v": + case "--verbose": + verbose = true; + break; + case "--critical-only": + criticalOnly = true; + break; + case "--mode": + mode = Integer.parseInt(getNextArgRequired()); + break; + default: + pw.println("Invalid option: " + opt); + return -1; + } } - } - Slog.d(LOG_TAG, "runReportPackageAllowlistProblems(): verbose=" + verbose - + ", criticalOnly=" + criticalOnly - + ", mode=" + UserSystemPackageInstaller.modeToString(mode)); + Slog.d(LOG_TAG, "runReportPackageAllowlistProblems(): verbose=" + verbose + + ", criticalOnly=" + criticalOnly + + ", mode=" + UserSystemPackageInstaller.modeToString(mode)); - try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { - mSystemPackageInstaller.dumpPackageWhitelistProblems(ipw, mode, verbose, - criticalOnly); + try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { + mSystemPackageInstaller.dumpPackageWhitelistProblems(ipw, mode, verbose, + criticalOnly); + } + return 0; } - return 0; - } - } + + } // class Shell @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { diff --git a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java index 0418afbf29ee..1a2ff264319e 100644 --- a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java +++ b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java @@ -19,6 +19,7 @@ package com.android.server.pm.parsing.library; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.utils.build.UnboundedSdkLevel; import com.android.server.SystemConfig; import com.android.server.pm.parsing.pkg.ParsedPackage; @@ -51,8 +52,11 @@ public class ApexSharedLibraryUpdater extends PackageSharedLibraryUpdater { private void updateSharedLibraryForPackage(SystemConfig.SharedLibraryEntry entry, ParsedPackage parsedPackage) { - if (entry.onBootclasspathBefore != 0 - && parsedPackage.getTargetSdkVersion() < entry.onBootclasspathBefore) { + if (entry.onBootclasspathBefore != null + && isTargetSdkAtMost( + parsedPackage.getTargetSdkVersion(), + entry.onBootclasspathBefore) + && UnboundedSdkLevel.isAtLeast(entry.onBootclasspathBefore)) { // this package targets an API where this library was in the BCP, so add // the library transparently in case the package is using it prefixRequiredLibrary(parsedPackage, entry.name); @@ -64,4 +68,19 @@ public class ApexSharedLibraryUpdater extends PackageSharedLibraryUpdater { removeLibrary(parsedPackage, entry.name); } } + + private static boolean isTargetSdkAtMost(int targetSdk, String onBcpBefore) { + if (isCodename(onBcpBefore)) { + return targetSdk < 10000; + } + return targetSdk < Integer.parseInt(onBcpBefore); + } + + private static boolean isCodename(String version) { + if (version.length() == 0) { + throw new IllegalArgumentException(); + } + // assume Android codenames start with upper case letters. + return Character.isUpperCase((version.charAt(0))); + } } diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java index 70d69c663572..e157a277366f 100644 --- a/services/core/java/com/android/server/policy/AppOpsPolicy.java +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -187,8 +187,12 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat initializeActivityRecognizersTags(); - // If this device does not have telephony, restrict the phone call ops - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + // Restrict phone call ops if the TelecomService will not start (conditioned on having + // FEATURE_MICROPHONE, FEATURE_TELECOM, or FEATURE_TELEPHONY). + PackageManager pm = mContext.getPackageManager(); + if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) + && !pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) + && !pm.hasSystemFeature(PackageManager.FEATURE_TELECOM)) { AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); appOps.setUserRestrictionForUser(AppOpsManager.OP_PHONE_CALL_MICROPHONE, true, mToken, null, UserHandle.USER_ALL); diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java index c0ab65a3215c..05d92beed11f 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java @@ -17,7 +17,6 @@ package com.android.server.soundtrigger_middleware; import android.annotation.NonNull; -import android.media.permission.SafeCloseable; import android.media.soundtrigger.ModelParameterRange; import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; @@ -30,6 +29,7 @@ import android.media.soundtrigger.Status; import android.os.IBinder; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.Queue; @@ -63,18 +63,24 @@ import java.util.concurrent.ConcurrentHashMap; */ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal, ICaptureStateNotifier.Listener { - private final @NonNull ISoundTriggerHal mDelegate; + @NonNull private final ISoundTriggerHal mDelegate; private GlobalCallback mGlobalCallback; + /** + * This lock must be held to synchronize forward calls (start/stop/onCaptureStateChange) that + * update the mActiveModels set and mCaptureState. + * It must not be locked in HAL callbacks to avoid deadlocks. + */ + @NonNull private final Object mStartStopLock = new Object(); /** * Information about a model that is currently loaded. This is needed in order to be able to * send abort events to its designated callback. */ private static class LoadedModel { - final int type; - final @NonNull ModelCallback callback; + public final int type; + @NonNull public final ModelCallback callback; - private LoadedModel(int type, @NonNull ModelCallback callback) { + LoadedModel(int type, @NonNull ModelCallback callback) { this.type = type; this.callback = callback; } @@ -83,19 +89,19 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal /** * This map holds the model type for every model that is loaded. */ - private final @NonNull Map<Integer, LoadedModel> mLoadedModels = new ConcurrentHashMap<>(); + @NonNull private final Map<Integer, LoadedModel> mLoadedModels = new ConcurrentHashMap<>(); /** * A set of all models that are currently active. * We use this in order to know which models to stop in case of external capture. * Used as a lock to synchronize operations that effect activity. */ - private final @NonNull Set<Integer> mActiveModels = new HashSet<>(); + @NonNull private final Set<Integer> mActiveModels = new HashSet<>(); /** * Notifier for changes in capture state. */ - private final @NonNull ICaptureStateNotifier mNotifier; + @NonNull private final ICaptureStateNotifier mNotifier; /** * Whether capture is active. @@ -106,10 +112,10 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal * Since we're wrapping the death recipient, we need to keep a translation map for unlinking. * Key is the client recipient, value is the wrapper. */ - private final @NonNull Map<IBinder.DeathRecipient, IBinder.DeathRecipient> + @NonNull private final Map<IBinder.DeathRecipient, IBinder.DeathRecipient> mDeathRecipientMap = new ConcurrentHashMap<>(); - private final @NonNull CallbackThread mCallbackThread = new CallbackThread(); + @NonNull private final CallbackThread mCallbackThread = new CallbackThread(); public SoundTriggerHalConcurrentCaptureHandler( @NonNull ISoundTriggerHal delegate, @@ -122,20 +128,28 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal @Override public void startRecognition(int modelHandle, int deviceHandle, int ioHandle, RecognitionConfig config) { - synchronized (mActiveModels) { - if (mCaptureState) { - throw new RecoverableException(Status.RESOURCE_CONTENTION); + synchronized (mStartStopLock) { + synchronized (mActiveModels) { + if (mCaptureState) { + throw new RecoverableException(Status.RESOURCE_CONTENTION); + } + mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config); + mActiveModels.add(modelHandle); } - mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config); - mActiveModels.add(modelHandle); } } @Override public void stopRecognition(int modelHandle) { - synchronized (mActiveModels) { - mDelegate.stopRecognition(modelHandle); - mActiveModels.remove(modelHandle); + synchronized (mStartStopLock) { + boolean wasActive; + synchronized (mActiveModels) { + wasActive = mActiveModels.remove(modelHandle); + } + if (wasActive) { + // Must be done outside of the lock, since it may trigger synchronous callbacks. + mDelegate.stopRecognition(modelHandle); + } } // Block until all previous events are delivered. Since this is potentially blocking on // upward calls, it must be done outside the lock. @@ -144,27 +158,38 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal @Override public void onCaptureStateChange(boolean active) { - synchronized (mActiveModels) { + synchronized (mStartStopLock) { if (active) { - // Abort all active models. This must be done as one transaction to the event - // thread, in order to be able to dedupe events before they are delivered. - try (SafeCloseable ignored = mCallbackThread.stallReader()) { - for (int modelHandle : mActiveModels) { - mDelegate.stopRecognition(modelHandle); - LoadedModel model = mLoadedModels.get(modelHandle); - // An abort event must be the last one for its model. - mCallbackThread.pushWithDedupe(modelHandle, true, - () -> notifyAbort(modelHandle, model)); - } - } + abortAllActiveModels(); } else { - mGlobalCallback.onResourcesAvailable(); + if (mGlobalCallback != null) { + mGlobalCallback.onResourcesAvailable(); + } } - mCaptureState = active; } } + private void abortAllActiveModels() { + while (true) { + int toStop; + synchronized (mActiveModels) { + Iterator<Integer> iterator = mActiveModels.iterator(); + if (!iterator.hasNext()) { + return; + } + toStop = iterator.next(); + mActiveModels.remove(toStop); + } + // Invoke stop outside of the lock. + mDelegate.stopRecognition(toStop); + + LoadedModel model = mLoadedModels.get(toStop); + // Queue an abort event (no need to flush). + mCallbackThread.push(() -> notifyAbort(toStop, model)); + } + } + @Override public int loadSoundModel(SoundModel soundModel, ModelCallback callback) { int handle = mDelegate.loadSoundModel(soundModel, new CallbackWrapper(callback)); @@ -188,23 +213,13 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal @Override public void registerCallback(GlobalCallback callback) { - mGlobalCallback = new GlobalCallback() { - @Override - public void onResourcesAvailable() { - mCallbackThread.push(callback::onResourcesAvailable); - } - }; + mGlobalCallback = () -> mCallbackThread.push(callback::onResourcesAvailable); mDelegate.registerCallback(mGlobalCallback); } @Override public void linkToDeath(IBinder.DeathRecipient recipient) { - IBinder.DeathRecipient wrapper = new IBinder.DeathRecipient() { - @Override - public void binderDied() { - mCallbackThread.push(() -> recipient.binderDied()); - } - }; + IBinder.DeathRecipient wrapper = () -> mCallbackThread.push(recipient::binderDied); mDelegate.linkToDeath(wrapper); mDeathRecipientMap.put(recipient, wrapper); } @@ -215,7 +230,7 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal } private class CallbackWrapper implements ISoundTriggerHal.ModelCallback { - private final @NonNull ISoundTriggerHal.ModelCallback mDelegateCallback; + @NonNull private final ISoundTriggerHal.ModelCallback mDelegateCallback; private CallbackWrapper(@NonNull ModelCallback delegateCallback) { mDelegateCallback = delegateCallback; @@ -223,18 +238,36 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal @Override public void recognitionCallback(int modelHandle, RecognitionEvent event) { - // A recognition event must be the last one for its model, unless it is a forced one - // (those leave the model active). - mCallbackThread.pushWithDedupe(modelHandle, !event.recognitionStillActive, - () -> mDelegateCallback.recognitionCallback(modelHandle, event)); + synchronized (mActiveModels) { + if (!mActiveModels.contains(modelHandle)) { + // Discard the event. + return; + } + if (!event.recognitionStillActive) { + mActiveModels.remove(modelHandle); + } + // A recognition event must be the last one for its model, unless it indicates that + // recognition is still active. + mCallbackThread.push( + () -> mDelegateCallback.recognitionCallback(modelHandle, event)); + } } @Override public void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event) { - // A recognition event must be the last one for its model, unless it is a forced one - // (those leave the model active). - mCallbackThread.pushWithDedupe(modelHandle, !event.common.recognitionStillActive, - () -> mDelegateCallback.phraseRecognitionCallback(modelHandle, event)); + synchronized (mActiveModels) { + if (!mActiveModels.contains(modelHandle)) { + // Discard the event. + return; + } + if (!event.common.recognitionStillActive) { + mActiveModels.remove(modelHandle); + } + // A recognition event must be the last one for its model, unless it indicates that + // recognition is still active. + mCallbackThread.push( + () -> mDelegateCallback.phraseRecognitionCallback(modelHandle, event)); + } } @Override @@ -254,36 +287,12 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal * <ul> * <li>Events are processed on a separate thread than the thread that pushed them, in the order * they were pushed. - * <li>Events can be deduped upon entry to the queue. This is achieved as follows: - * <ul> - * <li>Temporarily stall the reader via {@link #stallReader()}. - * <li>Within this scope, push as many events as needed via - * {@link #pushWithDedupe(int, boolean, Runnable)}. - * If an event with the same model handle as the one being pushed is already in the queue - * and has been marked as "lastForModel", the new event will be discarded before entering - * the queue. - * <li>Finally, un-stall the reader by existing the scope. - * <li>Events that do not require deduping can be pushed via {@link #push(Runnable)}. - * </ul> * <li>Events can be flushed via {@link #flush()}. This will block until all events pushed prior * to this call have been fully processed. * </ul> */ private static class CallbackThread { - private static class Entry { - final boolean lastForModel; - final int modelHandle; - final Runnable runnable; - - private Entry(boolean lastForModel, int modelHandle, Runnable runnable) { - this.lastForModel = lastForModel; - this.modelHandle = modelHandle; - this.runnable = runnable; - } - } - - private boolean mStallReader = false; - private final Queue<Entry> mList = new LinkedList<>(); + private final Queue<Runnable> mList = new LinkedList<>(); private int mPushCount = 0; private int mProcessedCount = 0; @@ -312,23 +321,11 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal * @param runnable The runnable to push. */ void push(Runnable runnable) { - pushEntry(new Entry(false, 0, runnable), false); - } - - - /** - * Push a new runnable to the queue, with deduping. - * If an entry with the same model handle is already in the queue and was designated as - * last for model, this one will be discarded. - * - * @param modelHandle The model handle, used for deduping purposes. - * @param lastForModel If true, this entry will be considered the last one for this model - * and any subsequence calls for this handle (whether lastForModel or - * not) will be discarded while this entry is in the queue. - * @param runnable The runnable to push. - */ - void pushWithDedupe(int modelHandle, boolean lastForModel, Runnable runnable) { - pushEntry(new Entry(lastForModel, modelHandle, runnable), true); + synchronized (mList) { + mList.add(runnable); + mPushCount++; + mList.notifyAll(); + } } /** @@ -346,45 +343,15 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal } } - /** - * Creates a scope (using a try-with-resources block), within which events that are pushed - * remain queued and processed. This is useful in order to utilize deduping. - */ - SafeCloseable stallReader() { - synchronized (mList) { - mStallReader = true; - return () -> { - synchronized (mList) { - mStallReader = false; - mList.notifyAll(); - } - }; - } - } - - private void pushEntry(Entry entry, boolean dedupe) { - synchronized (mList) { - if (dedupe) { - for (Entry existing : mList) { - if (existing.lastForModel && existing.modelHandle == entry.modelHandle) { - return; - } - } - } - mList.add(entry); - mPushCount++; - mList.notifyAll(); - } - } - private Runnable pop() throws InterruptedException { synchronized (mList) { - while (mStallReader || mList.isEmpty()) { + while (mList.isEmpty()) { mList.wait(); } - return mList.remove().runnable; + return mList.remove(); } } + } /** Notify the client that recognition has been aborted. */ diff --git a/services/core/java/com/android/server/utils/WatchedArrayList.java b/services/core/java/com/android/server/utils/WatchedArrayList.java index bb0ba1329d86..6059f9675e34 100644 --- a/services/core/java/com/android/server/utils/WatchedArrayList.java +++ b/services/core/java/com/android/server/utils/WatchedArrayList.java @@ -273,6 +273,13 @@ public class WatchedArrayList<E> extends WatchableImpl } /** + * Return true if all the objects in the given collection are in this array list. + */ + public boolean containsAll(Collection<?> c) { + return mStorage.containsAll(c); + } + + /** * Ensure capacity. */ public void ensureCapacity(int min) { diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java index 25ae00004b3e..c43e7f9babe6 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java +++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java @@ -18,6 +18,7 @@ package com.android.server.utils; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; @@ -168,12 +169,19 @@ public class WatchedSparseBooleanMatrix extends WatchableImpl implements Snappab * A copy constructor that can be used for snapshotting. */ private WatchedSparseBooleanMatrix(WatchedSparseBooleanMatrix r) { - mOrder = r.mOrder; - mSize = r.mSize; - mKeys = r.mKeys.clone(); - mMap = r.mMap.clone(); - mInUse = r.mInUse.clone(); - mValues = r.mValues.clone(); + copyFrom(r); + } + + /** + * Copy from src to this. + */ + public void copyFrom(@NonNull WatchedSparseBooleanMatrix src) { + mOrder = src.mOrder; + mSize = src.mSize; + mKeys = src.mKeys.clone(); + mMap = src.mMap.clone(); + mInUse = src.mInUse.clone(); + mValues = src.mValues.clone(); } /** diff --git a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java new file mode 100644 index 000000000000..05db12e49a13 --- /dev/null +++ b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 android.annotation.NonNull; +import android.util.ArraySet; +import android.util.SparseSetArray; + + +/** + * A watched variant of SparseSetArray. Changes to the array are notified to + * registered {@link Watcher}s. + * @param <T> The element type, stored in the SparseSetArray. + */ +public class WatchedSparseSetArray<T> extends WatchableImpl implements Snappable { + // The storage + private final SparseSetArray mStorage; + + // A private convenience function + private void onChanged() { + dispatchChange(this); + } + + public WatchedSparseSetArray() { + mStorage = new SparseSetArray(); + } + + /** + * Creates a new WatchedSparseSetArray from an existing WatchedSparseSetArray and copy its data + */ + public WatchedSparseSetArray(@NonNull WatchedSparseSetArray<T> watchedSparseSetArray) { + mStorage = new SparseSetArray(watchedSparseSetArray.untrackedStorage()); + } + + /** + * Return the underlying storage. This breaks the wrapper but is necessary when + * passing the array to distant methods. + */ + public SparseSetArray<T> untrackedStorage() { + return mStorage; + } + + /** + * Add a value for key n. + * @return FALSE when the value already existed for the given key, TRUE otherwise. + */ + public boolean add(int n, T value) { + final boolean res = mStorage.add(n, value); + onChanged(); + return res; + } + + /** + * Removes all mappings from this SparseSetArray. + */ + public void clear() { + mStorage.clear(); + onChanged(); + } + + /** + * @return whether the value exists for the key n. + */ + public boolean contains(int n, T value) { + return mStorage.contains(n, value); + } + + /** + * @return the set of items of key n + */ + public ArraySet<T> get(int n) { + return mStorage.get(n); + } + + /** + * Remove a value for key n. + * @return TRUE when the value existed for the given key and removed, FALSE otherwise. + */ + public boolean remove(int n, T value) { + if (mStorage.remove(n, value)) { + onChanged(); + return true; + } + return false; + } + + /** + * Remove all values for key n. + */ + public void remove(int n) { + mStorage.remove(n); + onChanged(); + } + + /** + * Return the size of the SparseSetArray. + */ + public int size() { + return mStorage.size(); + } + + /** + * Return the key stored at the given index. + */ + public int keyAt(int index) { + return mStorage.keyAt(index); + } + + /** + * Return the size of the array at the given index. + */ + public int sizeAt(int index) { + return mStorage.sizeAt(index); + } + + /** + * Return the value in the SetArray at the given key index and value index. + */ + public T valueAt(int intIndex, int valueIndex) { + return (T) mStorage.valueAt(intIndex, valueIndex); + } + + @NonNull + @Override + public Object snapshot() { + WatchedSparseSetArray l = new WatchedSparseSetArray(this); + l.seal(); + return l; + } + + /** + * Make <this> a snapshot of the argument. Note that <this> is immutable when the + * method returns. <this> must be empty when the function is called. + * @param r The source array, which is copied into <this> + */ + public void snapshot(@NonNull WatchedSparseSetArray<T> r) { + snapshot(this, r); + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static void snapshot(@NonNull WatchedSparseSetArray dst, + @NonNull WatchedSparseSetArray src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int arraySize = src.size(); + for (int i = 0; i < arraySize; i++) { + final ArraySet set = src.get(i); + final int setSize = set.size(); + for (int j = 0; j < setSize; j++) { + dst.add(src.keyAt(i), set.valueAt(j)); + } + } + dst.seal(); + } +} diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java index 8ecc51b3087c..3e364314e10f 100644 --- a/services/core/java/com/android/server/vibrator/Vibration.java +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -66,6 +66,7 @@ final class Vibration { IGNORED_FOR_ONGOING, IGNORED_FOR_POWER, IGNORED_FOR_RINGER_MODE, + IGNORED_FOR_RINGTONE, IGNORED_FOR_SETTINGS, IGNORED_SUPERSEDED, } diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index bf3298558cd6..d7341cb37685 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -733,6 +733,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { + attrs); } break; + case IGNORED_FOR_RINGTONE: + if (DEBUG) { + Slog.d(TAG, "Ignoring incoming vibration in favor of ringtone vibration"); + } + break; + default: if (DEBUG) { Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs @@ -809,6 +815,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return Vibration.Status.IGNORED_FOR_ALARM; } + if (currentVibration.attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE) { + return Vibration.Status.IGNORED_FOR_RINGTONE; + } + if (currentVibration.isRepeating()) { return Vibration.Status.IGNORED_FOR_ONGOING; } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 003268b33cdc..f6396556ae22 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -195,7 +195,6 @@ class ActivityStarter { private TaskFragment mInTaskFragment; @VisibleForTesting boolean mAddingToTask; - private Task mReuseTask; private ActivityInfo mNewTaskInfo; private Intent mNewTaskIntent; @@ -204,6 +203,7 @@ class ActivityStarter { // The task that the last activity was started into. We currently reset the actual start // activity's task and as a result may not have a reference to the task in all cases private Task mTargetTask; + private boolean mIsTaskCleared; private boolean mMovedToFront; private boolean mNoAnimation; private boolean mAvoidMoveToFront; @@ -597,7 +597,6 @@ class ActivityStarter { mInTask = starter.mInTask; mInTaskFragment = starter.mInTaskFragment; mAddingToTask = starter.mAddingToTask; - mReuseTask = starter.mReuseTask; mNewTaskInfo = starter.mNewTaskInfo; mNewTaskIntent = starter.mNewTaskIntent; @@ -605,6 +604,7 @@ class ActivityStarter { mTargetTask = starter.mTargetTask; mTargetRootTask = starter.mTargetRootTask; + mIsTaskCleared = starter.mIsTaskCleared; mMovedToFront = starter.mMovedToFront; mNoAnimation = starter.mNoAnimation; mAvoidMoveToFront = starter.mAvoidMoveToFront; @@ -1568,10 +1568,7 @@ class ActivityStarter { return; } - final int clearTaskFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK; - boolean clearedTask = (mLaunchFlags & clearTaskFlags) == clearTaskFlags - && mReuseTask != null; - if (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP || clearedTask) { + if (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP) { // The activity was already running so it wasn't started, but either brought to the // front or the new intent was delivered to it since it was already in front. Notify // anyone interested in this piece of information. @@ -1581,7 +1578,7 @@ class ActivityStarter { final ActivityRecord top = targetTask.getTopNonFinishingActivity(); final boolean visible = top != null && top.isVisible(); mService.getTaskChangeNotificationController().notifyActivityRestartAttempt( - targetTask.getTaskInfo(), homeTaskVisible, clearedTask, visible); + targetTask.getTaskInfo(), homeTaskVisible, mIsTaskCleared, visible); } if (ActivityManager.isStartResultSuccessful(result)) { @@ -1927,6 +1924,7 @@ class ActivityStarter { return START_SUCCESS; } + /** Returns the leaf task where the target activity may be placed. */ private Task computeTargetTask() { if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { @@ -1935,6 +1933,12 @@ class ActivityStarter { } else if (mSourceRecord != null) { return mSourceRecord.getTask(); } else if (mInTask != null) { + // The task is specified from AppTaskImpl, so it may not be attached yet. + if (!mInTask.isAttached()) { + // Attach the task to display area. Ignore the returned root task (though usually + // they are the same) because "target task" should be leaf task. + getOrCreateRootTask(mStartActivity, mLaunchFlags, mInTask, mOptions); + } return mInTask; } else { final Task rootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, null /* task */, @@ -2225,13 +2229,12 @@ class ActivityStarter { // activity. Well that should not be too hard... // Note: we must persist the {@link Task} first as intentActivity could be // removed from calling performClearTaskLocked (For example, if it is being brought out - // of history or if it is finished immediately), thus disassociating the task. Also note - // that mReuseTask is reset as a result of {@link Task#performClearTaskLocked} - // launching another activity. Keep the task-overlay activity because the targetTask - // will be reused to launch new activity. + // of history or if it is finished immediately), thus disassociating the task. Keep the + // task-overlay activity because the targetTask will be reused to launch new activity. targetTask.performClearTaskForReuse(true /* excludingTaskOverlay*/); targetTask.setIntent(mStartActivity); mAddingToTask = true; + mIsTaskCleared = true; } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || isDocumentLaunchesIntoExisting(mLaunchFlags) || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK, @@ -2344,7 +2347,6 @@ class ActivityStarter { mInTask = null; mInTaskFragment = null; mAddingToTask = false; - mReuseTask = null; mNewTaskInfo = null; mNewTaskIntent = null; @@ -2352,6 +2354,7 @@ class ActivityStarter { mTargetRootTask = null; mTargetTask = null; + mIsTaskCleared = false; mMovedToFront = false; mNoAnimation = false; mAvoidMoveToFront = false; @@ -2569,8 +2572,6 @@ class ActivityStarter { } else { mAddingToTask = true; } - - mReuseTask = mInTask; } else { mInTask = null; // Launch ResolverActivity in the source task, so that it stays in the task bounds @@ -2843,7 +2844,7 @@ class ActivityStarter { mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions); task.mTransitionController.collectExistenceChange(task); - addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask"); + addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask"); ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s", mStartActivity, mStartActivity.getTask()); @@ -2953,11 +2954,6 @@ class ActivityStarter { private Task getOrCreateRootTask(ActivityRecord r, int launchFlags, Task task, ActivityOptions aOptions) { - // We are reusing a task, keep the root task! - if (mReuseTask != null) { - return mReuseTask.getRootTask(); - } - final boolean onTop = (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind; final Task sourceTask = mSourceRecord != null ? mSourceRecord.getTask() : null; diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index eb5ca9c2f43b..95de040551b1 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -897,6 +897,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { proc.getThread(), r.token); final boolean isTransitionForward = r.isTransitionForward(); + final IBinder fragmentToken = r.getTaskFragment().getFragmentToken(); clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global @@ -907,7 +908,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(), results, newIntents, r.takeOptions(), isTransitionForward, proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController, - r.shareableActivityToken, r.getLaunchedFromBubble())); + r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken)); // Set desired final state. final ActivityLifecycleItem lifecycleItem; diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 22a2c41ca5f3..6e46fa6b67d0 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -26,6 +26,8 @@ import android.content.Intent; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; import android.os.UserHandle; /** @@ -54,6 +56,16 @@ class AppTaskImpl extends IAppTask.Stub { } @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (RuntimeException e) { + throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e); + } + } + + @Override public void finishAndRemoveTask() { checkCaller(); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index ef0b7374f79e..66c625e13f78 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -47,12 +47,6 @@ import com.android.server.LocalServices; class BackNavigationController { private static final String TAG = "BackNavigationController"; - // By default, enable new back dispatching without any animations. - private static final int BACK_PREDICTABILITY_PROP = - SystemProperties.getInt("persist.debug.back_predictability", 1); - private static final int ANIMATIONS_MASK = 1 << 1; - private static final int SCREENSHOT_MASK = 1 << 2; - @Nullable private TaskSnapshotController mTaskSnapshotController; @@ -60,15 +54,15 @@ class BackNavigationController { * Returns true if the back predictability feature is enabled */ static boolean isEnabled() { - return BACK_PREDICTABILITY_PROP > 0; + return SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0; } static boolean isScreenshotEnabled() { - return (BACK_PREDICTABILITY_PROP & SCREENSHOT_MASK) != 0; + return SystemProperties.getInt("persist.wm.debug.predictive_back_screenshot", 0) != 0; } private static boolean isAnimationEnabled() { - return (BACK_PREDICTABILITY_PROP & ANIMATIONS_MASK) != 0; + return SystemProperties.getInt("persist.wm.debug.predictive_back_anim", 0) != 0; } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 81560d4f8676..466075702409 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1992,7 +1992,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp scheduleAnimation(); forAllWindows(w -> { - if (w.mHasSurface && !rotateSeamlessly) { + if (!w.mHasSurface) return; + if (!rotateSeamlessly) { ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w); w.setOrientationChanging(true); } @@ -4047,12 +4048,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @VisibleForTesting void setImeInputTarget(InputTarget target) { mImeInputTarget = target; - boolean canScreenshot = mImeInputTarget == null || mImeInputTarget.canScreenshotIme(); - if (mImeWindowsContainer.setCanScreenshot(canScreenshot)) { + if (refreshImeSecureFlag(getPendingTransaction())) { mWmService.requestTraversal(); } } + /** + * Re-check the IME target's SECURE flag since it's possible to have changed after the target + * was set. + */ + boolean refreshImeSecureFlag(Transaction t) { + boolean canScreenshot = mImeInputTarget == null || mImeInputTarget.canScreenshotIme(); + return mImeWindowsContainer.setCanScreenshot(t, canScreenshot); + } + @VisibleForTesting void setImeControlTarget(InsetsControlTarget target) { mImeControlTarget = target; diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index 8d1425d17d47..67dd89ee295c 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -29,6 +29,7 @@ import android.graphics.PointF; import android.os.Debug; import android.os.IBinder; import android.util.Slog; +import android.view.Display; import android.view.InputApplicationHandle; import android.view.KeyEvent; import android.view.SurfaceControl; @@ -194,6 +195,9 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal int firstExternalDisplayId = DEFAULT_DISPLAY; for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) { final DisplayContent displayContent = mService.mRoot.mChildren.get(i); + if (displayContent.getDisplayInfo().state == Display.STATE_OFF) { + continue; + } // Heuristic solution here. Currently when "Freeform windows" developer option is // enabled we automatically put secondary displays in freeform mode and emulating // "desktop mode". It also makes sense to show the pointer on the same display. diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 00f7e6362be9..65062e576d55 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -249,6 +249,8 @@ final class InputMonitor { inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused); inputWindowHandle.setWindowToken(w.mClient); + inputWindowHandle.setName(w.getName()); + // Update layout params flags to force the window to be not touch modal. We do this to // restrict the window's touchable region to the task even if it requests touches outside // its window bounds. An example is a dialog in primary split should get touches outside its diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 0038c716fee7..c162e8ecadfc 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -175,7 +175,7 @@ final class LetterboxUiController { final Rect spaceToFill = transformedBounds != null ? transformedBounds : mActivityRecord.inMultiWindowMode() - ? mActivityRecord.getRootTask().getBounds() + ? mActivityRecord.getTask().getBounds() : mActivityRecord.getRootTask().getParent().getBounds(); mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint); } else if (mLetterbox != null) { diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java index 65dca86d0259..83be73a47eb4 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java @@ -176,13 +176,24 @@ class SurfaceAnimationRunner { t.addTransactionCommittedListener(Runnable::run, () -> { final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec(); + final Transaction edgeExtensionCreationTransaction = new Transaction(); edgeExtendWindow(animationLeash, animationSpec.getRootTaskBounds(), animationSpec.getAnimation(), - mFrameTransaction); + edgeExtensionCreationTransaction); synchronized (mLock) { // only run if animation is not yet canceled by this point if (mPreProcessingAnimations.get(animationLeash) == runningAnim) { + // In the case the animation is cancelled, edge extensions are removed + // onAnimationLeashLost which is called before onAnimationCancelled. + // So we need to check if the edge extensions have already been removed + // or not, and if so we don't want to apply the transaction. + synchronized (mEdgeExtensionLock) { + if (!mEdgeExtensions.isEmpty()) { + edgeExtensionCreationTransaction.apply(); + } + } + mPreProcessingAnimations.remove(animationLeash); mPendingAnimations.put(animationLeash, runningAnim); if (!mAnimationStartDeferred) { diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 3a458fdc75bb..900963e29bd6 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -2262,7 +2262,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { mFragmentToken, mRemoteToken.toWindowContainerToken(), getConfiguration(), - getChildCount() == 0, + runningActivityCount[0] == 0, runningActivityCount[0], isVisible(), childActivities, diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 214524c2f42c..2900a5df6198 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3782,11 +3782,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return INVALID_WINDOW_TYPE; } - boolean setCanScreenshot(boolean canScreenshot) { + boolean setCanScreenshot(Transaction t, boolean canScreenshot) { if (mSurfaceControl == null) { return false; } - getPendingTransaction().setSecure(mSurfaceControl, !canScreenshot); + t.setSecure(mSurfaceControl, !canScreenshot); return true; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 8546e8002602..237982220a18 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2606,11 +2606,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } + // Remove immediately if there is display transition because the animation is + // usually unnoticeable (e.g. covered by rotation animation) and the animation + // bounds could be inconsistent, such as depending on when the window applies + // its draw transaction with new rotation. + final boolean allowExitAnimation = !getDisplayContent().inTransition(); + if (wasVisible) { final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE; // Try starting an animation. - if (mWinAnimator.applyAnimationLocked(transit, false)) { + if (allowExitAnimation && mWinAnimator.applyAnimationLocked(transit, false)) { ProtoLog.v(WM_DEBUG_ANIM, "Set animatingExit: reason=remove/applyAnimation win=%s", this); mAnimatingExit = true; @@ -2624,7 +2630,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWmService.mAccessibilityController.onWindowTransition(this, transit); } } - final boolean isAnimating = mAnimatingExit || isExitAnimationRunningSelfOrParent(); + final boolean isAnimating = allowExitAnimation + && (mAnimatingExit || isExitAnimationRunningSelfOrParent()); final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null && mActivityRecord.isLastWindow(this); // We delay the removal of a window if it has a showing surface that can be used to run @@ -3602,6 +3609,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAnimatingExit = false; ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=destroySurface win=%s", this); + // Clear the flag so the buffer requested for the next new surface won't be dropped by + // mistaking the surface size needs to update. + mReportOrientationChanged = false; + if (useBLASTSync()) { immediatelyNotifyBlastSync(); } @@ -5284,12 +5295,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mControllableInsetProvider != null) { return; } - if (getDisplayContent().inTransition()) { - // Skip because the animation is usually unnoticeable (e.g. covered by rotation - // animation) and the animation bounds could be inconsistent, such as depending - // on when the window applies its draw transaction with new rotation. - return; - } final DisplayInfo displayInfo = getDisplayInfo(); anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(), @@ -6125,14 +6130,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP applyHere = true; } - for (int i = mDrawHandlers.size() - 1; i >= 0; i--) { - DrawHandler h = mDrawHandlers.get(i); + final List<DrawHandler> handlersToRemove = new ArrayList<>(); + // Iterate forwards to ensure we process in the same order + // we added. + for (int i = 0; i < mDrawHandlers.size(); i++) { + final DrawHandler h = mDrawHandlers.get(i); if (h.mSeqId <= seqId) { h.mConsumer.accept(t); - mDrawHandlers.remove(h); + handlersToRemove.add(h); hadHandlers = true; } } + for (int i = 0; i < handlersToRemove.size(); i++) { + final DrawHandler h = handlersToRemove.get(i); + mDrawHandlers.remove(h); + } if (hadHandlers) { mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this); diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index c2414e72abbd..5f43800bd9d5 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -217,6 +217,11 @@ class WindowSurfaceController { mService.openSurfaceTransaction(); try { getGlobalTransaction().setSecure(mSurfaceControl, isSecure); + + final DisplayContent dc = mAnimator.mWin.mDisplayContent; + if (dc != null) { + dc.refreshImeSecureFlag(getGlobalTransaction()); + } } finally { mService.closeSurfaceTransaction("setSecure"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked"); diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 17016a6eb314..fa5e450c687a 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -130,6 +130,11 @@ static struct { static struct { jclass clazz; + jfieldID mPtr; +} gNativeInputManagerServiceImpl; + +static struct { + jclass clazz; } gInputDeviceClassInfo; static struct { @@ -259,17 +264,16 @@ public: void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray); - base::Result<std::unique_ptr<InputChannel>> createInputChannel(JNIEnv* env, - const std::string& name); - base::Result<std::unique_ptr<InputChannel>> createInputMonitor(JNIEnv* env, int32_t displayId, + base::Result<std::unique_ptr<InputChannel>> createInputChannel(const std::string& name); + base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId, const std::string& name, int32_t pid); - status_t removeInputChannel(JNIEnv* env, const sp<IBinder>& connectionToken); + status_t removeInputChannel(const sp<IBinder>& connectionToken); status_t pilferPointers(const sp<IBinder>& token); void displayRemoved(JNIEnv* env, int32_t displayId); void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj); - void setFocusedDisplay(JNIEnv* env, int32_t displayId); + void setFocusedDisplay(int32_t displayId); void setInputDispatchMode(bool enabled, bool frozen); void setSystemUiLightsOut(bool lightsOut); void setPointerSpeed(int32_t speed); @@ -510,19 +514,18 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO } base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel( - JNIEnv* /* env */, const std::string& name) { + const std::string& name) { ATRACE_CALL(); return mInputManager->getDispatcher().createInputChannel(name); } base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputMonitor( - JNIEnv* /* env */, int32_t displayId, const std::string& name, int32_t pid) { + int32_t displayId, const std::string& name, int32_t pid) { ATRACE_CALL(); return mInputManager->getDispatcher().createInputMonitor(displayId, name, pid); } -status_t NativeInputManager::removeInputChannel(JNIEnv* /* env */, - const sp<IBinder>& connectionToken) { +status_t NativeInputManager::removeInputChannel(const sp<IBinder>& connectionToken) { ATRACE_CALL(); return mInputManager->getDispatcher().removeInputChannel(connectionToken); } @@ -1000,7 +1003,7 @@ void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId, mInputManager->getDispatcher().setFocusedApplication(displayId, applicationHandle); } -void NativeInputManager::setFocusedDisplay(JNIEnv* env, int32_t displayId) { +void NativeInputManager::setFocusedDisplay(int32_t displayId) { mInputManager->getDispatcher().setFocusedDisplay(displayId); } @@ -1490,6 +1493,11 @@ void NativeInputManager::notifyPointerDisplayIdChanged() { // ---------------------------------------------------------------------------- +static NativeInputManager* getNativeInputManager(JNIEnv* env, jobject clazz) { + return reinterpret_cast<NativeInputManager*>( + env->GetLongField(clazz, gNativeInputManagerServiceImpl.mPtr)); +} + static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); @@ -1504,8 +1512,8 @@ static jlong nativeInit(JNIEnv* env, jclass /* clazz */, return reinterpret_cast<jlong>(im); } -static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeStart(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); status_t result = im->getInputManager()->start(); if (result) { @@ -1513,39 +1521,39 @@ static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { } } -static void nativeSetDisplayViewports(JNIEnv* env, jclass /* clazz */, jlong ptr, - jobjectArray viewportObjArray) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetDisplayViewports(JNIEnv* env, jobject nativeImplObj, + jobjectArray viewportObjArray) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setDisplayViewports(env, viewportObjArray); } -static jint nativeGetScanCodeState(JNIEnv* /* env */, jclass /* clazz */, - jlong ptr, jint deviceId, jint sourceMask, jint scanCode) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jint nativeGetScanCodeState(JNIEnv* env, jobject nativeImplObj, jint deviceId, + jint sourceMask, jint scanCode) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); return (jint)im->getInputManager()->getReader().getScanCodeState(deviceId, uint32_t(sourceMask), scanCode); } -static jint nativeGetKeyCodeState(JNIEnv* /* env */, jclass /* clazz */, - jlong ptr, jint deviceId, jint sourceMask, jint keyCode) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jint nativeGetKeyCodeState(JNIEnv* env, jobject nativeImplObj, jint deviceId, + jint sourceMask, jint keyCode) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); return (jint)im->getInputManager()->getReader().getKeyCodeState(deviceId, uint32_t(sourceMask), keyCode); } -static jint nativeGetSwitchState(JNIEnv* /* env */, jclass /* clazz */, - jlong ptr, jint deviceId, jint sourceMask, jint sw) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jint nativeGetSwitchState(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint sourceMask, + jint sw) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); return (jint)im->getInputManager()->getReader().getSwitchState(deviceId, uint32_t(sourceMask), sw); } -static jboolean nativeHasKeys(JNIEnv* env, jclass /* clazz */, - jlong ptr, jint deviceId, jint sourceMask, jintArray keyCodes, jbooleanArray outFlags) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jboolean nativeHasKeys(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint sourceMask, + jintArray keyCodes, jbooleanArray outFlags) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); int32_t* codes = env->GetIntArrayElements(keyCodes, nullptr); uint8_t* flags = env->GetBooleanArrayElements(outFlags, nullptr); @@ -1567,9 +1575,9 @@ static jboolean nativeHasKeys(JNIEnv* env, jclass /* clazz */, return result; } -static jint nativeGetKeyCodeForKeyLocation(JNIEnv* env, jclass /* clazz */, jlong ptr, - jint deviceId, jint locationKeyCode) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jint nativeGetKeyCodeForKeyLocation(JNIEnv* env, jobject nativeImplObj, jint deviceId, + jint locationKeyCode) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); return (jint)im->getInputManager()->getReader().getKeyCodeForKeyLocation(deviceId, locationKeyCode); } @@ -1582,17 +1590,16 @@ static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj * ALOGW("Input channel object '%s' was disposed without first being removed with " "the input manager!", inputChannel->getName().c_str()); - im->removeInputChannel(env, inputChannel->getConnectionToken()); + im->removeInputChannel(inputChannel->getConnectionToken()); } -static jobject nativeCreateInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr, - jstring nameObj) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jobject nativeCreateInputChannel(JNIEnv* env, jobject nativeImplObj, jstring nameObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); ScopedUtfChars nameChars(env, nameObj); std::string name = nameChars.c_str(); - base::Result<std::unique_ptr<InputChannel>> inputChannel = im->createInputChannel(env, name); + base::Result<std::unique_ptr<InputChannel>> inputChannel = im->createInputChannel(name); if (!inputChannel.ok()) { std::string message = inputChannel.error().message(); @@ -1612,9 +1619,9 @@ static jobject nativeCreateInputChannel(JNIEnv* env, jclass /* clazz */, jlong p return inputChannelObj; } -static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId, +static jobject nativeCreateInputMonitor(JNIEnv* env, jobject nativeImplObj, jint displayId, jstring nameObj, jint pid) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); if (displayId == ADISPLAY_ID_NONE) { std::string message = "InputChannel used as a monitor must be associated with a display"; @@ -1626,7 +1633,7 @@ static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong p std::string name = nameChars.c_str(); base::Result<std::unique_ptr<InputChannel>> inputChannel = - im->createInputMonitor(env, displayId, name, pid); + im->createInputMonitor(displayId, name, pid); if (!inputChannel.ok()) { std::string message = inputChannel.error().message(); @@ -1643,11 +1650,11 @@ static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong p return inputChannelObj; } -static void nativeRemoveInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr, jobject tokenObj) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeRemoveInputChannel(JNIEnv* env, jobject nativeImplObj, jobject tokenObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); sp<IBinder> token = ibinderForJavaObject(env, tokenObj); - status_t status = im->removeInputChannel(env, token); + status_t status = im->removeInputChannel(token); if (status && status != BAD_VALUE) { // ignore already removed channel std::string message; message += StringPrintf("Failed to remove input channel. status=%d", status); @@ -1655,47 +1662,44 @@ static void nativeRemoveInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr, } } -static void nativePilferPointers(JNIEnv* env, jclass /* clazz */, jlong ptr, jobject tokenObj) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativePilferPointers(JNIEnv* env, jobject nativeImplObj, jobject tokenObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); sp<IBinder> token = ibinderForJavaObject(env, tokenObj); im->pilferPointers(token); } -static void nativeSetInputFilterEnabled(JNIEnv* /* env */, jclass /* clazz */, - jlong ptr, jboolean enabled) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetInputFilterEnabled(JNIEnv* env, jobject nativeImplObj, jboolean enabled) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getDispatcher().setInputFilterEnabled(enabled); } -static jboolean nativeSetInTouchMode(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, - jboolean inTouchMode, jint pid, jint uid, - jboolean hasPermission) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jboolean nativeSetInTouchMode(JNIEnv* env, jobject nativeImplObj, jboolean inTouchMode, + jint pid, jint uid, jboolean hasPermission) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); return im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode, pid, uid, hasPermission); } -static void nativeSetMaximumObscuringOpacityForTouch(JNIEnv* /* env */, jclass /* clazz */, - jlong ptr, jfloat opacity) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetMaximumObscuringOpacityForTouch(JNIEnv* env, jobject nativeImplObj, + jfloat opacity) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getDispatcher().setMaximumObscuringOpacityForTouch(opacity); } -static void nativeSetBlockUntrustedTouchesMode(JNIEnv* env, jclass /* clazz */, jlong ptr, - jint mode) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetBlockUntrustedTouchesMode(JNIEnv* env, jobject nativeImplObj, jint mode) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getDispatcher().setBlockUntrustedTouchesMode( static_cast<BlockUntrustedTouchesMode>(mode)); } -static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */, jlong ptr, - jobject inputEventObj, jboolean injectIntoUid, jint uid, - jint syncMode, jint timeoutMillis, jint policyFlags) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jint nativeInjectInputEvent(JNIEnv* env, jobject nativeImplObj, jobject inputEventObj, + jboolean injectIntoUid, jint uid, jint syncMode, + jint timeoutMillis, jint policyFlags) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); const std::optional<int32_t> targetUid = injectIntoUid ? std::make_optional(uid) : std::nullopt; // static_cast is safe because the value was already checked at the Java layer @@ -1735,9 +1739,8 @@ static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */, jlong ptr, } } -static jobject nativeVerifyInputEvent(JNIEnv* env, jclass /* clazz */, jlong ptr, - jobject inputEventObj) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jobject nativeVerifyInputEvent(JNIEnv* env, jobject nativeImplObj, jobject inputEventObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) { KeyEvent keyEvent; @@ -1778,56 +1781,53 @@ static jobject nativeVerifyInputEvent(JNIEnv* env, jclass /* clazz */, jlong ptr } } -static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */, - jlong ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeToggleCapsLock(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().toggleCapsLockState(deviceId); } -static void nativeDisplayRemoved(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeDisplayRemoved(JNIEnv* env, jobject nativeImplObj, jint displayId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->displayRemoved(env, displayId); } -static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */, - jlong ptr, jint displayId, jobject applicationHandleObj) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetFocusedApplication(JNIEnv* env, jobject nativeImplObj, jint displayId, + jobject applicationHandleObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setFocusedApplication(env, displayId, applicationHandleObj); } -static void nativeSetFocusedDisplay(JNIEnv* env, jclass /* clazz */, - jlong ptr, jint displayId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetFocusedDisplay(JNIEnv* env, jobject nativeImplObj, jint displayId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); - im->setFocusedDisplay(env, displayId); + im->setFocusedDisplay(displayId); } -static void nativeRequestPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr, - jobject tokenObj, jboolean enabled) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeRequestPointerCapture(JNIEnv* env, jobject nativeImplObj, jobject tokenObj, + jboolean enabled) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); sp<IBinder> windowToken = ibinderForJavaObject(env, tokenObj); im->requestPointerCapture(windowToken, enabled); } -static void nativeSetInputDispatchMode(JNIEnv* /* env */, - jclass /* clazz */, jlong ptr, jboolean enabled, jboolean frozen) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetInputDispatchMode(JNIEnv* env, jobject nativeImplObj, jboolean enabled, + jboolean frozen) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setInputDispatchMode(enabled, frozen); } -static void nativeSetSystemUiLightsOut(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, - jboolean lightsOut) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetSystemUiLightsOut(JNIEnv* env, jobject nativeImplObj, jboolean lightsOut) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setSystemUiLightsOut(lightsOut); } -static jboolean nativeTransferTouchFocus(JNIEnv* env, jclass /* clazz */, jlong ptr, +static jboolean nativeTransferTouchFocus(JNIEnv* env, jobject nativeImplObj, jobject fromChannelTokenObj, jobject toChannelTokenObj, jboolean isDragDrop) { if (fromChannelTokenObj == nullptr || toChannelTokenObj == nullptr) { @@ -1837,7 +1837,7 @@ static jboolean nativeTransferTouchFocus(JNIEnv* env, jclass /* clazz */, jlong sp<IBinder> fromChannelToken = ibinderForJavaObject(env, fromChannelTokenObj); sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj); - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); if (im->getInputManager()->getDispatcher().transferTouchFocus(fromChannelToken, toChannelToken, isDragDrop)) { return JNI_TRUE; @@ -1846,11 +1846,11 @@ static jboolean nativeTransferTouchFocus(JNIEnv* env, jclass /* clazz */, jlong } } -static jboolean nativeTransferTouch(JNIEnv* env, jclass /* clazz */, jlong ptr, +static jboolean nativeTransferTouch(JNIEnv* env, jobject nativeImplObj, jobject destChannelTokenObj) { sp<IBinder> destChannelToken = ibinderForJavaObject(env, destChannelTokenObj); - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); if (im->getInputManager()->getDispatcher().transferTouch(destChannelToken)) { return JNI_TRUE; } else { @@ -1858,42 +1858,39 @@ static jboolean nativeTransferTouch(JNIEnv* env, jclass /* clazz */, jlong ptr, } } -static void nativeSetPointerSpeed(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint speed) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetPointerSpeed(JNIEnv* env, jobject nativeImplObj, jint speed) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setPointerSpeed(speed); } -static void nativeSetPointerAcceleration(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, - jfloat acceleration) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetPointerAcceleration(JNIEnv* env, jobject nativeImplObj, jfloat acceleration) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setPointerAcceleration(acceleration); } -static void nativeSetShowTouches(JNIEnv* /* env */, - jclass /* clazz */, jlong ptr, jboolean enabled) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetShowTouches(JNIEnv* env, jobject nativeImplObj, jboolean enabled) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setShowTouches(enabled); } -static void nativeSetInteractive(JNIEnv* env, - jclass clazz, jlong ptr, jboolean interactive) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetInteractive(JNIEnv* env, jobject nativeImplObj, jboolean interactive) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setInteractive(interactive); } -static void nativeReloadCalibration(JNIEnv* env, jclass clazz, jlong ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeReloadCalibration(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->reloadCalibration(); } -static void nativeVibrate(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, - jlongArray patternObj, jintArray amplitudesObj, jint repeat, jint token) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeVibrate(JNIEnv* env, jobject nativeImplObj, jint deviceId, jlongArray patternObj, + jintArray amplitudesObj, jint repeat, jint token) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); size_t patternSize = env->GetArrayLength(patternObj); if (patternSize > MAX_VIBRATE_PATTERN_SIZE) { @@ -1926,10 +1923,10 @@ static void nativeVibrate(JNIEnv* env, jclass /* clazz */, jlong ptr, jint devic im->getInputManager()->getReader().vibrate(deviceId, sequence, repeat, token); } -static void nativeVibrateCombined(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, +static void nativeVibrateCombined(JNIEnv* env, jobject nativeImplObj, jint deviceId, jlongArray patternObj, jobject amplitudesObj, jint repeat, jint token) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); size_t patternSize = env->GetArrayLength(patternObj); @@ -1976,21 +1973,20 @@ static void nativeVibrateCombined(JNIEnv* env, jclass /* clazz */, jlong ptr, ji im->getInputManager()->getReader().vibrate(deviceId, sequence, repeat, token); } -static void nativeCancelVibrate(JNIEnv* /* env */, - jclass /* clazz */, jlong ptr, jint deviceId, jint token) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeCancelVibrate(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint token) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().cancelVibrate(deviceId, token); } -static bool nativeIsVibrating(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static bool nativeIsVibrating(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); return im->getInputManager()->getReader().isVibrating(deviceId); } -static jintArray nativeGetVibratorIds(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jintArray nativeGetVibratorIds(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); std::vector<int32_t> vibrators = im->getInputManager()->getReader().getVibratorIds(deviceId); jintArray vibIdArray = env->NewIntArray(vibrators.size()); @@ -2000,8 +1996,8 @@ static jintArray nativeGetVibratorIds(JNIEnv* env, jclass clazz, jlong ptr, jint return vibIdArray; } -static jobject nativeGetLights(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jobject nativeGetLights(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); jobject jLights = env->NewObject(gArrayListClassInfo.clazz, gArrayListClassInfo.constructor); std::vector<InputDeviceLightInfo> lights = @@ -2045,9 +2041,9 @@ static jobject nativeGetLights(JNIEnv* env, jclass clazz, jlong ptr, jint device return jLights; } -static jint nativeGetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, +static jint nativeGetLightPlayerId(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint lightId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); std::optional<int32_t> ret = im->getInputManager()->getReader().getLightPlayerId(deviceId, lightId); @@ -2055,54 +2051,51 @@ static jint nativeGetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, j return static_cast<jint>(ret.value_or(0)); } -static jint nativeGetLightColor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, - jint lightId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jint nativeGetLightColor(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint lightId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); std::optional<int32_t> ret = im->getInputManager()->getReader().getLightColor(deviceId, lightId); return static_cast<jint>(ret.value_or(0)); } -static void nativeSetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, - jint lightId, jint playerId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetLightPlayerId(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint lightId, + jint playerId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().setLightPlayerId(deviceId, lightId, playerId); } -static void nativeSetLightColor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, - jint lightId, jint color) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetLightColor(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint lightId, + jint color) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().setLightColor(deviceId, lightId, color); } -static jint nativeGetBatteryCapacity(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jint nativeGetBatteryCapacity(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); std::optional<int32_t> ret = im->getInputManager()->getReader().getBatteryCapacity(deviceId); return static_cast<jint>(ret.value_or(android::os::IInputConstants::INVALID_BATTERY_CAPACITY)); } -static jint nativeGetBatteryStatus(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jint nativeGetBatteryStatus(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); std::optional<int32_t> ret = im->getInputManager()->getReader().getBatteryStatus(deviceId); return static_cast<jint>(ret.value_or(BATTERY_STATUS_UNKNOWN)); } -static void nativeReloadKeyboardLayouts(JNIEnv* /* env */, - jclass /* clazz */, jlong ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeReloadKeyboardLayouts(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().requestRefreshConfiguration( InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS); } -static void nativeReloadDeviceAliases(JNIEnv* /* env */, - jclass /* clazz */, jlong ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeReloadDeviceAliases(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().requestRefreshConfiguration( InputReaderConfiguration::CHANGE_DEVICE_ALIAS); @@ -2117,58 +2110,54 @@ static std::string dumpInputProperties() { return out; } -static jstring nativeDump(JNIEnv* env, jclass /* clazz */, jlong ptr) { +static jstring nativeDump(JNIEnv* env, jobject nativeImplObj) { std::string dump = dumpInputProperties(); - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->dump(dump); return env->NewStringUTF(dump.c_str()); } -static void nativeMonitor(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeMonitor(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().monitor(); im->getInputManager()->getDispatcher().monitor(); } -static jboolean nativeIsInputDeviceEnabled(JNIEnv* env /* env */, - jclass /* clazz */, jlong ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jboolean nativeIsInputDeviceEnabled(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); return im->getInputManager()->getReader().isInputDeviceEnabled(deviceId); } -static void nativeEnableInputDevice(JNIEnv* /* env */, - jclass /* clazz */, jlong ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeEnableInputDevice(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setInputDeviceEnabled(deviceId, true); } -static void nativeDisableInputDevice(JNIEnv* /* env */, - jclass /* clazz */, jlong ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeDisableInputDevice(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setInputDeviceEnabled(deviceId, false); } -static void nativeSetPointerIconType(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint iconId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetPointerIconType(JNIEnv* env, jobject nativeImplObj, jint iconId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setPointerIconType(iconId); } -static void nativeReloadPointerIcons(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeReloadPointerIcons(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->reloadPointerIcons(); } -static void nativeSetCustomPointerIcon(JNIEnv* env, jclass /* clazz */, - jlong ptr, jobject iconObj) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetCustomPointerIcon(JNIEnv* env, jobject nativeImplObj, jobject iconObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); PointerIcon pointerIcon; status_t result = android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon); @@ -2182,40 +2171,38 @@ static void nativeSetCustomPointerIcon(JNIEnv* env, jclass /* clazz */, im->setCustomPointerIcon(spriteIcon); } -static jboolean nativeCanDispatchToDisplay(JNIEnv* env, jclass /* clazz */, jlong ptr, - jint deviceId, jint displayId) { - - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jboolean nativeCanDispatchToDisplay(JNIEnv* env, jobject nativeImplObj, jint deviceId, + jint displayId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); return im->getInputManager()->getReader().canDispatchToDisplay(deviceId, displayId); } -static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jclass /* clazz */, jlong ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().requestRefreshConfiguration( InputReaderConfiguration::CHANGE_DISPLAY_INFO); } -static void nativeNotifyPointerDisplayIdChanged(JNIEnv* env, jclass /* clazz */, jlong ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeNotifyPointerDisplayIdChanged(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->notifyPointerDisplayIdChanged(); } -static void nativeSetDisplayEligibilityForPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr, +static void nativeSetDisplayEligibilityForPointerCapture(JNIEnv* env, jobject nativeImplObj, jint displayId, jboolean isEligible) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getDispatcher().setDisplayEligibilityForPointerCapture(displayId, isEligible); } -static void nativeChangeUniqueIdAssociation(JNIEnv* env, jclass /* clazz */, jlong ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeChangeUniqueIdAssociation(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().requestRefreshConfiguration( InputReaderConfiguration::CHANGE_DISPLAY_INFO); } -static void nativeSetMotionClassifierEnabled(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, - jboolean enabled) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeSetMotionClassifierEnabled(JNIEnv* env, jobject nativeImplObj, jboolean enabled) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->setMotionClassifierEnabled(enabled); } @@ -2251,8 +2238,8 @@ static jobject createInputSensorInfo(JNIEnv* env, jstring name, jstring vendor, return sensorInfo; } -static jobjectArray nativeGetSensorList(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static jobjectArray nativeGetSensorList(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); std::vector<InputDeviceSensorInfo> sensors = im->getInputManager()->getReader().getSensors(deviceId); @@ -2281,10 +2268,10 @@ static jobjectArray nativeGetSensorList(JNIEnv* env, jclass /* clazz */, jlong p return arr; } -static jboolean nativeEnableSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, +static jboolean nativeEnableSensor(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint sensorType, jint samplingPeriodUs, jint maxBatchReportLatencyUs) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); return im->getInputManager() ->getReader() @@ -2293,18 +2280,18 @@ static jboolean nativeEnableSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, j std::chrono::microseconds(maxBatchReportLatencyUs)); } -static void nativeDisableSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, +static void nativeDisableSensor(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint sensorType) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().disableSensor(deviceId, static_cast<InputDeviceSensorType>( sensorType)); } -static jboolean nativeFlushSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, +static jboolean nativeFlushSensor(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint sensorType) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getReader().flushSensor(deviceId, static_cast<InputDeviceSensorType>(sensorType)); @@ -2313,8 +2300,8 @@ static jboolean nativeFlushSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, ji sensorType)); } -static void nativeCancelCurrentTouch(JNIEnv* env, jclass /* clazz */, jlong ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); +static void nativeCancelCurrentTouch(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getDispatcher().cancelCurrentTouch(); } @@ -2322,87 +2309,84 @@ static void nativeCancelCurrentTouch(JNIEnv* env, jclass /* clazz */, jlong ptr) static const JNINativeMethod gInputManagerMethods[] = { /* name, signature, funcPtr */ - {"nativeInit", + {"init", "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/" "MessageQueue;)J", (void*)nativeInit}, - {"nativeStart", "(J)V", (void*)nativeStart}, - {"nativeSetDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V", + {"start", "()V", (void*)nativeStart}, + {"setDisplayViewports", "([Landroid/hardware/display/DisplayViewport;)V", (void*)nativeSetDisplayViewports}, - {"nativeGetScanCodeState", "(JIII)I", (void*)nativeGetScanCodeState}, - {"nativeGetKeyCodeState", "(JIII)I", (void*)nativeGetKeyCodeState}, - {"nativeGetSwitchState", "(JIII)I", (void*)nativeGetSwitchState}, - {"nativeHasKeys", "(JII[I[Z)Z", (void*)nativeHasKeys}, - {"nativeGetKeyCodeForKeyLocation", "(JII)I", (void*)nativeGetKeyCodeForKeyLocation}, - {"nativeCreateInputChannel", "(JLjava/lang/String;)Landroid/view/InputChannel;", + {"getScanCodeState", "(III)I", (void*)nativeGetScanCodeState}, + {"getKeyCodeState", "(III)I", (void*)nativeGetKeyCodeState}, + {"getSwitchState", "(III)I", (void*)nativeGetSwitchState}, + {"hasKeys", "(II[I[Z)Z", (void*)nativeHasKeys}, + {"getKeyCodeForKeyLocation", "(II)I", (void*)nativeGetKeyCodeForKeyLocation}, + {"createInputChannel", "(Ljava/lang/String;)Landroid/view/InputChannel;", (void*)nativeCreateInputChannel}, - {"nativeCreateInputMonitor", "(JILjava/lang/String;I)Landroid/view/InputChannel;", + {"createInputMonitor", "(ILjava/lang/String;I)Landroid/view/InputChannel;", (void*)nativeCreateInputMonitor}, - {"nativeRemoveInputChannel", "(JLandroid/os/IBinder;)V", (void*)nativeRemoveInputChannel}, - {"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers}, - {"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled}, - {"nativeSetInTouchMode", "(JZIIZ)Z", (void*)nativeSetInTouchMode}, - {"nativeSetMaximumObscuringOpacityForTouch", "(JF)V", + {"removeInputChannel", "(Landroid/os/IBinder;)V", (void*)nativeRemoveInputChannel}, + {"pilferPointers", "(Landroid/os/IBinder;)V", (void*)nativePilferPointers}, + {"setInputFilterEnabled", "(Z)V", (void*)nativeSetInputFilterEnabled}, + {"setInTouchMode", "(ZIIZ)Z", (void*)nativeSetInTouchMode}, + {"setMaximumObscuringOpacityForTouch", "(F)V", (void*)nativeSetMaximumObscuringOpacityForTouch}, - {"nativeSetBlockUntrustedTouchesMode", "(JI)V", (void*)nativeSetBlockUntrustedTouchesMode}, - {"nativeInjectInputEvent", "(JLandroid/view/InputEvent;ZIIII)I", - (void*)nativeInjectInputEvent}, - {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", + {"setBlockUntrustedTouchesMode", "(I)V", (void*)nativeSetBlockUntrustedTouchesMode}, + {"injectInputEvent", "(Landroid/view/InputEvent;ZIIII)I", (void*)nativeInjectInputEvent}, + {"verifyInputEvent", "(Landroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", (void*)nativeVerifyInputEvent}, - {"nativeToggleCapsLock", "(JI)V", (void*)nativeToggleCapsLock}, - {"nativeDisplayRemoved", "(JI)V", (void*)nativeDisplayRemoved}, - {"nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V", + {"toggleCapsLock", "(I)V", (void*)nativeToggleCapsLock}, + {"displayRemoved", "(I)V", (void*)nativeDisplayRemoved}, + {"setFocusedApplication", "(ILandroid/view/InputApplicationHandle;)V", (void*)nativeSetFocusedApplication}, - {"nativeSetFocusedDisplay", "(JI)V", (void*)nativeSetFocusedDisplay}, - {"nativeRequestPointerCapture", "(JLandroid/os/IBinder;Z)V", - (void*)nativeRequestPointerCapture}, - {"nativeSetInputDispatchMode", "(JZZ)V", (void*)nativeSetInputDispatchMode}, - {"nativeSetSystemUiLightsOut", "(JZ)V", (void*)nativeSetSystemUiLightsOut}, - {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;Z)Z", + {"setFocusedDisplay", "(I)V", (void*)nativeSetFocusedDisplay}, + {"requestPointerCapture", "(Landroid/os/IBinder;Z)V", (void*)nativeRequestPointerCapture}, + {"setInputDispatchMode", "(ZZ)V", (void*)nativeSetInputDispatchMode}, + {"setSystemUiLightsOut", "(Z)V", (void*)nativeSetSystemUiLightsOut}, + {"transferTouchFocus", "(Landroid/os/IBinder;Landroid/os/IBinder;Z)Z", (void*)nativeTransferTouchFocus}, - {"nativeTransferTouch", "(JLandroid/os/IBinder;)Z", (void*)nativeTransferTouch}, - {"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed}, - {"nativeSetPointerAcceleration", "(JF)V", (void*)nativeSetPointerAcceleration}, - {"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches}, - {"nativeSetInteractive", "(JZ)V", (void*)nativeSetInteractive}, - {"nativeReloadCalibration", "(J)V", (void*)nativeReloadCalibration}, - {"nativeVibrate", "(JI[J[III)V", (void*)nativeVibrate}, - {"nativeVibrateCombined", "(JI[JLandroid/util/SparseArray;II)V", - (void*)nativeVibrateCombined}, - {"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate}, - {"nativeIsVibrating", "(JI)Z", (void*)nativeIsVibrating}, - {"nativeGetVibratorIds", "(JI)[I", (void*)nativeGetVibratorIds}, - {"nativeGetLights", "(JI)Ljava/util/List;", (void*)nativeGetLights}, - {"nativeGetLightPlayerId", "(JII)I", (void*)nativeGetLightPlayerId}, - {"nativeGetLightColor", "(JII)I", (void*)nativeGetLightColor}, - {"nativeSetLightPlayerId", "(JIII)V", (void*)nativeSetLightPlayerId}, - {"nativeSetLightColor", "(JIII)V", (void*)nativeSetLightColor}, - {"nativeGetBatteryCapacity", "(JI)I", (void*)nativeGetBatteryCapacity}, - {"nativeGetBatteryStatus", "(JI)I", (void*)nativeGetBatteryStatus}, - {"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts}, - {"nativeReloadDeviceAliases", "(J)V", (void*)nativeReloadDeviceAliases}, - {"nativeDump", "(J)Ljava/lang/String;", (void*)nativeDump}, - {"nativeMonitor", "(J)V", (void*)nativeMonitor}, - {"nativeIsInputDeviceEnabled", "(JI)Z", (void*)nativeIsInputDeviceEnabled}, - {"nativeEnableInputDevice", "(JI)V", (void*)nativeEnableInputDevice}, - {"nativeDisableInputDevice", "(JI)V", (void*)nativeDisableInputDevice}, - {"nativeSetPointerIconType", "(JI)V", (void*)nativeSetPointerIconType}, - {"nativeReloadPointerIcons", "(J)V", (void*)nativeReloadPointerIcons}, - {"nativeSetCustomPointerIcon", "(JLandroid/view/PointerIcon;)V", + {"transferTouch", "(Landroid/os/IBinder;)Z", (void*)nativeTransferTouch}, + {"setPointerSpeed", "(I)V", (void*)nativeSetPointerSpeed}, + {"setPointerAcceleration", "(F)V", (void*)nativeSetPointerAcceleration}, + {"setShowTouches", "(Z)V", (void*)nativeSetShowTouches}, + {"setInteractive", "(Z)V", (void*)nativeSetInteractive}, + {"reloadCalibration", "()V", (void*)nativeReloadCalibration}, + {"vibrate", "(I[J[III)V", (void*)nativeVibrate}, + {"vibrateCombined", "(I[JLandroid/util/SparseArray;II)V", (void*)nativeVibrateCombined}, + {"cancelVibrate", "(II)V", (void*)nativeCancelVibrate}, + {"isVibrating", "(I)Z", (void*)nativeIsVibrating}, + {"getVibratorIds", "(I)[I", (void*)nativeGetVibratorIds}, + {"getLights", "(I)Ljava/util/List;", (void*)nativeGetLights}, + {"getLightPlayerId", "(II)I", (void*)nativeGetLightPlayerId}, + {"getLightColor", "(II)I", (void*)nativeGetLightColor}, + {"setLightPlayerId", "(III)V", (void*)nativeSetLightPlayerId}, + {"setLightColor", "(III)V", (void*)nativeSetLightColor}, + {"getBatteryCapacity", "(I)I", (void*)nativeGetBatteryCapacity}, + {"getBatteryStatus", "(I)I", (void*)nativeGetBatteryStatus}, + {"reloadKeyboardLayouts", "()V", (void*)nativeReloadKeyboardLayouts}, + {"reloadDeviceAliases", "()V", (void*)nativeReloadDeviceAliases}, + {"dump", "()Ljava/lang/String;", (void*)nativeDump}, + {"monitor", "()V", (void*)nativeMonitor}, + {"isInputDeviceEnabled", "(I)Z", (void*)nativeIsInputDeviceEnabled}, + {"enableInputDevice", "(I)V", (void*)nativeEnableInputDevice}, + {"disableInputDevice", "(I)V", (void*)nativeDisableInputDevice}, + {"setPointerIconType", "(I)V", (void*)nativeSetPointerIconType}, + {"reloadPointerIcons", "()V", (void*)nativeReloadPointerIcons}, + {"setCustomPointerIcon", "(Landroid/view/PointerIcon;)V", (void*)nativeSetCustomPointerIcon}, - {"nativeCanDispatchToDisplay", "(JII)Z", (void*)nativeCanDispatchToDisplay}, - {"nativeNotifyPortAssociationsChanged", "(J)V", (void*)nativeNotifyPortAssociationsChanged}, - {"nativeChangeUniqueIdAssociation", "(J)V", (void*)nativeChangeUniqueIdAssociation}, - {"nativeNotifyPointerDisplayIdChanged", "(J)V", (void*)nativeNotifyPointerDisplayIdChanged}, - {"nativeSetDisplayEligibilityForPointerCapture", "(JIZ)V", + {"canDispatchToDisplay", "(II)Z", (void*)nativeCanDispatchToDisplay}, + {"notifyPortAssociationsChanged", "()V", (void*)nativeNotifyPortAssociationsChanged}, + {"changeUniqueIdAssociation", "()V", (void*)nativeChangeUniqueIdAssociation}, + {"notifyPointerDisplayIdChanged", "()V", (void*)nativeNotifyPointerDisplayIdChanged}, + {"setDisplayEligibilityForPointerCapture", "(IZ)V", (void*)nativeSetDisplayEligibilityForPointerCapture}, - {"nativeSetMotionClassifierEnabled", "(JZ)V", (void*)nativeSetMotionClassifierEnabled}, - {"nativeGetSensorList", "(JI)[Landroid/hardware/input/InputSensorInfo;", + {"setMotionClassifierEnabled", "(Z)V", (void*)nativeSetMotionClassifierEnabled}, + {"getSensorList", "(I)[Landroid/hardware/input/InputSensorInfo;", (void*)nativeGetSensorList}, - {"nativeEnableSensor", "(JIIII)Z", (void*)nativeEnableSensor}, - {"nativeDisableSensor", "(JII)V", (void*)nativeDisableSensor}, - {"nativeFlushSensor", "(JII)Z", (void*)nativeFlushSensor}, - {"nativeCancelCurrentTouch", "(J)V", (void*)nativeCancelCurrentTouch}, + {"enableSensor", "(IIII)Z", (void*)nativeEnableSensor}, + {"disableSensor", "(II)V", (void*)nativeDisableSensor}, + {"flushSensor", "(II)Z", (void*)nativeFlushSensor}, + {"cancelCurrentTouch", "()V", (void*)nativeCancelCurrentTouch}, }; #define FIND_CLASS(var, className) \ @@ -2422,11 +2406,21 @@ static const JNINativeMethod gInputManagerMethods[] = { LOG_FATAL_IF(! (var), "Unable to find field " fieldName); int register_android_server_InputManager(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, "com/android/server/input/InputManagerService", - gInputManagerMethods, NELEM(gInputManagerMethods)); - (void) res; // Faked use when LOG_NDEBUG. + int res = jniRegisterNativeMethods(env, + "com/android/server/input/" + "NativeInputManagerService$NativeImpl", + gInputManagerMethods, NELEM(gInputManagerMethods)); + (void)res; // Faked use when LOG_NDEBUG. LOG_FATAL_IF(res < 0, "Unable to register native methods."); + FIND_CLASS(gNativeInputManagerServiceImpl.clazz, + "com/android/server/input/" + "NativeInputManagerService$NativeImpl"); + gNativeInputManagerServiceImpl.clazz = + jclass(env->NewGlobalRef(gNativeInputManagerServiceImpl.clazz)); + gNativeInputManagerServiceImpl.mPtr = + env->GetFieldID(gNativeInputManagerServiceImpl.clazz, "mPtr", "J"); + // Callbacks jclass clazz; diff --git a/services/devicepolicy/TEST_MAPPING b/services/devicepolicy/TEST_MAPPING index 3d86cf30f38e..72bba11c5366 100644 --- a/services/devicepolicy/TEST_MAPPING +++ b/services/devicepolicy/TEST_MAPPING @@ -16,5 +16,15 @@ { "name": "CtsDevicePolicyManagerTestCases" } + ], + "presubmit": [ + { + "name": "CtsDevicePolicyManagerTestCases", + "options": [ + { + "include-filter": "com.android.cts.devicepolicy.ManagedProfileTest#testParentProfileApiDisabled" + } + ] + } ] } 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 e6fd9164d96b..d305fc5d7dc4 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -618,10 +618,17 @@ public class DataManager { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED); intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); - BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId); - mBroadcastReceivers.put(userId, broadcastReceiver); - mContext.registerReceiverAsUser( - broadcastReceiver, UserHandle.of(userId), intentFilter, null, null); + + if (mBroadcastReceivers.get(userId) == null) { + BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId); + mBroadcastReceivers.put(userId, broadcastReceiver); + mContext.registerReceiverAsUser( + broadcastReceiver, UserHandle.of(userId), intentFilter, null, null); + } else { + // Stopped was not called on this user before setup is called again. This + // could happen during consecutive rapid user switching. + if (DEBUG) Log.d(TAG, "PerUserBroadcastReceiver was registered for: " + userId); + } ContentObserver contactsContentObserver = new ContactsContentObserver( BackgroundThread.getHandler()); @@ -639,9 +646,15 @@ public class DataManager { // Should never occur for local calls. } - PackageMonitor packageMonitor = new PerUserPackageMonitor(); - packageMonitor.register(mContext, null, UserHandle.of(userId), true); - mPackageMonitors.put(userId, packageMonitor); + if (mPackageMonitors.get(userId) == null) { + PackageMonitor packageMonitor = new PerUserPackageMonitor(); + packageMonitor.register(mContext, null, UserHandle.of(userId), true); + mPackageMonitors.put(userId, packageMonitor); + } else { + // Stopped was not called on this user before setup is called again. This + // could happen during consecutive rapid user switching. + if (DEBUG) Log.d(TAG, "PerUserPackageMonitor was registered for: " + userId); + } if (userId == UserHandle.USER_SYSTEM) { // The call log and MMS/SMS messages are shared across user profiles. So only need diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt index 7017440a86bb..7b152247eb9c 100644 --- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt +++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt @@ -34,7 +34,6 @@ import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverr import com.android.server.testutils.TestHandler import com.android.server.testutils.mock import com.android.server.testutils.mockThrowOnUnmocked -import com.android.server.testutils.spy import com.android.server.testutils.whenever import com.android.server.wm.ActivityTaskManagerInternal import com.google.common.truth.Truth.assertThat @@ -46,13 +45,9 @@ import org.junit.runner.RunWith import org.junit.runners.Parameterized import org.mockito.Mockito.any import org.mockito.Mockito.anyInt -import org.mockito.Mockito.clearInvocations -import org.mockito.Mockito.doAnswer import org.mockito.Mockito.doReturn import org.mockito.Mockito.intThat -import org.mockito.Mockito.never import org.mockito.Mockito.same -import org.mockito.Mockito.verify import org.testng.Assert.assertThrows import java.io.File import java.util.UUID @@ -365,7 +360,7 @@ class PackageManagerComponentLabelIconOverrideTest { val mockActivityTaskManager: ActivityTaskManagerInternal = mockThrowOnUnmocked { whenever(this.isCallerRecents(anyInt())) { false } } - val mockAppsFilter: AppsFilter = mockThrowOnUnmocked { + val mockAppsFilter: AppsFilterImpl = mockThrowOnUnmocked { whenever(this.shouldFilterApplication(anyInt(), any<PackageSetting>(), any<PackageSetting>(), anyInt())) { false } whenever(this.snapshot()) { this@mockThrowOnUnmocked } diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index c9523ec16b8f..529def3697cd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -27,6 +27,7 @@ import static android.app.AlarmManager.FLAG_STANDALONE; import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE; import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC_WAKEUP; +import static android.app.AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT; import static android.app.AlarmManager.WINDOW_EXACT; import static android.app.AlarmManager.WINDOW_HEURISTIC; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -122,6 +123,7 @@ import android.app.IAlarmListener; import android.app.IAlarmManager; import android.app.PendingIntent; import android.app.compat.CompatChanges; +import android.app.role.RoleManager; import android.app.usage.UsageStatsManagerInternal; import android.content.ContentResolver; import android.content.Context; @@ -184,6 +186,7 @@ import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -232,6 +235,8 @@ public class AlarmManagerServiceTest { @Mock private PackageManagerInternal mPackageManagerInternal; @Mock + private RoleManager mRoleManager; + @Mock private AppStateTrackerImpl mAppStateTracker; @Mock private AlarmManagerService.ClockReceiver mClockReceiver; @@ -457,6 +462,7 @@ public class AlarmManagerServiceTest { when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); when(mMockContext.getSystemService(BatteryManager.class)).thenReturn(mBatteryManager); + when(mMockContext.getSystemService(RoleManager.class)).thenReturn(mRoleManager); registerAppIds(new String[]{TEST_CALLING_PACKAGE}, new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)}); @@ -3191,6 +3197,70 @@ public class AlarmManagerServiceTest { } @Test + public void isScheduleExactAlarmAllowedByDefault() { + final String package1 = "priv"; + final String package2 = "signed"; + final String package3 = "normal"; + final String package4 = "wellbeing"; + final int uid1 = 1294; + final int uid2 = 8321; + final int uid3 = 3412; + final int uid4 = 4591; + + when(mPackageManagerInternal.isUidPrivileged(uid1)).thenReturn(true); + when(mPackageManagerInternal.isUidPrivileged(uid2)).thenReturn(false); + when(mPackageManagerInternal.isUidPrivileged(uid3)).thenReturn(false); + when(mPackageManagerInternal.isUidPrivileged(uid4)).thenReturn(false); + + when(mPackageManagerInternal.isPlatformSigned(package1)).thenReturn(false); + when(mPackageManagerInternal.isPlatformSigned(package2)).thenReturn(true); + when(mPackageManagerInternal.isPlatformSigned(package3)).thenReturn(false); + when(mPackageManagerInternal.isPlatformSigned(package4)).thenReturn(false); + + when(mRoleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING)).thenReturn( + Arrays.asList(package4)); + + mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); + mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = false; + mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] { + package1, + package3, + }); + + // Deny listed packages will be false. + assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2)); + assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4)); + + mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, false); + mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = true; + mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] { + package1, + package3, + }); + + // Same as above, deny listed packages will be false. + assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2)); + assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4)); + + mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); + mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = true; + mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] { + package1, + package3, + }); + + // Deny list doesn't matter now, only exemptions should be true. + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2)); + assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4)); + } + + @Test public void alarmScheduledAtomPushed() { for (int i = 0; i < 10; i++) { final PendingIntent pi = getNewMockPendingIntent(); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 9a4f8e261124..43ba39adcb85 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -2164,6 +2164,49 @@ public class QuotaControllerTest { } } + @Test + public void testIsWithinEJQuotaLocked_TempAllowlisting_Restricted() { + setDischarging(); + JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TempAllowlisting_Restricted", 1); + setStandbyBucket(RESTRICTED_INDEX, js); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS); + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true); + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + final long gracePeriodMs = 15 * SECOND_IN_MILLIS; + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); + Handler handler = mQuotaController.getHandler(); + spyOn(handler); + + // The temp allowlist should not enable RESTRICTED apps' to schedule & start EJs if they're + // out of quota. + mTempAllowlistListener.onAppAdded(mSourceUid); + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mTempAllowlistListener.onAppRemoved(mSourceUid); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + // Still in grace period + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); + } + advanceElapsedClock(6 * SECOND_IN_MILLIS); + // Out of grace period. + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); + } + } + /** * Tests that Timers properly track sessions when an app becomes top and is closed. */ @@ -5559,6 +5602,111 @@ public class QuotaControllerTest { mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } + @Test + public void testEJTimerTracking_TempAllowlisting_Restricted() { + setDischarging(); + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + final long gracePeriodMs = 15 * SECOND_IN_MILLIS; + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); + Handler handler = mQuotaController.getHandler(); + spyOn(handler); + + JobStatus job = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting_Restricted", 1); + setStandbyBucket(RESTRICTED_INDEX, job); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + } + assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + List<TimingSession> expected = new ArrayList<>(); + + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(job); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job, job, true); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Job starts after app is added to temp allowlist and stops before removal. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mTempAllowlistListener.onAppAdded(mSourceUid); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + mQuotaController.prepareForExecutionLocked(job); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job, null, false); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Job starts after app is added to temp allowlist and stops after removal, + // before grace period ends. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mTempAllowlistListener.onAppAdded(mSourceUid); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + mQuotaController.prepareForExecutionLocked(job); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mTempAllowlistListener.onAppRemoved(mSourceUid); + long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS; + advanceElapsedClock(elapsedGracePeriodMs); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job, null, false); + } + expected.add(createTimingSession(start, 12 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + elapsedGracePeriodMs += SECOND_IN_MILLIS; + + // Job starts during grace period and ends after grace period ends + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + mQuotaController.prepareForExecutionLocked(job); + } + final long remainingGracePeriod = gracePeriodMs - elapsedGracePeriodMs; + advanceElapsedClock(remainingGracePeriod); + // Wait for handler to update Timer + // Can't directly evaluate the message because for some reason, the captured message returns + // the wrong 'what' even though the correct message goes to the handler and the correct + // path executes. + verify(handler, timeout(gracePeriodMs + 5 * SECOND_IN_MILLIS)).handleMessage(any()); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS + remainingGracePeriod, 1)); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job, job, true); + } + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Job starts and runs completely after temp allowlist grace period. + advanceElapsedClock(10 * SECOND_IN_MILLIS); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + mQuotaController.prepareForExecutionLocked(job); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job, job, true); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + /** * Tests that Timers properly track sessions when TOP state and temp allowlisting overlaps. */ diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 0567f58b1bbe..353c8e22cceb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -194,7 +194,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { val packageParser: PackageParser2 = mock() val keySetManagerService: KeySetManagerService = mock() val packageAbiHelper: PackageAbiHelper = mock() - val appsFilter: AppsFilter = mock { + val appsFilter: AppsFilterImpl = mock { whenever(snapshot()) { this@mock } } val dexManager: DexManager = mock() diff --git a/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml b/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml index 0c531de1827e..a4558acdc274 100644 --- a/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml +++ b/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml @@ -18,4 +18,5 @@ android:accountType="@string/test_account_type1" android:icon="@drawable/icon1" android:smallIcon="@drawable/icon1" + android:customTokens="true" android:label="@string/test_account_type1_authenticator_label" /> diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java index 3c2fbd9ccf7d..863dcb64b885 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java @@ -18,8 +18,11 @@ package com.android.server.accessibility; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -255,6 +258,20 @@ public class MagnificationProcessorTest { } @Test + public void getCurrentMode_changeOtherDisplayMode_returnDefaultModeOnDefaultDisplay() { + final int otherDisplayId = TEST_DISPLAY + 1; + setMagnificationActivated(otherDisplayId, MAGNIFICATION_MODE_WINDOW); + + int currentMode = mMagnificationProcessor.getControllingMode(TEST_DISPLAY); + + assertFalse(mMockMagnificationController.isActivated(TEST_DISPLAY, + ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)); + assertFalse(mMockMagnificationController.isActivated(TEST_DISPLAY, + ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)); + assertEquals(MAGNIFICATION_MODE_FULLSCREEN, currentMode); + } + + @Test public void resetFullscreenMagnification_fullscreenMagnificationActivated() { setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index cc6d7611b4a1..ca22f80a7189 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -555,7 +555,22 @@ public class MagnificationControllerTest { } @Test - public void onRectangleOnScreenRequested_NoneIsActivated_noneDispatchEvent() { + public void onRectangleOnScreenRequested_noneIsActivated_noneDispatchEvent() { + UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks(); + + callbacks.onRectangleOnScreenRequested(TEST_DISPLAY, + TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom); + + verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested( + eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt()); + verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(), + anyInt(), anyInt(), anyInt(), anyInt()); + } + + @Test + public void onRectangleOnScreenRequested_otherDisplayIsActivated_noneEventOnDefaultDisplay() { + mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY + 1, + true); UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks(); callbacks.onRectangleOnScreenRequested(TEST_DISPLAY, @@ -579,6 +594,41 @@ public class MagnificationControllerTest { } @Test + public void getLastActivatedMode_switchMode_returnExpectedLastActivatedMode() + throws RemoteException { + activateMagnifier(MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y); + + final int lastActivatedMode = mMagnificationController + .getLastMagnificationActivatedMode(TEST_DISPLAY); + + assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, lastActivatedMode); + } + + @Test + public void getLastActivatedMode_switchModeAtOtherDisplay_returnExpectedLastActivatedMode() + throws RemoteException { + activateMagnifier(TEST_DISPLAY, MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y); + activateMagnifier(TEST_DISPLAY + 1, MODE_FULLSCREEN, MAGNIFIED_CENTER_X, + MAGNIFIED_CENTER_Y); + + final int lastActivatedMode = mMagnificationController + .getLastMagnificationActivatedMode(TEST_DISPLAY); + + assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, lastActivatedMode); + } + + @Test + public void getLastActivatedMode_otherDisplayIsActivated_defaultModeOnDefaultDisplay() + throws RemoteException { + activateMagnifier(TEST_DISPLAY + 1, MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y); + + int lastActivatedMode = mMagnificationController + .getLastMagnificationActivatedMode(TEST_DISPLAY); + + assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, lastActivatedMode); + } + + @Test public void onFullScreenMagnificationActivationState_fullScreenEnabled_logFullScreenDuration() { MagnificationController spyController = spy(mMagnificationController); spyController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true); @@ -818,17 +868,22 @@ public class MagnificationControllerTest { } private void activateMagnifier(int mode, float centerX, float centerY) throws RemoteException { + activateMagnifier(TEST_DISPLAY, mode, centerX, centerY); + } + + private void activateMagnifier(int displayId, int mode, float centerX, float centerY) + throws RemoteException { final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled( - TEST_DISPLAY); + displayId); if (windowMagnifying) { - mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false); + mWindowMagnificationManager.disableWindowMagnification(displayId, false); mMockConnection.invokeCallbacks(); } if (mode == MODE_FULLSCREEN) { - mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, centerX, + mScreenMagnificationController.setScaleAndCenter(displayId, DEFAULT_SCALE, centerX, centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); } else { - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE, + mWindowMagnificationManager.enableWindowMagnification(displayId, DEFAULT_SCALE, centerX, centerY, null, TEST_SERVICE_ID); mMockConnection.invokeCallbacks(); } diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 997a138cf58f..d5c5745d6680 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -26,9 +26,11 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.never; import static org.mockito.Mockito.nullable; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.accounts.AbstractAccountAuthenticator; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerInternal; @@ -1698,13 +1700,14 @@ public class AccountManagerServiceTest extends AndroidTestCase { final CountDownLatch latch = new CountDownLatch(1); Response response = new Response(latch, mMockAccountManagerResponse); + long expiryEpochTimeInMillis = System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND; mAms.getAuthToken( response, // response AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "authTokenType", // authTokenType true, // notifyOnAuthFailure false, // expectActivityLaunch - createGetAuthTokenOptions()); + createGetAuthTokenOptionsWithExpiry(expiryEpochTimeInMillis)); waitForLatch(latch); verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture()); @@ -1715,6 +1718,58 @@ public class AccountManagerServiceTest extends AndroidTestCase { AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS); assertEquals(result.getString(AccountManager.KEY_ACCOUNT_TYPE), AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1); + assertEquals(result.getLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY), + expiryEpochTimeInMillis); + } + + @SmallTest + public void testGetAuthTokenCachedSuccess() throws Exception { + unlockSystemUser(); + when(mMockContext.createPackageContextAsUser( + anyString(), anyInt(), any(UserHandle.class))).thenReturn(mMockContext); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE}; + when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + long expiryEpochTimeInMillis = System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND; + mAms.getAuthToken( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, + "authTokenType", // authTokenType + true, // notifyOnAuthFailure + false, // expectActivityLaunch + createGetAuthTokenOptionsWithExpiry(expiryEpochTimeInMillis)); + waitForLatch(latch); + + // Make call for cached token. + mAms.getAuthToken( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, + "authTokenType", // authTokenType + true, // notifyOnAuthFailure + false, // expectActivityLaunch + createGetAuthTokenOptionsWithExpiry(expiryEpochTimeInMillis + 10)); + waitForLatch(latch); + + verify(mMockAccountManagerResponse, times(2)).onResult(mBundleCaptor.capture()); + List<Bundle> result = mBundleCaptor.getAllValues(); + assertGetTokenResponse(result.get(0), expiryEpochTimeInMillis); + // cached token was returned with the same expiration time as first token. + assertGetTokenResponse(result.get(1), expiryEpochTimeInMillis); + } + + private void assertGetTokenResponse(Bundle result, long expiryEpochTimeInMillis) { + assertEquals(result.getString(AccountManager.KEY_AUTHTOKEN), + AccountManagerServiceTestFixtures.AUTH_TOKEN); + assertEquals(result.getString(AccountManager.KEY_ACCOUNT_NAME), + AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS); + assertEquals(result.getString(AccountManager.KEY_ACCOUNT_TYPE), + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1); + assertEquals(result.getLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY), + expiryEpochTimeInMillis); + } @SmallTest @@ -3241,11 +3296,16 @@ public class AccountManagerServiceTest extends AndroidTestCase { } private Bundle createGetAuthTokenOptions() { + return createGetAuthTokenOptionsWithExpiry( + System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND); + } + + private Bundle createGetAuthTokenOptionsWithExpiry(long expiryEpochTimeInMillis) { Bundle options = new Bundle(); options.putString(AccountManager.KEY_ANDROID_PACKAGE_NAME, AccountManagerServiceTestFixtures.CALLER_PACKAGE); options.putLong(AccountManagerServiceTestFixtures.KEY_TOKEN_EXPIRY, - System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND); + expiryEpochTimeInMillis); return options; } diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java new file mode 100644 index 000000000000..00f4c3908f26 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.SystemClock; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link DropboxRateLimiter}. + * + * Build/Install/Run: + * atest DropboxRateLimiterTest + */ +public class DropboxRateLimiterTest { + private DropboxRateLimiter mRateLimiter; + private TestClock mClock; + + @Before + public void setUp() { + mClock = new TestClock(); + mRateLimiter = new DropboxRateLimiter(mClock); + } + + @Test + public void testMultipleProcesses() { + // The first 5 entries should not be rate limited. + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + // Different processes and tags should not get rate limited either. + assertFalse(mRateLimiter.shouldRateLimit("tag", "process2")); + assertFalse(mRateLimiter.shouldRateLimit("tag2", "process")); + // The 6th entry of the same process should be rate limited. + assertTrue(mRateLimiter.shouldRateLimit("tag", "process")); + } + + @Test + public void testBufferClearing() throws Exception { + // The first 5 entries should not be rate limited. + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + // The 6th entry of the same process should be rate limited. + assertTrue(mRateLimiter.shouldRateLimit("tag", "process")); + + // After 11 seconds there should be nothing left in the buffer and the same type of entry + // should not get rate limited anymore. + mClock.setOffsetMillis(11000); + + assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); + } + + private static class TestClock implements DropboxRateLimiter.Clock { + long mOffsetMillis = 0L; + + public long uptimeMillis() { + return mOffsetMillis + SystemClock.uptimeMillis(); + } + + public void setOffsetMillis(long millis) { + mOffsetMillis = millis; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java b/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java index b154d6f6db0c..1171518130cc 100644 --- a/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java @@ -110,10 +110,7 @@ public class BackupTransportClientTest { Thread thread = new Thread(() -> { try { - /*String name =*/ client.transportDirName(); - fail("transportDirName should be cancelled"); - } catch (CancellationException ex) { - // This is expected. + assertThat(client.transportDirName()).isNull(); } catch (Exception ex) { fail("unexpected Exception: " + ex.getClass().getCanonicalName()); } @@ -189,7 +186,7 @@ public class BackupTransportClientTest { } @Test - public void testFinishBackup_canceledBeforeCompletion_throwsException() throws Exception { + public void testFinishBackup_canceledBeforeCompletion_returnsError() throws Exception { TestCallbacksFakeTransportBinder binder = new TestCallbacksFakeTransportBinder(); BackupTransportClient client = new BackupTransportClient(binder); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java index 33ea7108a705..b9ae6702c37e 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java @@ -25,6 +25,8 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; + import static junit.framework.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; @@ -48,6 +50,7 @@ import android.os.Looper; import android.os.Process; import android.os.ResultReceiver; import android.os.ShellCallback; +import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; @@ -370,6 +373,19 @@ public class LockSettingsShellCommandTest { mUserId); } + @Test + public void testRequireStrongAuth_STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN() throws Exception { + when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true); + + assertEquals(0, mCommand.exec(new Binder(), in, out, err, + new String[] { "require-strong-auth", "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN"}, + mShellCallback, mResultReceiver)); + + verify(mLockPatternUtils).requireStrongAuth( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, + UserHandle.USER_ALL); + } + private List<LockPatternView.Cell> stringToPattern(String str) { return LockPatternUtils.byteArrayToPattern(str.getBytes()); } diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java index b72b8d2ec6e8..d8f4349b95bf 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java @@ -77,7 +77,7 @@ import java.util.concurrent.Executor; @Presubmit @RunWith(JUnit4.class) -public class AppsFilterTest { +public class AppsFilterImplTest { private static final int DUMMY_CALLING_APPID = 10345; private static final int DUMMY_TARGET_APPID = 10556; @@ -98,9 +98,9 @@ public class AppsFilterTest { } @Mock - AppsFilter.FeatureConfig mFeatureConfigMock; + AppsFilterImpl.FeatureConfig mFeatureConfigMock; @Mock - AppsFilter.StateProvider mStateProvider; + AppsFilterImpl.StateProvider mStateProvider; @Mock Executor mMockExecutor; @Mock @@ -204,11 +204,11 @@ public class AppsFilterTest { MockitoAnnotations.initMocks(this); doAnswer(invocation -> { - ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) + ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) .currentState(mExisting, USER_INFO_LIST); return new Object(); }).when(mStateProvider) - .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); + .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); doAnswer(invocation -> { ((Runnable) invocation.getArgument(0)).run(); @@ -218,14 +218,14 @@ public class AppsFilterTest { when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true); when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))).thenAnswer( (Answer<Boolean>) invocation -> - ((AndroidPackage)invocation.getArgument(SYSTEM_USER)).getTargetSdkVersion() + ((AndroidPackage) invocation.getArgument(SYSTEM_USER)).getTargetSdkVersion() >= Build.VERSION_CODES.R); } @Test public void testSystemReadyPropogates() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -236,8 +236,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_FilterMatches() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -259,8 +259,8 @@ public class AppsFilterTest { } @Test public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -308,8 +308,8 @@ public class AppsFilterTest { @Test public void testQueriesProvider_FilterMatches() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -333,8 +333,8 @@ public class AppsFilterTest { @Test public void testOnUserUpdated_FilterMatches() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); @@ -356,11 +356,11 @@ public class AppsFilterTest { // adds new user doAnswer(invocation -> { - ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) + ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) .currentState(mExisting, USER_INFO_LIST_WITH_ADDED); return new Object(); }).when(mStateProvider) - .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); + .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); appsFilter.onUserCreated(ADDED_USER); for (int subjectUserId : USER_ARRAY_WITH_ADDED) { @@ -373,11 +373,11 @@ public class AppsFilterTest { // delete user doAnswer(invocation -> { - ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) + ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) .currentState(mExisting, USER_INFO_LIST); return new Object(); }).when(mStateProvider) - .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); + .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); appsFilter.onUserDeleted(ADDED_USER); for (int subjectUserId : USER_ARRAY) { @@ -391,8 +391,8 @@ public class AppsFilterTest { @Test public void testQueriesDifferentProvider_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -416,8 +416,8 @@ public class AppsFilterTest { @Test public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -435,8 +435,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_NoMatchingAction_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -452,8 +452,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -473,8 +473,8 @@ public class AppsFilterTest { @Test public void testNoQueries_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -490,7 +490,7 @@ public class AppsFilterTest { @Test public void testNoUsesLibrary_Filters() throws Exception { - final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, mMockExecutor, mMockPmInternal); @@ -516,7 +516,7 @@ public class AppsFilterTest { @Test public void testUsesLibrary_DoesntFilter() throws Exception { - final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, mMockExecutor, mMockPmInternal); @@ -543,7 +543,7 @@ public class AppsFilterTest { @Test public void testUsesOptionalLibrary_DoesntFilter() throws Exception { - final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, mMockExecutor, mMockPmInternal); @@ -570,7 +570,7 @@ public class AppsFilterTest { @Test public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception { - final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, mMockExecutor, mMockPmInternal); @@ -602,8 +602,8 @@ public class AppsFilterTest { @Test public void testForceQueryable_SystemDoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -621,8 +621,8 @@ public class AppsFilterTest { @Test public void testForceQueryable_NonSystemFilters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -638,9 +638,10 @@ public class AppsFilterTest { @Test public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, - false, null, mMockExecutor, mMockPmInternal); + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, + new String[]{"com.some.package"}, false, null, + mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -657,8 +658,8 @@ public class AppsFilterTest { @Test public void testSystemSignedTarget_DoesntFilter() throws CertificateException { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); appsFilter.onSystemReady(); @@ -686,9 +687,10 @@ public class AppsFilterTest { @Test public void testForceQueryableByDevice_NonSystemCaller_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, - false, null, mMockExecutor, mMockPmInternal); + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, + new String[]{"com.some.package"}, false, null, + mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -704,8 +706,8 @@ public class AppsFilterTest { @Test public void testSystemQueryable_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, true /* system force queryable */, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); @@ -723,8 +725,8 @@ public class AppsFilterTest { @Test public void testQueriesPackage_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -742,8 +744,8 @@ public class AppsFilterTest { public void testNoQueries_FeatureOff_DoesntFilter() throws Exception { when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))) .thenReturn(false); - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -759,8 +761,8 @@ public class AppsFilterTest { @Test public void testSystemUid_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -775,8 +777,8 @@ public class AppsFilterTest { @Test public void testSystemUidSecondaryUser_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -792,8 +794,8 @@ public class AppsFilterTest { @Test public void testNonSystemUid_NoCallingSetting_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -807,8 +809,8 @@ public class AppsFilterTest { @Test public void testNoTargetPackage_filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -838,7 +840,7 @@ public class AppsFilterTest { .setOverlayTargetOverlayableName("overlayableName"); ParsingPackage actor = pkg("com.some.package.actor"); - final AppsFilter appsFilter = new AppsFilter( + final AppsFilterImpl appsFilter = new AppsFilterImpl( mStateProvider, mFeatureConfigMock, new String[]{}, @@ -933,7 +935,7 @@ public class AppsFilterTest { when(mMockPmInternal.getSharedUserPackages(any(Integer.class))).thenReturn( actorSharedSettingPackages ); - final AppsFilter appsFilter = new AppsFilter( + final AppsFilterImpl appsFilter = new AppsFilterImpl( mStateProvider, mFeatureConfigMock, new String[]{}, @@ -985,8 +987,8 @@ public class AppsFilterTest { @Test public void testInitiatingApp_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1003,8 +1005,8 @@ public class AppsFilterTest { @Test public void testUninstalledInitiatingApp_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1021,8 +1023,8 @@ public class AppsFilterTest { @Test public void testOriginatingApp_Filters() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -1046,8 +1048,8 @@ public class AppsFilterTest { @Test public void testInstallingApp_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -1071,8 +1073,8 @@ public class AppsFilterTest { @Test public void testInstrumentation_DoesntFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -1100,8 +1102,8 @@ public class AppsFilterTest { @Test public void testWhoCanSee() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -1173,8 +1175,8 @@ public class AppsFilterTest { @Test public void testOnChangeReport() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); @@ -1246,8 +1248,8 @@ public class AppsFilterTest { @Test public void testOnChangeReportedFilter() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor, mMockPmInternal); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1270,6 +1272,53 @@ public class AppsFilterTest { watcher.verifyNoChangeReported("shouldFilterApplication"); } + @Test + public void testAppsFilterRead() throws Exception { + final AppsFilterImpl appsFilter = + new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor, mMockPmInternal); + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), + DUMMY_TARGET_APPID); + PackageSetting instrumentation = simulateAddPackage(appsFilter, + pkgWithInstrumentation("com.some.other.package", "com.some.package"), + DUMMY_CALLING_APPID); + + final int hasProviderAppId = Process.FIRST_APPLICATION_UID + 1; + final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2; + PackageSetting queriesProvider = simulateAddPackage(appsFilter, + pkgQueriesProvider("com.yet.some.other.package", "com.some.authority"), + queriesProviderAppId); + appsFilter.grantImplicitAccess( + hasProviderAppId, queriesProviderAppId, false /* retainOnUpdate */); + + AppsFilterSnapshot snapshot = appsFilter.snapshot(); + assertFalse( + snapshot.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target, + SYSTEM_USER)); + assertFalse( + snapshot.shouldFilterApplication(DUMMY_TARGET_APPID, target, instrumentation, + SYSTEM_USER)); + + SparseArray<int[]> queriesProviderFilter = + snapshot.getVisibilityAllowList(queriesProvider, USER_ARRAY, mExisting); + assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)), contains(queriesProviderAppId)); + assertTrue(snapshot.canQueryPackage(instrumentation.getPkg(), + target.getPackageName())); + + // New changes don't affect the snapshot + appsFilter.removePackage(target, false); + assertTrue( + appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target, + SYSTEM_USER)); + assertFalse( + snapshot.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target, + SYSTEM_USER)); + + } + private List<Integer> toList(int[] array) { ArrayList<Integer> ret = new ArrayList<>(array.length); for (int i = 0; i < array.length; i++) { @@ -1282,7 +1331,7 @@ public class AppsFilterTest { PackageSettingBuilder withBuilder(PackageSettingBuilder builder); } - private void simulateAddBasicAndroid(AppsFilter appsFilter) throws Exception { + private void simulateAddBasicAndroid(AppsFilterImpl appsFilter) throws Exception { final Signature frameworkSignature = Mockito.mock(Signature.class); final SigningDetails frameworkSigningDetails = new SigningDetails(new Signature[]{frameworkSignature}, 1); @@ -1291,17 +1340,17 @@ public class AppsFilterTest { b -> b.setSigningDetails(frameworkSigningDetails)); } - private PackageSetting simulateAddPackage(AppsFilter filter, + private PackageSetting simulateAddPackage(AppsFilterImpl filter, ParsingPackage newPkgBuilder, int appId) { return simulateAddPackage(filter, newPkgBuilder, appId, null /*settingBuilder*/); } - private PackageSetting simulateAddPackage(AppsFilter filter, + private PackageSetting simulateAddPackage(AppsFilterImpl filter, ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action) { return simulateAddPackage(filter, newPkgBuilder, appId, action, null /*sharedUserSetting*/); } - private PackageSetting simulateAddPackage(AppsFilter filter, + private PackageSetting simulateAddPackage(AppsFilterImpl filter, ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action, @Nullable SharedUserSetting sharedUserSetting) { final PackageSetting setting = @@ -1324,7 +1373,7 @@ public class AppsFilterTest { return setting; } - private void simulateAddPackage(PackageSetting setting, AppsFilter filter, + private void simulateAddPackage(PackageSetting setting, AppsFilterImpl filter, @Nullable SharedUserSetting sharedUserSetting) { mExisting.put(setting.getPackageName(), setting); if (sharedUserSetting != null) { diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java index 1d9ea4b6028c..0b144dc7ee3a 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java @@ -41,6 +41,8 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTest { + private static final String SDK_INT_PLUS_ONE = "" + (Build.VERSION.SDK_INT + 1); + private static final String SDK_INT_PLUS_TWO = "" + (Build.VERSION.SDK_INT + 2); private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries = new ArrayMap<>(8); @@ -51,14 +53,19 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes private void installSharedLibraries() throws Exception { mSharedLibraries.clear(); - insertLibrary("foo", 0, 0); - insertLibrary("fooBcpSince30", 30, 0); - insertLibrary("fooBcpBefore30", 0, 30); - insertLibrary("fooFromFuture", Build.VERSION.SDK_INT + 2, 0); + insertLibrary("foo", null, null); + insertLibrary("fooBcpSince30", "30", null); + insertLibrary("fooBcpBefore30", null, "30"); + // simulate libraries being added to the BCP in a future release + insertLibrary("fooSinceFuture", SDK_INT_PLUS_ONE, null); + insertLibrary("fooSinceFutureCodename", "Z", null); + // simulate libraries being removed from the BCP in a future release + insertLibrary("fooBcpBeforeFuture", null, SDK_INT_PLUS_ONE); + insertLibrary("fooBcpBeforeFutureCodename", null, "Z"); } - private void insertLibrary(String libraryName, int onBootclasspathSince, - int onBootclasspathBefore) { + private void insertLibrary(String libraryName, String onBootclasspathSince, + String onBootclasspathBefore) { mSharedLibraries.put(libraryName, new SystemConfig.SharedLibraryEntry( libraryName, "foo.jar", @@ -112,7 +119,7 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes } @Test - public void testBcpSince11kNotAppliedWithoutLibrary() { + public void testBcpSinceFutureNotAppliedWithoutLibrary() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()); @@ -128,15 +135,17 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes } @Test - public void testBcpSince11kNotAppliedWithLibrary() { + public void testBcpSinceFutureNotAppliedWithLibrary() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) - .addUsesLibrary("fooFromFuture") + .addUsesLibrary("fooSinceFuture") + .addUsesLibrary("fooSinceFutureCodename") .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) - .addUsesLibrary("fooFromFuture") + .addUsesLibrary("fooSinceFuture") + .addUsesLibrary("fooSinceFutureCodename") .hideAsParsed()) .hideAsFinal(); @@ -183,7 +192,7 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes */ @Test public void testBcpRemovedThenAddedPast() { - insertLibrary("fooBcpRemovedThenAdded", 30, 28); + insertLibrary("fooBcpRemovedThenAdded", "30", "28"); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.N) @@ -207,7 +216,8 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes */ @Test public void testBcpRemovedThenAddedMiddle_targetQ() { - insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); + insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30"); + insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30"); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.Q) @@ -217,6 +227,7 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes .setTargetSdkVersion(Build.VERSION_CODES.Q) .addUsesLibrary("fooBcpRemovedThenAdded") .addUsesLibrary("fooBcpBefore30") + .addUsesLibrary("fooBcpRemovedThenAddedCodename") .hideAsParsed()) .hideAsFinal(); @@ -232,7 +243,8 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes */ @Test public void testBcpRemovedThenAddedMiddle_targetR() { - insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); + insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30"); + insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30"); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) @@ -256,7 +268,8 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes */ @Test public void testBcpRemovedThenAddedMiddle_targetR_usingLib() { - insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); + insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30"); + insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30"); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) @@ -274,6 +287,82 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes checkBackwardsCompatibility(before, after); } + /** + * Test a library that was first removed from the BCP [to a mainline module] and later was + * moved back to the BCP via a mainline module update. Both things happening in future SDKs. + */ + @Test + public void testBcpRemovedThenAddedFuture() { + insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_TWO, SDK_INT_PLUS_ONE); + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .hideAsParsed()) + .hideAsFinal(); + + // in this example, we are at the point where the library is still in the BCP + checkBackwardsCompatibility(before, after); + } + + /** + * Test a library that was first removed from the BCP [to a mainline module] and later was + * moved back to the BCP via a mainline module update. Both things happening in future SDKs. + */ + @Test + public void testBcpRemovedThenAddedFuture_usingLib() { + insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_TWO, SDK_INT_PLUS_ONE); + + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE)) + .addUsesLibrary("fooBcpRemovedThenAdded") + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE)) + .hideAsParsed()) + .hideAsFinal(); + + // in this example, we are at the point where the library was removed from the BCP + checkBackwardsCompatibility(before, after); + } + + @Test + public void testBcpBeforeFuture() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .addUsesLibrary("fooBcpBeforeFuture") + .addUsesLibrary("fooBcpBeforeFutureCodename") + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .hideAsParsed()) + .hideAsFinal(); + + // in this example, we are at the point where the library was removed from the BCP + checkBackwardsCompatibility(before, after); + } + + @Test + public void testBcpBeforeFuture_futureTargetSdk() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE)) + .addUsesLibrary("fooBcpBeforeFuture") + .addUsesLibrary("fooBcpBeforeFutureCodename") + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE)) + .hideAsParsed()) + .hideAsFinal(); + + // in this example, we are at the point where the library was removed from the BCP + checkBackwardsCompatibility(before, after); + } + private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { checkBackwardsCompatibility(before, after, () -> new ApexSharedLibraryUpdater(mSharedLibraries)); diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java index 6bdd88c6a712..2d0755d00ba8 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java @@ -20,18 +20,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; 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.atLeast; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -56,32 +53,20 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.mockito.ArgumentCaptor; -import java.util.LinkedList; -import java.util.List; import java.util.concurrent.atomic.AtomicReference; @RunWith(Parameterized.class) public class SoundHw2CompatTest { - @Parameterized.Parameter(0) public String mVersion; - @Parameterized.Parameter(1) public boolean mSupportConcurrentCapture; + @Parameterized.Parameter public String mVersion; private final Runnable mRebootRunnable = mock(Runnable.class); private ISoundTriggerHal mCanonical; - private CaptureStateNotifier mCaptureStateNotifier; private android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver; // We run the test once for every version of the underlying driver. - @Parameterized.Parameters(name = "{0}, concurrent={1}") - public static Iterable<Object[]> data() { - List<Object[]> result = new LinkedList<>(); - - for (String version : new String[]{"V2_0", "V2_1", "V2_2", "V2_3",}) { - for (boolean concurrentCapture : new boolean[]{false, true}) { - result.add(new Object[]{version, concurrentCapture}); - } - } - - return result; + @Parameterized.Parameters + public static Object[] data() { + return new String[]{"V2_0", "V2_1", "V2_2", "V2_3"}; } @Before @@ -139,7 +124,7 @@ public class SoundHw2CompatTest { when(mHalDriver.asBinder()).thenReturn(binder); android.hardware.soundtrigger.V2_3.Properties halProperties = - TestUtil.createDefaultProperties_2_3(mSupportConcurrentCapture); + TestUtil.createDefaultProperties_2_3(); doAnswer(invocation -> { ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument( 0)).onValues(0, halProperties.base); @@ -156,10 +141,7 @@ public class SoundHw2CompatTest { }).when(driver).getProperties_2_3(any()); } - mCaptureStateNotifier = spy(new CaptureStateNotifier()); - - mCanonical = SoundTriggerHw2Compat.create(mHalDriver, mRebootRunnable, - mCaptureStateNotifier); + mCanonical = SoundTriggerHw2Compat.create(mHalDriver, mRebootRunnable, null); // During initialization any method can be called, but after we're starting to enforce that // no additional methods are called. @@ -171,7 +153,6 @@ public class SoundHw2CompatTest { mCanonical.detach(); verifyNoMoreInteractions(mHalDriver); verifyNoMoreInteractions(mRebootRunnable); - mCaptureStateNotifier.verifyNoMoreListeners(); } @Test @@ -194,12 +175,12 @@ public class SoundHw2CompatTest { // It is OK for the SUT to cache the properties, so the underlying method doesn't // need to be called every single time. verify(driver, atMost(1)).getProperties_2_3(any()); - TestUtil.validateDefaultProperties(properties, mSupportConcurrentCapture); + TestUtil.validateDefaultProperties(properties); } else { // It is OK for the SUT to cache the properties, so the underlying method doesn't // need to be called every single time. verify(mHalDriver, atMost(1)).getProperties(any()); - TestUtil.validateDefaultProperties(properties, mSupportConcurrentCapture, 0, ""); + TestUtil.validateDefaultProperties(properties, 0, ""); } } @@ -291,7 +272,7 @@ public class SoundHw2CompatTest { ISoundTriggerHal.ModelCallback canonicalCallback = mock( ISoundTriggerHal.ModelCallback.class); - final int maxModels = TestUtil.createDefaultProperties_2_0(false).maxSoundModels; + final int maxModels = TestUtil.createDefaultProperties_2_0().maxSoundModels; int[] modelHandles = new int[maxModels]; // Load as many models as we're allowed. @@ -318,7 +299,7 @@ public class SoundHw2CompatTest { verify(globalCallback).onResourcesAvailable(); } - private int loadPhraseModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback) + private void loadPhraseModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception { final int handle = 29; ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel> @@ -345,10 +326,9 @@ public class SoundHw2CompatTest { TestUtil.validatePhraseSoundModel_2_0(modelCaptor.getValue()); validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback); - return handle; } - private int loadPhraseModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback) + private void loadPhraseModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception { final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 = (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver; @@ -380,14 +360,13 @@ public class SoundHw2CompatTest { TestUtil.validatePhraseSoundModel_2_1(model.get()); validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback); - return handle; } - public int loadPhraseModel(ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception { + public void loadPhraseModel(ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception { if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) { - return loadPhraseModel_2_1(canonicalCallback); + loadPhraseModel_2_1(canonicalCallback); } else { - return loadPhraseModel_2_0(canonicalCallback); + loadPhraseModel_2_0(canonicalCallback); } } @@ -484,80 +463,6 @@ public class SoundHw2CompatTest { } @Test - public void testConcurrentCaptureAbort() throws Exception { - assumeFalse(mSupportConcurrentCapture); - verify(mCaptureStateNotifier, atLeast(1)).registerListener(any()); - - // Register global callback. - ISoundTriggerHal.GlobalCallback globalCallback = mock( - ISoundTriggerHal.GlobalCallback.class); - mCanonical.registerCallback(globalCallback); - - // Load. - ISoundTriggerHal.ModelCallback canonicalCallback = mock( - ISoundTriggerHal.ModelCallback.class); - final int handle = loadGenericModel(canonicalCallback); - - // Then start. - startRecognition(handle, canonicalCallback); - - // Now activate external capture. - mCaptureStateNotifier.setState(true); - - // Expect hardware to have been stopped. - verify(mHalDriver).stopRecognition(handle); - - // Expect an abort event (async). - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); - mCanonical.flushCallbacks(); - verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture()); - assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status); - - // Deactivate external capture. - mCaptureStateNotifier.setState(false); - - // Expect a onResourcesAvailable(). - mCanonical.flushCallbacks(); - verify(globalCallback).onResourcesAvailable(); - } - - @Test - public void testConcurrentCaptureReject() throws Exception { - assumeFalse(mSupportConcurrentCapture); - verify(mCaptureStateNotifier, atLeast(1)).registerListener(any()); - - // Register global callback. - ISoundTriggerHal.GlobalCallback globalCallback = mock( - ISoundTriggerHal.GlobalCallback.class); - mCanonical.registerCallback(globalCallback); - - // Load (this registers the callback). - ISoundTriggerHal.ModelCallback canonicalCallback = mock( - ISoundTriggerHal.ModelCallback.class); - final int handle = loadGenericModel(canonicalCallback); - - // Report external capture active. - mCaptureStateNotifier.setState(true); - - // Then start. - RecognitionConfig config = TestUtil.createRecognitionConfig(); - try { - mCanonical.startRecognition(handle, 203, 204, config); - fail("Expected an exception"); - } catch (RecoverableException e) { - assertEquals(Status.RESOURCE_CONTENTION, e.errorCode); - } - - // Deactivate external capture. - mCaptureStateNotifier.setState(false); - - // Expect a onResourcesAvailable(). - mCanonical.flushCallbacks(); - verify(globalCallback).onResourcesAvailable(); - } - - @Test public void testStopRecognition() throws Exception { mCanonical.stopRecognition(17); verify(mHalDriver).stopRecognition(17); @@ -675,7 +580,7 @@ public class SoundHw2CompatTest { } @Test - public void testGlobalCallback() throws Exception { + public void testGlobalCallback() { testGlobalCallback_2_0(); } @@ -803,29 +708,4 @@ public class SoundHw2CompatTest { verifyNoMoreInteractions(canonicalCallback); clearInvocations(canonicalCallback); } - - public static class CaptureStateNotifier implements ICaptureStateNotifier { - private final List<Listener> mListeners = new LinkedList<>(); - - @Override - public boolean registerListener(Listener listener) { - mListeners.add(listener); - return false; - } - - @Override - public void unregisterListener(Listener listener) { - mListeners.remove(listener); - } - - public void setState(boolean state) { - for (Listener listener : mListeners) { - listener.onCaptureStateChange(state); - } - } - - public void verifyNoMoreListeners() { - assertEquals(0, mListeners.size()); - } - } } diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java new file mode 100644 index 000000000000..61989252d04d --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.soundtrigger_middleware; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.media.soundtrigger.RecognitionEvent; +import android.media.soundtrigger.RecognitionStatus; + +import androidx.annotation.NonNull; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.ArgumentCaptor; + +@RunWith(JUnit4.class) +public class SoundTriggerHalConcurrentCaptureHandlerTest { + private ISoundTriggerHal mUnderlying; + private CaptureStateNotifier mNotifier; + private ISoundTriggerHal.GlobalCallback mGlobalCallback; + private SoundTriggerHalConcurrentCaptureHandler mHandler; + + @Before + public void setUp() { + mNotifier = new CaptureStateNotifier(); + mUnderlying = mock(ISoundTriggerHal.class); + mGlobalCallback = mock(ISoundTriggerHal.GlobalCallback.class); + mHandler = new SoundTriggerHalConcurrentCaptureHandler(mUnderlying, mNotifier); + mHandler.registerCallback(mGlobalCallback); + } + + @Test + public void testBasic() throws Exception { + ISoundTriggerHal.ModelCallback callback = mock(ISoundTriggerHal.ModelCallback.class); + int handle = mHandler.loadSoundModel(TestUtil.createGenericSoundModel(), callback); + verify(mUnderlying).loadSoundModel(any(), any()); + + mHandler.startRecognition(handle, 101, 102, TestUtil.createRecognitionConfig()); + verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); + + mNotifier.setActive(true); + verify(mUnderlying).stopRecognition(handle); + ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( + RecognitionEvent.class); + Thread.sleep(50); + verify(callback).recognitionCallback(eq(handle), eventCaptor.capture()); + RecognitionEvent event = eventCaptor.getValue(); + assertEquals(event.status, RecognitionStatus.ABORTED); + assertFalse(event.recognitionStillActive); + verifyZeroInteractions(mGlobalCallback); + clearInvocations(callback, mUnderlying); + + mNotifier.setActive(false); + Thread.sleep(50); + verify(mGlobalCallback).onResourcesAvailable(); + verifyNoMoreInteractions(callback, mUnderlying); + + mNotifier.setActive(true); + verifyNoMoreInteractions(callback, mUnderlying); + } + + @Test + public void testStopBeforeActive() throws Exception { + ISoundTriggerHal.ModelCallback callback = mock(ISoundTriggerHal.ModelCallback.class); + int handle = mHandler.loadSoundModel(TestUtil.createGenericSoundModel(), callback); + verify(mUnderlying).loadSoundModel(any(), any()); + + mHandler.startRecognition(handle, 101, 102, TestUtil.createRecognitionConfig()); + verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); + mHandler.stopRecognition(handle); + verify(mUnderlying).stopRecognition(handle); + clearInvocations(mUnderlying); + + mNotifier.setActive(true); + Thread.sleep(50); + verifyNoMoreInteractions(mUnderlying); + verifyNoMoreInteractions(callback); + } + + @Test + public void testStopAfterActive() { + ISoundTriggerHal.ModelCallback callback = mock(ISoundTriggerHal.ModelCallback.class); + int handle = mHandler.loadSoundModel(TestUtil.createGenericSoundModel(), callback); + verify(mUnderlying).loadSoundModel(any(), any()); + + mHandler.startRecognition(handle, 101, 102, TestUtil.createRecognitionConfig()); + verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); + + mNotifier.setActive(true); + verify(mUnderlying, times(1)).stopRecognition(handle); + mHandler.stopRecognition(handle); + verify(callback, times(1)).recognitionCallback(eq(handle), any()); + } + + @Test(timeout = 200) + public void testAbortWhileStop() { + ISoundTriggerHal.ModelCallback callback = mock(ISoundTriggerHal.ModelCallback.class); + int handle = mHandler.loadSoundModel(TestUtil.createGenericSoundModel(), callback); + ArgumentCaptor<ISoundTriggerHal.ModelCallback> modelCallbackCaptor = + ArgumentCaptor.forClass(ISoundTriggerHal.ModelCallback.class); + verify(mUnderlying).loadSoundModel(any(), modelCallbackCaptor.capture()); + ISoundTriggerHal.ModelCallback modelCallback = modelCallbackCaptor.getValue(); + + mHandler.startRecognition(handle, 101, 102, TestUtil.createRecognitionConfig()); + verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); + + doAnswer(invocation -> { + RecognitionEvent event = TestUtil.createRecognitionEvent(RecognitionStatus.ABORTED, + false); + // Call the callback from a different thread to detect deadlocks by preventing recursive + // locking from working. + runOnSeparateThread(() -> modelCallback.recognitionCallback(handle, event)); + return null; + }).when(mUnderlying).stopRecognition(handle); + mHandler.stopRecognition(handle); + verify(mUnderlying, times(1)).stopRecognition(handle); + + ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( + RecognitionEvent.class); + verify(callback, atMost(1)).recognitionCallback(eq(handle), eventCaptor.capture()); + } + + @Test(timeout = 200) + public void testActiveWhileStop() { + ISoundTriggerHal.ModelCallback callback = mock(ISoundTriggerHal.ModelCallback.class); + int handle = mHandler.loadSoundModel(TestUtil.createGenericSoundModel(), callback); + ArgumentCaptor<ISoundTriggerHal.ModelCallback> modelCallbackCaptor = + ArgumentCaptor.forClass(ISoundTriggerHal.ModelCallback.class); + verify(mUnderlying).loadSoundModel(any(), modelCallbackCaptor.capture()); + ISoundTriggerHal.ModelCallback modelCallback = modelCallbackCaptor.getValue(); + + mHandler.startRecognition(handle, 101, 102, TestUtil.createRecognitionConfig()); + verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); + + doAnswer(invocation -> { + // The stop request causes a callback to be flushed. + RecognitionEvent event = TestUtil.createRecognitionEvent(RecognitionStatus.FORCED, + true); + // Call the callback from a different thread to detect deadlocks by preventing recursive + // locking from working. + runOnSeparateThread(() -> modelCallback.recognitionCallback(handle, event)); + // While the HAL is processing the stop request, capture state becomes active. + new Thread(() -> mNotifier.setActive(true)).start(); + Thread.sleep(50); + return null; + }).when(mUnderlying).stopRecognition(handle); + mHandler.stopRecognition(handle); + // We only expect one underlying invocation of stop(). + verify(mUnderlying, times(1)).stopRecognition(handle); + + // The callback shouldn't be invoked in this case. + verify(callback, never()).recognitionCallback(eq(handle), any()); + } + + @Test(timeout = 200) + public void testStopWhileActive() { + ISoundTriggerHal.ModelCallback callback = mock(ISoundTriggerHal.ModelCallback.class); + int handle = mHandler.loadSoundModel(TestUtil.createGenericSoundModel(), callback); + ArgumentCaptor<ISoundTriggerHal.ModelCallback> modelCallbackCaptor = + ArgumentCaptor.forClass(ISoundTriggerHal.ModelCallback.class); + verify(mUnderlying).loadSoundModel(any(), modelCallbackCaptor.capture()); + ISoundTriggerHal.ModelCallback modelCallback = modelCallbackCaptor.getValue(); + + mHandler.startRecognition(handle, 101, 102, TestUtil.createRecognitionConfig()); + verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); + + doAnswer(invocation -> { + // The stop request causes a callback to be flushed. + RecognitionEvent event = TestUtil.createRecognitionEvent(RecognitionStatus.FORCED, + true); + // Call the callback from a different thread to detect deadlocks by preventing recursive + // locking from working. + runOnSeparateThread(() -> modelCallback.recognitionCallback(handle, event)); + // While the HAL is processing the stop request, client requests stop. + new Thread(() -> mHandler.stopRecognition(handle)).start(); + Thread.sleep(50); + return null; + }).when(mUnderlying).stopRecognition(handle); + mNotifier.setActive(true); + // We only expect one underlying invocation of stop(). + verify(mUnderlying, times(1)).stopRecognition(handle); + verify(callback, atMost(1)).recognitionCallback(eq(handle), any()); + } + + @Test(timeout = 200) + public void testEventWhileActive() throws Exception { + ISoundTriggerHal.ModelCallback callback = mock(ISoundTriggerHal.ModelCallback.class); + int handle = mHandler.loadSoundModel(TestUtil.createGenericSoundModel(), callback); + ArgumentCaptor<ISoundTriggerHal.ModelCallback> modelCallbackCaptor = + ArgumentCaptor.forClass(ISoundTriggerHal.ModelCallback.class); + verify(mUnderlying).loadSoundModel(any(), modelCallbackCaptor.capture()); + ISoundTriggerHal.ModelCallback modelCallback = modelCallbackCaptor.getValue(); + + mHandler.startRecognition(handle, 101, 102, TestUtil.createRecognitionConfig()); + verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); + + doAnswer(invocation -> { + RecognitionEvent event = TestUtil.createRecognitionEvent(RecognitionStatus.SUCCESS, + false); + // Call the callback from a different thread to detect deadlocks by preventing recursive + // locking from working. + runOnSeparateThread(() -> modelCallback.recognitionCallback(handle, event)); + return null; + }).when(mUnderlying).stopRecognition(handle); + mNotifier.setActive(true); + verify(mUnderlying, times(1)).stopRecognition(handle); + Thread.sleep(50); + + ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( + RecognitionEvent.class); + verify(callback, atMost(2)).recognitionCallback(eq(handle), eventCaptor.capture()); + RecognitionEvent lastEvent = eventCaptor.getValue(); + assertEquals(lastEvent.status, RecognitionStatus.ABORTED); + assertFalse(lastEvent.recognitionStillActive); + } + + + @Test(timeout = 200) + public void testNonFinalEventWhileActive() throws Exception { + ISoundTriggerHal.ModelCallback callback = mock(ISoundTriggerHal.ModelCallback.class); + int handle = mHandler.loadSoundModel(TestUtil.createGenericSoundModel(), callback); + ArgumentCaptor<ISoundTriggerHal.ModelCallback> modelCallbackCaptor = + ArgumentCaptor.forClass(ISoundTriggerHal.ModelCallback.class); + verify(mUnderlying).loadSoundModel(any(), modelCallbackCaptor.capture()); + ISoundTriggerHal.ModelCallback modelCallback = modelCallbackCaptor.getValue(); + + mHandler.startRecognition(handle, 101, 102, TestUtil.createRecognitionConfig()); + verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); + + doAnswer(invocation -> { + RecognitionEvent event = TestUtil.createRecognitionEvent(RecognitionStatus.FORCED, + true); + // Call the callback from a different thread to detect deadlocks by preventing recursive + // locking from working. + runOnSeparateThread(() -> modelCallback.recognitionCallback(handle, event)); + + return null; + }).when(mUnderlying).stopRecognition(handle); + mNotifier.setActive(true); + verify(mUnderlying, times(1)).stopRecognition(handle); + + Thread.sleep(50); + ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( + RecognitionEvent.class); + verify(callback, atMost(2)).recognitionCallback(eq(handle), eventCaptor.capture()); + RecognitionEvent lastEvent = eventCaptor.getValue(); + assertEquals(lastEvent.status, RecognitionStatus.ABORTED); + assertFalse(lastEvent.recognitionStillActive); + } + + private static void runOnSeparateThread(Runnable runnable) { + Thread thread = new Thread(runnable); + thread.start(); + try { + thread.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private static class CaptureStateNotifier implements ICaptureStateNotifier { + boolean mActive = false; + Listener mListener; + + @Override + public boolean registerListener(@NonNull Listener listener) { + mListener = listener; + return mActive; + } + + @Override + public void unregisterListener(@NonNull Listener listener) { + mListener = null; + } + + public void setActive(boolean active) { + mActive = active; + if (mListener != null) { + // Call the callback from a different thread to detect deadlocks by preventing + // recursive locking from working. + runOnSeparateThread(() -> mListener.onCaptureStateChange(mActive)); + } + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java index 0187e34cbc5f..3bebc94fe0ed 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java @@ -134,7 +134,7 @@ public class SoundTriggerMiddlewareImplTest { public void setUp() throws Exception { clearInvocations(mHalDriver); clearInvocations(mAudioSessionProvider); - when(mHalDriver.getProperties()).thenReturn(TestUtil.createDefaultProperties(false)); + when(mHalDriver.getProperties()).thenReturn(TestUtil.createDefaultProperties()); mService = new SoundTriggerMiddlewareImpl(() -> mHalDriver, mAudioSessionProvider); } @@ -156,7 +156,7 @@ public class SoundTriggerMiddlewareImplTest { assertEquals(1, allDescriptors.length); Properties properties = allDescriptors[0].properties; - assertEquals(TestUtil.createDefaultProperties(false), properties); + assertEquals(TestUtil.createDefaultProperties(), properties); } @Test diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java index 30b4a59b32b1..39561f74d7ed 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java @@ -162,8 +162,8 @@ class TestUtil { phrases.get(0).recognitionModes); } - static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties_2_0( - boolean supportConcurrentCapture) { + static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties + createDefaultProperties_2_0() { android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties = new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties(); properties.implementor = "implementor"; @@ -185,17 +185,16 @@ class TestUtil { | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER; properties.captureTransition = true; properties.maxBufferMs = 321; - properties.concurrentCapture = supportConcurrentCapture; + properties.concurrentCapture = true; properties.triggerInEvent = true; properties.powerConsumptionMw = 432; return properties; } - static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3( - boolean supportConcurrentCapture) { + static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3() { android.hardware.soundtrigger.V2_3.Properties properties = new android.hardware.soundtrigger.V2_3.Properties(); - properties.base = createDefaultProperties_2_0(supportConcurrentCapture); + properties.base = createDefaultProperties_2_0(); properties.supportedModelArch = "supportedModelArch"; properties.audioCapabilities = android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION @@ -203,7 +202,7 @@ class TestUtil { return properties; } - static Properties createDefaultProperties(boolean supportConcurrentCapture) { + static Properties createDefaultProperties() { Properties properties = new Properties(); properties.implementor = "implementor"; properties.description = "description"; @@ -217,7 +216,7 @@ class TestUtil { | RecognitionMode.USER_AUTHENTICATION | RecognitionMode.GENERIC_TRIGGER; properties.captureTransition = true; properties.maxBufferMs = 321; - properties.concurrentCapture = supportConcurrentCapture; + properties.concurrentCapture = true; properties.triggerInEvent = true; properties.powerConsumptionMw = 432; properties.supportedModelArch = "supportedModelArch"; @@ -226,13 +225,13 @@ class TestUtil { return properties; } - static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture) { - validateDefaultProperties(properties, supportConcurrentCapture, + static void validateDefaultProperties(Properties properties) { + validateDefaultProperties(properties, AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION, "supportedModelArch"); } - static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture, + static void validateDefaultProperties(Properties properties, @AudioCapabilities int audioCapabilities, @NonNull String supportedModelArch) { assertEquals("implementor", properties.implementor); assertEquals("description", properties.description); @@ -246,7 +245,7 @@ class TestUtil { properties.recognitionModes); assertTrue(properties.captureTransition); assertEquals(321, properties.maxBufferMs); - assertEquals(supportConcurrentCapture, properties.concurrentCapture); + assertEquals(true, properties.concurrentCapture); assertTrue(properties.triggerInEvent); assertEquals(432, properties.powerConsumptionMw); assertEquals(supportedModelArch, properties.supportedModelArch); diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index bfdffc0e6567..20486b3e396d 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -429,18 +429,40 @@ public class SystemConfigTest { public void readPermissions_allowLibs_parsesSimpleLibrary() throws IOException { String contents = "<permissions>\n" - + " <library \n" - + " name=\"foo\"\n" - + " file=\"" + mFooJar + "\"\n" - + " on-bootclasspath-before=\"10\"\n" - + " on-bootclasspath-since=\"20\"\n" - + " />\n\n" - + " </permissions>"; + + " <library \n" + + " name=\"foo\"\n" + + " file=\"" + mFooJar + "\"\n" + + " on-bootclasspath-before=\"10\"\n" + + " on-bootclasspath-since=\"20\"\n" + + " />\n\n" + + " </permissions>"; parseSharedLibraries(contents); assertFooIsOnlySharedLibrary(); SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo"); - assertThat(entry.onBootclasspathBefore).isEqualTo(10); - assertThat(entry.onBootclasspathSince).isEqualTo(20); + assertThat(entry.onBootclasspathBefore).isEqualTo("10"); + assertThat(entry.onBootclasspathSince).isEqualTo("20"); + } + + /** + * Tests that readPermissions works correctly for a library with on-bootclasspath-before + * and on-bootclasspath-since that uses codenames. + */ + @Test + public void readPermissions_allowLibs_parsesSimpleLibraryWithCodenames() throws IOException { + String contents = + "<permissions>\n" + + " <library \n" + + " name=\"foo\"\n" + + " file=\"" + mFooJar + "\"\n" + + " on-bootclasspath-before=\"Q\"\n" + + " on-bootclasspath-since=\"W\"\n" + + " />\n\n" + + " </permissions>"; + parseSharedLibraries(contents); + assertFooIsOnlySharedLibrary(); + SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo"); + assertThat(entry.onBootclasspathBefore).isEqualTo("Q"); + assertThat(entry.onBootclasspathSince).isEqualTo("W"); } /** @@ -461,8 +483,8 @@ public class SystemConfigTest { parseSharedLibraries(contents); assertFooIsOnlySharedLibrary(); SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo"); - assertThat(entry.onBootclasspathBefore).isEqualTo(10); - assertThat(entry.onBootclasspathSince).isEqualTo(20); + assertThat(entry.onBootclasspathBefore).isEqualTo("10"); + assertThat(entry.onBootclasspathSince).isEqualTo("20"); } /** @@ -543,12 +565,20 @@ public class SystemConfigTest { */ @Test public void readPermissions_allowLibs_allowsCurrentMaxSdk() throws IOException { + // depending on whether this test is running before or after finalization, we need to + // pass a different parameter + String parameter; + if ("REL".equals(Build.VERSION.CODENAME)) { + parameter = "" + Build.VERSION.SDK_INT; + } else { + parameter = "ZZZ"; + } String contents = "<permissions>\n" + " <library \n" + " name=\"foo\"\n" + " file=\"" + mFooJar + "\"\n" - + " max-device-sdk=\"" + Build.VERSION.SDK_INT + "\"\n" + + " max-device-sdk=\"" + parameter + "\"\n" + " />\n\n" + " </permissions>"; parseSharedLibraries(contents); diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java index 4ed4c236535f..37c95f735d89 100644 --- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java @@ -17,6 +17,7 @@ package com.android.server.utils; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -860,6 +861,54 @@ public class WatcherTest { } } + @Test + public void testWatchedSparseSetArray() { + final String name = "WatchedSparseSetArray"; + WatchableTester tester; + + // Test WatchedSparseSetArray + WatchedSparseSetArray array = new WatchedSparseSetArray(); + tester = new WatchableTester(array, name); + tester.verify(0, "Initial array - no registration"); + array.add(INDEX_A, 1); + tester.verify(0, "Updates with no registration"); + tester.register(); + tester.verify(0, "Updates with no registration"); + array.add(INDEX_B, 2); + tester.verify(1, "Updates with registration"); + array.add(INDEX_B, 4); + array.add(INDEX_C, 5); + tester.verify(3, "Updates with registration"); + // Special methods + assertTrue(array.remove(INDEX_C, 5)); + tester.verify(4, "Removed 5 from key 3"); + array.remove(INDEX_B); + tester.verify(5, "Removed everything for key 2"); + + // Snapshot + { + WatchedSparseSetArray arraySnap = (WatchedSparseSetArray) array.snapshot(); + tester.verify(5, "Generate snapshot"); + // Verify that the snapshot is a proper copy of the source. + assertEquals("WatchedSparseSetArray snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + ArraySet set = array.get(array.keyAt(i)); + ArraySet setSnap = arraySnap.get(arraySnap.keyAt(i)); + assertNotNull(set); + assertTrue(set.equals(setSnap)); + } + array.add(INDEX_D, 9); + tester.verify(6, "Tick after snapshot"); + // Verify that the array is sealed + verifySealed(name, ()->arraySnap.add(INDEX_D, 10)); + assertTrue(!array.isSealed()); + assertTrue(arraySnap.isSealed()); + } + array.clear(); + tester.verify(7, "Cleared all entries"); + } + private static class IndexGenerator { private final int mSeed; private final Random mRandom; @@ -1084,6 +1133,18 @@ public class WatcherTest { assertEquals(a.equals(s), true); a.put(rowIndex, colIndex, !a.get(rowIndex, colIndex)); assertEquals(a.equals(s), false); + + // Verify copy-in/out + { + final String msg = name + " copy"; + WatchedSparseBooleanMatrix copy = new WatchedSparseBooleanMatrix(); + copy.copyFrom(matrix); + final int end = copy.size(); + assertTrue(msg + " size mismatch " + end + " " + matrix.size(), end == matrix.size()); + for (int i = 0; i < end; i++) { + assertEquals(copy.keyAt(i), keys[i]); + } + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index f3d494d160f2..4fbf0065f78d 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -602,10 +602,15 @@ public class VibratorManagerServiceTest { VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK); VibratorManagerService service = createSystemReadyService(); mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); + + // The haptic feedback should be ignored in low power, but not the ringtone. The end + // of the test asserts which actual effects ended up playing. vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS); assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1, service, TEST_TIMEOUT_MILLIS)); + // Allow the ringtone to complete, as the other vibrations won't cancel it. + assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), @@ -815,6 +820,29 @@ public class VibratorManagerServiceTest { } @Test + public void vibrate_withOngoingRingtoneVibration_ignoresEffect() throws Exception { + mockVibrators(1); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + VibratorManagerService service = createSystemReadyService(); + + VibrationEffect alarmEffect = VibrationEffect.createWaveform( + new long[]{10_000, 10_000}, new int[]{128, 255}, -1); + vibrate(service, alarmEffect, new VibrationAttributes.Builder().setUsage( + VibrationAttributes.USAGE_RINGTONE).build()); + + // VibrationThread will start this vibration async, so wait before checking it started. + assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), + service, TEST_TIMEOUT_MILLIS)); + + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), + HAPTIC_FEEDBACK_ATTRS); + + // Wait before checking it never played a second effect. + assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1, + service, /* timeout= */ 50)); + } + + @Test public void vibrate_withInputDevices_vibratesInputDevices() throws Exception { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 9902e83c3648..908de34352c9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -1139,18 +1139,8 @@ public class ActivityStarterTests extends WindowTestsBase { true /* createdByOrganizer */); sourceRecord.getTask().addChild(taskFragment, POSITION_TOP); - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* inTaskFragment */ taskFragment, - /* restrictedBgActivity */false, - /* intentGrants */null); + startActivityInner(starter, targetRecord, sourceRecord, null /* options */, + null /* inTask */, taskFragment); assertFalse(taskFragment.hasChild()); } @@ -1167,18 +1157,8 @@ public class ActivityStarterTests extends WindowTestsBase { taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class), SYSTEM_UID, "system_uid"); - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* inTaskFragment */ taskFragment, - /* restrictedBgActivity */false, - /* intentGrants */null); + startActivityInner(starter, targetRecord, sourceRecord, null /* options */, + null /* inTask */, taskFragment); assertTrue(taskFragment.hasChild()); } @@ -1195,18 +1175,8 @@ public class ActivityStarterTests extends WindowTestsBase { taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class), targetRecord.getUid(), "test_process_name"); - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* inTaskFragment */ taskFragment, - /* restrictedBgActivity */false, - /* intentGrants */null); + startActivityInner(starter, targetRecord, sourceRecord, null /* options */, + null /* inTask */, taskFragment); assertTrue(taskFragment.hasChild()); } @@ -1231,18 +1201,8 @@ public class ActivityStarterTests extends WindowTestsBase { doReturn(true).when(signingDetails).hasAncestorOrSelfWithDigest(any()); doReturn(signingDetails).when(androidPackage).getSigningDetails(); - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* inTaskFragment */ taskFragment, - /* restrictedBgActivity */false, - /* intentGrants */null); + startActivityInner(starter, targetRecord, sourceRecord, null /* options */, + null /* inTask */, taskFragment); assertTrue(taskFragment.hasChild()); } @@ -1258,23 +1218,30 @@ public class ActivityStarterTests extends WindowTestsBase { targetRecord.info.flags |= ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING; - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* inTaskFragment */ taskFragment, - /* restrictedBgActivity */false, - /* intentGrants */null); + startActivityInner(starter, targetRecord, sourceRecord, null /* options */, + null /* inTask */, taskFragment); assertTrue(taskFragment.hasChild()); } @Test + public void testStartActivityInner_inTask() { + final ActivityStarter starter = prepareStarter(0, false); + // Simulate an app uses AppTask to create a non-attached task, and then it requests to + // start activity in the task. + final Task inTask = new TaskBuilder(mSupervisor).setTaskDisplayArea(null).setTaskId(123) + .build(); + inTask.inRecents = true; + assertFalse(inTask.isAttached()); + final ActivityRecord target = new ActivityBuilder(mAtm).build(); + startActivityInner(starter, target, null /* source */, null /* options */, inTask, + null /* inTaskFragment */); + + assertTrue(inTask.isAttached()); + assertEquals(inTask, target.getTask()); + } + + @Test public void testLaunchCookie_newAndExistingTask() { final ActivityStarter starter = prepareStarter(0, false); @@ -1322,21 +1289,20 @@ public class ActivityStarterTests extends WindowTestsBase { // Start the target launch-into-pip activity from a source final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build(); - starter.startActivityInner( - /* r */ targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */ null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */ true, - /* options */ opts, - /* inTask */ null, - /* inTaskFragment */ null, - /* restrictedBgActivity */ false, - /* intentGrants */ null); + startActivityInner(starter, targetRecord, sourceRecord, opts, + null /* inTask */, null /* inTaskFragment */); // Verify the ActivityRecord#getLaunchIntoPipHostActivity points to sourceRecord. assertThat(targetRecord.getLaunchIntoPipHostActivity()).isNotNull(); assertEquals(targetRecord.getLaunchIntoPipHostActivity(), sourceRecord); } + + private static void startActivityInner(ActivityStarter starter, ActivityRecord target, + ActivityRecord source, ActivityOptions options, Task inTask, + TaskFragment inTaskFragment) { + starter.startActivityInner(target, source, null /* voiceSession */, + null /* voiceInteractor */, 0 /* startFlags */, true /* doResume */, + options, inTask, inTaskFragment, false /* restrictedBgActivity */, + null /* intentGrants */); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 605726c0e804..118f159bee7b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1201,6 +1201,20 @@ public class DisplayContentTests extends WindowTestsBase { dc.computeImeControlTarget()); } + @UseTestDisplay(addWindows = W_INPUT_METHOD) + @Test + public void testImeSecureFlagGetUpdatedAfterImeInputTarget() { + // Verify IME window can get up-to-date secure flag update when the IME input target + // set before setCanScreenshot called. + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + SurfaceControl.Transaction t = mDisplayContent.mInputMethodWindow.getPendingTransaction(); + spyOn(t); + mDisplayContent.setImeInputTarget(app); + mDisplayContent.mInputMethodWindow.setCanScreenshot(t, false /* canScreenshot */); + + verify(t).setSecure(eq(mDisplayContent.mInputMethodWindow.mSurfaceControl), eq(true)); + } + @UseTestDisplay(addWindows = W_ACTIVITY) @Test public void testComputeImeControlTarget_notMatchParentBounds() throws Exception { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java index f9689990c5e8..a82826006f17 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.START_ABORTED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; @@ -26,6 +27,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; import android.app.WindowConfiguration; import android.content.ComponentName; @@ -45,13 +47,13 @@ import java.util.List; import java.util.Set; /** - * Tests for the {@link DisplayWindowPolicyControllerHelper} class. + * Tests for the {@link DisplayWindowPolicyController} class. * * Build/Install/Run: - * atest WmTests:DisplayWindowPolicyControllerHelperTests + * atest WmTests:DisplayWindowPolicyControllerTests */ @RunWith(WindowTestRunner.class) -public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase { +public class DisplayWindowPolicyControllerTests extends WindowTestsBase { private static final int TEST_USER_0_ID = 0; private static final int TEST_USER_1_ID = 10; @@ -152,8 +154,51 @@ public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase { assertTrue(mSecondaryDisplay.mDwpcHelper.isWindowingModeSupported(WINDOWING_MODE_PINNED)); } + @Test + public void testInterestedWindowFlags() { + final int fakeFlag1 = 0x00000010; + final int fakeFlag2 = 0x00000100; + final int fakeSystemFlag1 = 0x00000010; + final int fakeSystemFlag2 = 0x00000100; + + mDwpc.setInterestedWindowFlags(fakeFlag1, fakeSystemFlag1); + + assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag1, fakeSystemFlag1)); + assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag1, fakeSystemFlag2)); + assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag2, fakeSystemFlag1)); + assertFalse(mDwpc.isInterestedWindowFlags(fakeFlag2, fakeSystemFlag2)); + } + + @Test + public void testCanContainActivities() { + ActivityStarter starter = new ActivityStarter(mock(ActivityStartController.class), mAtm, + mSupervisor, mock(ActivityStartInterceptor.class)); + final Task task = new TaskBuilder(mSupervisor).setDisplay(mSecondaryDisplay).build(); + final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord disallowedRecord = + new ActivityBuilder(mAtm).setComponent(mDwpc.DISALLOWED_ACTIVITY).build(); + + int result = starter.startActivityInner( + disallowedRecord, + sourceRecord, + /* voiceSession */null, + /* voiceInteractor */ null, + /* startFlags */ 0, + /* doResume */true, + /* options */null, + /* inTask */null, + /* inTaskFragment */ null, + /* restrictedBgActivity */false, + /* intentGrants */null); + + assertEquals(result, START_ABORTED); + } + private class TestDisplayWindowPolicyController extends DisplayWindowPolicyController { + public ComponentName DISALLOWED_ACTIVITY = + new ComponentName("fake.package", "DisallowedActivity"); + ComponentName mTopActivity = null; int mTopActivityUid = UserHandle.USER_NULL; ArraySet<Integer> mRunningUids = new ArraySet<>(); @@ -161,7 +206,14 @@ public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase { @Override public boolean canContainActivities(@NonNull List<ActivityInfo> activities, @WindowConfiguration.WindowingMode int windowingMode) { - return false; + final int activityCount = activities.size(); + for (int i = 0; i < activityCount; i++) { + final ActivityInfo aInfo = activities.get(i); + if (aInfo.getComponentName().equals(DISALLOWED_ACTIVITY)) { + return false; + } + } + return true; } @Override diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index c86f38d4264d..8d4a0176e3be 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -119,13 +119,6 @@ final class HotwordDetectionConnection { private static final Duration MAX_UPDATE_TIMEOUT_DURATION = Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS); private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour - /** - * Time after which each HotwordDetectionService process is stopped and replaced by a new one. - * 0 indicates no restarts. - */ - private static final int RESTART_PERIOD_SECONDS = - DeviceConfig.getInt(DeviceConfig.NAMESPACE_VOICE_INTERACTION, - KEY_RESTART_PERIOD_IN_SECONDS, 0); private static final int MAX_ISOLATED_PROCESS_NUMBER = 10; // Hotword metrics @@ -150,6 +143,11 @@ final class HotwordDetectionConnection { private final @NonNull ServiceConnectionFactory mServiceConnectionFactory; private final IHotwordRecognitionStatusCallback mCallback; private final int mDetectorType; + /** + * Time after which each HotwordDetectionService process is stopped and replaced by a new one. + * 0 indicates no restarts. + */ + private final int mReStartPeriodSeconds; final Object mLock; final int mVoiceInteractionServiceUid; @@ -195,6 +193,8 @@ final class HotwordDetectionConnection { mUser = userId; mCallback = callback; mDetectorType = detectorType; + mReStartPeriodSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_VOICE_INTERACTION, + KEY_RESTART_PERIOD_IN_SECONDS, 0); final Intent intent = new Intent(HotwordDetectionService.SERVICE_INTERFACE); intent.setComponent(mDetectionComponentName); initAudioFlingerLocked(); @@ -206,11 +206,11 @@ final class HotwordDetectionConnection { mLastRestartInstant = Instant.now(); updateStateAfterProcessStart(options, sharedMemory); - if (RESTART_PERIOD_SECONDS <= 0) { + if (mReStartPeriodSeconds <= 0) { mCancellationTaskFuture = null; } else { - // TODO(volnov): we need to be smarter here, e.g. schedule it a bit more often, but wait - // until the current session is closed. + // TODO: we need to be smarter here, e.g. schedule it a bit more often, + // but wait until the current session is closed. mCancellationTaskFuture = mScheduledExecutorService.scheduleAtFixedRate(() -> { Slog.v(TAG, "Time to restart the process, TTL has passed"); synchronized (mLock) { @@ -218,7 +218,7 @@ final class HotwordDetectionConnection { HotwordMetricsLogger.writeServiceRestartEvent(mDetectorType, HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE); } - }, RESTART_PERIOD_SECONDS, RESTART_PERIOD_SECONDS, TimeUnit.SECONDS); + }, mReStartPeriodSeconds, mReStartPeriodSeconds, TimeUnit.SECONDS); } } @@ -785,7 +785,7 @@ final class HotwordDetectionConnection { } public void dump(String prefix, PrintWriter pw) { - pw.print(prefix); pw.print("RESTART_PERIOD_SECONDS="); pw.println(RESTART_PERIOD_SECONDS); + pw.print(prefix); pw.print("mReStartPeriodSeconds="); pw.println(mReStartPeriodSeconds); pw.print(prefix); pw.print("mBound=" + mRemoteHotwordDetectionService.isBound()); pw.print(", mValidatingDspTrigger=" + mValidatingDspTrigger); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 8d7fab4d101f..a2266fb3a929 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1959,6 +1959,13 @@ public class CarrierConfigManager { "nr_advanced_threshold_bandwidth_khz_int"; /** + * Boolean indicating if operator name should be shown in the status bar + * @hide + */ + public static final String KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL = + "show_operator_name_in_statusbar_bool"; + + /** * The string is used to filter redundant string from PLMN Network Name that's supplied by * specific carrier. * @@ -8913,6 +8920,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putBoolean(KEY_WORLD_MODE_ENABLED_BOOL, false); sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING, ""); + sDefaults.putBoolean(KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false); sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false); sDefaults.putBoolean(KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL, false); diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java index 3843a6240b43..249f740c482b 100644 --- a/telephony/java/android/telephony/UiccCardInfo.java +++ b/telephony/java/android/telephony/UiccCardInfo.java @@ -21,6 +21,9 @@ import android.content.pm.PackageManager; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.telephony.util.TelephonyUtils; +import com.android.telephony.Rlog; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -227,7 +230,6 @@ public final class UiccCardInfo implements Parcelable { this.mIccIdAccessRestricted = iccIdAccessRestricted; } - @Override public boolean equals(Object obj) { if (this == obj) { @@ -261,7 +263,7 @@ public final class UiccCardInfo implements Parcelable { + ", mCardId=" + mCardId + ", mEid=" - + mEid + + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid) + ", mPhysicalSlotIndex=" + mPhysicalSlotIndex + ", mIsRemovable=" diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java index 17ce45063d41..dd3639a9a7b9 100644 --- a/telephony/java/android/telephony/UiccSlotInfo.java +++ b/telephony/java/android/telephony/UiccSlotInfo.java @@ -279,7 +279,7 @@ public class UiccSlotInfo implements Parcelable { + ", mIsEuicc=" + mIsEuicc + ", mCardId=" - + mCardId + + SubscriptionInfo.givePrintableIccid(mCardId) + ", cardState=" + mCardStateInfo + ", mIsExtendedApduSupported=" diff --git a/tests/FlickerTests/OWNERS b/tests/FlickerTests/OWNERS index c1221e3940d2..d40ff5659708 100644 --- a/tests/FlickerTests/OWNERS +++ b/tests/FlickerTests/OWNERS @@ -1,4 +1,4 @@ -# Bug component: 909476 +# Bug component: 1157642 include /services/core/java/com/android/server/wm/OWNERS natanieljr@google.com pablogamito@google.com diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt new file mode 100644 index 000000000000..172c4330c3c6 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.helpers + +import android.app.Instrumentation +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.traces.common.FlickerComponentName +import com.android.server.wm.traces.parser.toFlickerComponent +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper + +class ImeEditorPopupDialogAppHelper @JvmOverloads constructor( + instr: Instrumentation, + private val rotation: Int, + private val imePackageName: String = IME_PACKAGE, + launcherName: String = ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME, + component: FlickerComponentName = + ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME.toFlickerComponent() +) : ImeAppHelper(instr, launcherName, component) { + override fun openIME( + device: UiDevice, + wmHelper: WindowManagerStateHelper? + ) { + val editText = device.wait(Until.findObject(By.text("focused editText")), FIND_TIMEOUT) + + require(editText != null) { + "Text field not found, this usually happens when the device " + + "was left in an unknown state (e.g. in split screen)" + } + editText.click() + waitIMEShown(device, wmHelper) + } + + fun dismissDialog(wmHelper: WindowManagerStateHelper) { + val dismissButton = uiDevice.wait( + Until.findObject(By.text("Dismiss")), FIND_TIMEOUT) + + // Pressing back key to dismiss the dialog + if (dismissButton != null) { + dismissButton.click() + wmHelper.waitForAppTransitionIdle() + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt new file mode 100644 index 000000000000..bff099e2e7a1 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.ime + +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit +import android.view.Surface +import android.view.WindowManagerPolicyConstants +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.annotation.Group4 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper +import com.android.server.wm.flicker.navBarWindowIsVisible +import com.android.server.wm.flicker.statusBarWindowIsVisible +import com.android.server.wm.flicker.traces.region.RegionSubject +import com.android.server.wm.traces.common.FlickerComponentName +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group4 +class CloseImeEditorPopupDialogTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation, testSpec.startRotation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + setup { + eachRun { + imeTestApp.launchViaIntent(wmHelper) + imeTestApp.openIME(device, wmHelper) + } + } + transitions { + imeTestApp.dismissDialog(wmHelper) + instrumentation.uiAutomation.syncInputTransactions() + } + teardown { + eachRun { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + imeTestApp.exit() + } + } + } + } + + @Postsubmit + @Test + fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() + + @Postsubmit + @Test + fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() + + @Postsubmit + @Test + fun imeWindowBecameInvisible() = testSpec.imeWindowBecomesInvisible() + + @Postsubmit + @Test + fun imeLayerAndImeSnapshotVisibleOnScreen() { + testSpec.assertLayers { + this.isVisible(FlickerComponentName.IME) + .then() + .isVisible(FlickerComponentName.IME_SNAPSHOT) + .then() + .isInvisible(FlickerComponentName.IME) + } + } + + @Postsubmit + @Test + fun imeSnapshotAssociatedOnAppVisibleRegion() { + testSpec.assertLayers { + this.invoke("imeSnapshotAssociatedOnAppVisibleRegion") { + val imeSnapshotLayers = it.subjects.filter { + subject -> subject.name.contains( + FlickerComponentName.IME_SNAPSHOT.toLayerName()) && subject.isVisible + } + if (imeSnapshotLayers.isNotEmpty()) { + val visibleAreas = imeSnapshotLayers.mapNotNull { imeSnapshotLayer -> + imeSnapshotLayer.layer?.visibleRegion }.toTypedArray() + val imeVisibleRegion = RegionSubject.assertThat(visibleAreas, this, timestamp) + val appVisibleRegion = it.visibleRegion(imeTestApp.component) + if (imeVisibleRegion.region.isNotEmpty) { + imeVisibleRegion.coversAtMost(appVisibleRegion.region) + } + } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + repetitions = 2, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ), + supportedRotations = listOf(Surface.ROTATION_0) + ) + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt index 7f4966375b98..1b60403ac354 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt @@ -79,7 +79,7 @@ class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestPar /** * Checks that [FlickerComponentName.IME] layer is visible at the end of the transition */ - @Presubmit + @FlakyTest(bugId = 227142436) @Test fun imeLayerExistsEnd() { testSpec.assertLayersEnd { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt index 9c9dedc23ff9..4b8a8c80cd45 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt @@ -17,6 +17,7 @@ package com.android.server.wm.flicker.quickswitch import android.platform.test.annotations.RequiresDevice +import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.annotation.Group1 @@ -44,6 +45,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group1 +@FlakyTest(bugId = 228009808) open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit(testSpec: FlickerTestParameter) : QuickSwitchBetweenTwoAppsForwardTest(testSpec) { @Before diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 739fe020d555..7f513b21957f 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -119,5 +119,16 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + <activity android:name=".ImeEditorPopupDialogActivity" + android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity" + android:configChanges="orientation|screenSize" + android:theme="@style/CutoutShortEdges" + android:label="ImeEditorPopupDialogActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> </application> </manifest> diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java index 3040a09f2345..18c95cf7bbff 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java @@ -56,6 +56,7 @@ public class ActivityOptions { public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME = new ComponentName(FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity"); + public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity"; public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME = new ComponentName(FLICKER_APP_PACKAGE, @@ -65,4 +66,10 @@ public class ActivityOptions { public static final ComponentName PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME = new ComponentName(FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".PortraitOnlyActivity"); + + public static final String EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME = + "ImeEditorPopupDialogActivity"; + public static final ComponentName EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME = + new ComponentName(FLICKER_APP_PACKAGE, + FLICKER_APP_PACKAGE + ".ImeEditorPopupDialogActivity"); } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java new file mode 100644 index 000000000000..a8613f531e1c --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.testapp; + +import android.app.Activity; +import android.app.AlertDialog; +import android.os.Bundle; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.LinearLayout; + +public class ImeEditorPopupDialogActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + WindowManager.LayoutParams p = getWindow().getAttributes(); + p.layoutInDisplayCutoutMode = WindowManager.LayoutParams + .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + getWindow().setAttributes(p); + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + setContentView(R.layout.activity_simple); + + final EditText editText = new EditText(this); + editText.setHint("focused editText"); + final AlertDialog dialog = new AlertDialog.Builder(this) + .setView(editText) + .setPositiveButton("Dismiss", (d, which) -> d.dismiss()) + .create(); + dialog.show(); + } +} diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index bd0a4bc44e18..bfb32854a374 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -165,6 +165,7 @@ cc_library_host_static { ], proto: { export_proto_headers: true, + type: "full", }, defaults: ["aapt2_defaults"], } |