diff options
191 files changed, 4982 insertions, 1526 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/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/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/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/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/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/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 dd69fa0bebb2..776d3dafe8c0 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4754,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/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/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/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/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/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/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/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 05b2c5055e44..3a727ba7b70c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -681,7 +681,6 @@ public class MediaControlPanel { button.setOnClickListener(v -> { if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId); - mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId); logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT); action.run(); 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/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/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index f5abe28914c3..40265a367a06 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -143,6 +143,7 @@ public class NavigationBarView extends FrameLayout { private boolean mDeadZoneConsuming = false; private final NavigationBarTransitions mBarTransitions; private final OverviewProxyService mOverviewProxyService; + @Nullable private AutoHideController mAutoHideController; // performs manual animation in sync with layout transitions @@ -316,7 +317,7 @@ public class NavigationBarView extends FrameLayout { }; private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> { - if (visible) { + if (visible && mAutoHideController != null) { mAutoHideController.touchAutoHide(); } notifyActiveTouchRegions(); 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 08249a3d493e..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; } } @@ -476,7 +476,7 @@ public class PeopleSpaceWidgetManager { updateWidgetIdsBasedOnNotifications(tilesUpdated, notifications); } } catch (Exception e) { - Log.e(TAG, "Throwing exception: " + e); + Log.e(TAG, "updateWidgetsWithNotificationChangedInBackground failing", e); } } @@ -499,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); } } @@ -851,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( @@ -959,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); } } @@ -1042,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) { @@ -1091,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) { @@ -1301,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; @@ -1311,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; } @@ -1342,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/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/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/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/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 7dd1ebddced7..597c949168d4 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 @@ -33,6 +33,7 @@ import android.os.Bundle; import android.os.Parcelable; import android.os.UserHandle; import android.provider.Settings; +import android.telephony.SubscriptionManager; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; @@ -70,6 +71,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; @@ -116,6 +120,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; @@ -138,6 +143,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, @@ -154,6 +181,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue NetworkController networkController, StatusBarStateController statusBarStateController, CommandQueue commandQueue, + CarrierConfigTracker carrierConfigTracker, CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, OperatorNameViewController.Factory operatorNameViewControllerFactory, SecureSettings secureSettings, @@ -173,6 +201,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mNetworkController = networkController; mStatusBarStateController = statusBarStateController; mCommandQueue = commandQueue; + mCarrierConfigTracker = carrierConfigTracker; mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger; mOperatorNameViewControllerFactory = operatorNameViewControllerFactory; mSecureSettings = secureSettings; @@ -213,6 +242,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue initNotificationIconArea(); mSystemEventAnimator = new StatusBarSystemEventAnimator(mSystemIconArea, getResources()); + mCarrierConfigTracker.addCallback(mCarrierConfigCallback); + mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); } @VisibleForTesting @@ -288,6 +319,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. */ @@ -574,11 +607,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/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/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index 91167161ac75..538a9c763438 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -105,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 @@ -242,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) 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 542e0d31e7a8..98397fb01b6b 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 @@ -60,6 +60,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; @@ -91,6 +92,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; @@ -375,6 +377,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/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/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index 76df8b9f84e8..e78c8d1ddcac 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -24,8 +24,10 @@ import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT; +import android.annotation.NonNull; import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; +import android.app.backup.BackupAgent; import android.app.backup.BackupManager; import android.app.backup.FullBackup; import android.app.backup.IBackupManagerMonitor; @@ -38,10 +40,12 @@ import android.content.pm.Signature; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.provider.Settings; +import android.system.OsConstants; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupRestoreTask; @@ -57,6 +61,7 @@ import com.android.server.backup.utils.FullBackupRestoreObserverUtils; import com.android.server.backup.utils.RestoreUtils; import com.android.server.backup.utils.TarBackupReader; +import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -135,6 +140,8 @@ public class FullRestoreEngine extends RestoreEngine { private boolean mPipesClosed; private final BackupEligibilityRules mBackupEligibilityRules; + private FileMetadata mReadOnlyParent = null; + public FullRestoreEngine( UserBackupManagerService backupManagerService, OperationStorage operationStorage, BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer, @@ -158,6 +165,22 @@ public class FullRestoreEngine extends RestoreEngine { mBackupEligibilityRules = backupEligibilityRules; } + @VisibleForTesting + FullRestoreEngine() { + mIsAdbRestore = false; + mAllowApks = false; + mEphemeralOpToken = 0; + mUserId = 0; + mBackupEligibilityRules = null; + mAgentTimeoutParameters = null; + mBuffer = null; + mBackupManagerService = null; + mOperationStorage = null; + mMonitor = null; + mMonitorTask = null; + mOnlyPackage = null; + } + public IBackupAgent getAgent() { return mAgent; } @@ -397,6 +420,11 @@ public class FullRestoreEngine extends RestoreEngine { okay = false; } + if (shouldSkipReadOnlyDir(info)) { + // b/194894879: We don't support restore of read-only dirs. + okay = false; + } + // At this point we have an agent ready to handle the full // restore data as well as a pipe for sending data to // that agent. Tell the agent to start reading from the @@ -573,6 +601,45 @@ public class FullRestoreEngine extends RestoreEngine { return (info != null); } + boolean shouldSkipReadOnlyDir(FileMetadata info) { + if (isValidParent(mReadOnlyParent, info)) { + // This file has a read-only parent directory, we shouldn't + // restore it. + return true; + } else { + // We're now in a different branch of the file tree, update the parent + // value. + if (isReadOnlyDir(info)) { + // Current directory is read-only. Remember it so that we can skip all + // of its contents. + mReadOnlyParent = info; + Slog.w(TAG, "Skipping restore of " + info.path + " and its contents as " + + "read-only dirs are currently not supported."); + return true; + } else { + mReadOnlyParent = null; + } + } + + return false; + } + + private static boolean isValidParent(FileMetadata parentDir, @NonNull FileMetadata childDir) { + return parentDir != null + && childDir.packageName.equals(parentDir.packageName) + && childDir.domain.equals(parentDir.domain) + && childDir.path.startsWith(getPathWithTrailingSeparator(parentDir.path)); + } + + private static String getPathWithTrailingSeparator(String path) { + return path.endsWith(File.separator) ? path : path + File.separator; + } + + private static boolean isReadOnlyDir(FileMetadata file) { + // Check if owner has 'write' bit in the file's mode value (see 'man -7 inode' for details). + return file.type == BackupAgent.TYPE_DIRECTORY && (file.mode & OsConstants.S_IWUSR) == 0; + } + private void setUpPipes() throws IOException { synchronized (mPipesLock) { mPipes = ParcelFileDescriptor.createPipe(); 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/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/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/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/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/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/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/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/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/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/restore/FullRestoreEngineTest.java b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java new file mode 100644 index 000000000000..049c745fc128 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java @@ -0,0 +1,150 @@ +/* + * 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.backup.restore; + +import static com.google.common.truth.Truth.assertWithMessage; + +import android.app.backup.BackupAgent; +import android.platform.test.annotations.Presubmit; +import android.system.OsConstants; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.backup.FileMetadata; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class FullRestoreEngineTest { + private static final String DEFAULT_PACKAGE_NAME = "package"; + private static final String DEFAULT_DOMAIN_NAME = "domain"; + private static final String NEW_PACKAGE_NAME = "new_package"; + private static final String NEW_DOMAIN_NAME = "new_domain"; + + private FullRestoreEngine mRestoreEngine; + + @Before + public void setUp() { + mRestoreEngine = new FullRestoreEngine(); + } + + @Test + public void shouldSkipReadOnlyDir_skipsAllReadonlyDirsAndTheirChildren() { + // Create the file tree. + TestFile[] testFiles = new TestFile[] { + TestFile.dir("root"), + TestFile.file("root/auth_token"), + TestFile.dir("root/media"), + TestFile.file("root/media/picture1.png"), + TestFile.file("root/push_token.txt"), + TestFile.dir("root/read-only-dir-1").markReadOnly().expectSkipped(), + TestFile.dir("root/read-only-dir-1/writable-subdir").expectSkipped(), + TestFile.file("root/read-only-dir-1/writable-subdir/writable-file").expectSkipped(), + TestFile.dir("root/read-only-dir-1/writable-subdir/read-only-subdir-2") + .markReadOnly().expectSkipped(), + TestFile.file("root/read-only-dir-1/writable-file").expectSkipped(), + TestFile.file("root/random-stuff.txt"), + TestFile.dir("root/database"), + TestFile.file("root/database/users.db"), + TestFile.dir("root/read-only-dir-2").markReadOnly().expectSkipped(), + TestFile.file("root/read-only-dir-2/writable-file-1").expectSkipped(), + TestFile.file("root/read-only-dir-2/writable-file-2").expectSkipped(), + }; + + assertCorrectItemsAreSkipped(testFiles); + } + + @Test + public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSamePackage() { + TestFile[] testFiles = new TestFile[]{ + TestFile.dir("read-only-dir").markReadOnly().expectSkipped(), + TestFile.file("read-only-dir/file").expectSkipped(), + TestFile.file("read-only-dir/file-from-different-package") + .setPackage(NEW_PACKAGE_NAME), + }; + + assertCorrectItemsAreSkipped(testFiles); + } + + @Test + public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSameDomain() { + TestFile[] testFiles = new TestFile[]{ + TestFile.dir("read-only-dir").markReadOnly().expectSkipped(), + TestFile.file("read-only-dir/file").expectSkipped(), + TestFile.file("read-only-dir/file-from-different-domain") + .setDomain(NEW_DOMAIN_NAME), + }; + + assertCorrectItemsAreSkipped(testFiles); + } + + private void assertCorrectItemsAreSkipped(TestFile[] testFiles) { + // Verify all directories marked with .expectSkipped are skipped. + for (TestFile testFile : testFiles) { + boolean actualExcluded = mRestoreEngine.shouldSkipReadOnlyDir(testFile.mMetadata); + boolean expectedExcluded = testFile.mShouldSkip; + assertWithMessage(testFile.mMetadata.path).that(actualExcluded).isEqualTo( + expectedExcluded); + } + } + + private static class TestFile { + private final FileMetadata mMetadata; + private boolean mShouldSkip; + + static TestFile dir(String path) { + return new TestFile(path, BackupAgent.TYPE_DIRECTORY); + } + + static TestFile file(String path) { + return new TestFile(path, BackupAgent.TYPE_FILE); + } + + TestFile markReadOnly() { + mMetadata.mode = 0; + return this; + } + + TestFile expectSkipped() { + mShouldSkip = true; + return this; + } + + TestFile setPackage(String packageName) { + mMetadata.packageName = packageName; + return this; + } + + TestFile setDomain(String domain) { + mMetadata.domain = domain; + return this; + } + + private TestFile(String path, int type) { + FileMetadata metadata = new FileMetadata(); + metadata.path = path; + metadata.type = type; + metadata.packageName = DEFAULT_PACKAGE_NAME; + metadata.domain = DEFAULT_DOMAIN_NAME; + metadata.mode = OsConstants.S_IWUSR; // Mark as writable. + mMetadata = metadata; + } + } +} 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/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/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"], } |