diff options
129 files changed, 2930 insertions, 1660 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 1b9cf2648a3f..7393bcde13b6 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -27,7 +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.Disabled; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -282,15 +282,14 @@ public class AlarmManager { 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. + * 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. + * TODO (b/226439802): Either enable it in the next SDK or replace it with a better alternative. * @hide */ @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2) + @Disabled public static final long SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = 226439802L; @UnsupportedAppUsage 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 2ea8592e883e..7b77e86f4d6a 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -565,9 +565,6 @@ 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; @@ -612,8 +609,6 @@ public class AlarmManagerService extends SystemService { private static final boolean DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = true; - private static final boolean DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = true; - // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; @@ -701,14 +696,6 @@ 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; @@ -892,15 +879,6 @@ 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 @@ -971,15 +949,6 @@ 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(); @@ -1156,9 +1125,6 @@ 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(); @@ -2928,10 +2894,8 @@ public class AlarmManagerService extends SystemService { } 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)); + return CompatChanges.isChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, + packageName, UserHandle.of(userId)); } @NeverCompile // Avoid size overhead of debugging code. @@ -4707,7 +4671,6 @@ 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()); @@ -4827,32 +4790,6 @@ public class AlarmManagerService extends SystemService { removeExactAlarmsOnPermissionRevoked(uid, packageName, /*killUid = */false); } 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)) { - removeExactAlarmsOnPermissionRevoked(uid, pkg, true); - } - } else if (hasScheduleExactAlarmInternal(pkg, uid)) { - sendScheduleExactAlarmPermissionStateChangedBroadcast(pkg, - UserHandle.getUserId(uid)); - } - } - } - break; default: // nope, just ignore it break; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 36e1c941cbc6..fe99c71d9a9a 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -320,7 +320,6 @@ package android.app { method public void setDemoted(boolean); method public void setFgServiceShown(boolean); method public void setImportanceLockedByCriticalDeviceFunction(boolean); - method public void setImportanceLockedByOEM(boolean); method public void setImportantConversation(boolean); method public void setOriginalImportance(int); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index abd60177f884..5d1d225f4d2d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4617,8 +4617,8 @@ public class ActivityManager { try { getService().broadcastIntentWithFeature( null, null, intent, null, null, Activity.RESULT_OK, null, null, - null /*requiredPermissions*/, null /*excludedPermissions*/, appOp, null, false, - true, userId); + null /*requiredPermissions*/, null /*excludedPermissions*/, + null /*excludedPackages*/, appOp, null, false, true, userId); } catch (RemoteException ex) { } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index ac46066997ff..6d982ced385c 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1193,7 +1193,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, null, false, false, getUserId()); + null, AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1210,7 +1210,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1226,7 +1226,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1243,8 +1243,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false, - getUserId()); + null /*excludedPermissions=*/, null /*excludedPackages*/, + AppOpsManager.OP_NONE, options, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1259,7 +1259,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1268,7 +1268,7 @@ class ContextImpl extends Context { @Override public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions, - String[] excludedPermissions) { + String[] excludedPermissions, String[] excludedPackages) { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { @@ -1276,7 +1276,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, excludedPermissions, - AppOpsManager.OP_NONE, null, false, false, getUserId()); + excludedPackages, AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1303,7 +1303,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - excludedPermissions, AppOpsManager.OP_NONE, options, false, false, + excludedPermissions, null, AppOpsManager.OP_NONE, options, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1321,7 +1321,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, appOp, null, false, false, getUserId()); + null /*excludedPermissions=*/, null, appOp, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1338,7 +1338,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, false, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, true, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1402,7 +1402,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermissions, - null /*excludedPermissions=*/, appOp, options, true, false, getUserId()); + null /*excludedPermissions=*/, null, appOp, options, true, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1416,7 +1416,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); + null, AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1439,8 +1439,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false, - user.getIdentifier()); + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, options, false, + false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1457,7 +1457,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, appOp, null, false, false, user.getIdentifier()); + null /*excludedPermissions=*/, null, appOp, null, false, false, + user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1508,7 +1509,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermissions, - null /*excludedPermissions=*/, appOp, options, true, false, + null /*excludedPermissions=*/, null, appOp, options, true, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1550,7 +1551,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, null, false, true, getUserId()); + null, AppOpsManager.OP_NONE, null, false, true, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1589,7 +1590,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, options, false, true, getUserId()); + null, AppOpsManager.OP_NONE, options, false, true, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1625,7 +1626,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, true, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, true, true, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1658,7 +1659,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, null, false, true, user.getIdentifier()); + null, AppOpsManager.OP_NONE, null, false, true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1673,7 +1674,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, options, false, true, user.getIdentifier()); + null, AppOpsManager.OP_NONE, options, false, true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1708,7 +1709,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, true, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, true, true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 4efe9dfe7185..8367441b1b95 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -141,7 +141,7 @@ interface IActivityManager { int broadcastIntentWithFeature(in IApplicationThread caller, in String callingFeatureId, in Intent intent, in String resolvedType, in IIntentReceiver resultTo, int resultCode, in String resultData, in Bundle map, in String[] requiredPermissions, in String[] excludePermissions, - int appOp, in Bundle options, boolean serialized, boolean sticky, int userId); + in String[] excludePackages, int appOp, in Bundle options, boolean serialized, boolean sticky, int userId); void unbroadcastIntent(in IApplicationThread caller, in Intent intent, int userId); @UnsupportedAppUsage oneway void finishReceiver(in IBinder who, int resultCode, in String resultData, in Bundle map, diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index df9f2a3cbb25..da6a551175e3 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -77,6 +77,7 @@ interface INotificationManager boolean areNotificationsEnabledForPackage(String pkg, int uid); boolean areNotificationsEnabled(String pkg); int getPackageImportance(String pkg); + boolean isImportanceLocked(String pkg, int uid); List<String> getAllowedAssistantAdjustments(String pkg); void allowAssistantAdjustment(String adjustmentType); diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 6f0b03aeb6f3..c9cc1a179102 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -253,7 +253,6 @@ public final class NotificationChannel implements Parcelable { // If this is a blockable system notification channel. private boolean mBlockableSystem = false; private int mAllowBubbles = DEFAULT_ALLOW_BUBBLE; - private boolean mImportanceLockedByOEM; private boolean mImportanceLockedDefaultApp; private String mParentId = null; private String mConversationId = null; @@ -322,13 +321,13 @@ public final class NotificationChannel implements Parcelable { mLightColor = in.readInt(); mBlockableSystem = in.readBoolean(); mAllowBubbles = in.readInt(); - mImportanceLockedByOEM = in.readBoolean(); mOriginalImportance = in.readInt(); mParentId = in.readString(); mConversationId = in.readString(); mDemoted = in.readBoolean(); mImportantConvo = in.readBoolean(); mDeletedTime = in.readLong(); + mImportanceLockedDefaultApp = in.readBoolean(); } @Override @@ -382,13 +381,13 @@ public final class NotificationChannel implements Parcelable { dest.writeInt(mLightColor); dest.writeBoolean(mBlockableSystem); dest.writeInt(mAllowBubbles); - dest.writeBoolean(mImportanceLockedByOEM); dest.writeInt(mOriginalImportance); dest.writeString(mParentId); dest.writeString(mConversationId); dest.writeBoolean(mDemoted); dest.writeBoolean(mImportantConvo); dest.writeLong(mDeletedTime); + dest.writeBoolean(mImportanceLockedDefaultApp); } /** @@ -851,14 +850,6 @@ public final class NotificationChannel implements Parcelable { * @hide */ @TestApi - public void setImportanceLockedByOEM(boolean locked) { - mImportanceLockedByOEM = locked; - } - - /** - * @hide - */ - @TestApi public void setImportanceLockedByCriticalDeviceFunction(boolean locked) { mImportanceLockedDefaultApp = locked; } @@ -1107,8 +1098,8 @@ public final class NotificationChannel implements Parcelable { out.attributeBoolean(null, ATT_IMP_CONVERSATION, isImportantConversation()); } - // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of - // truth and so aren't written to this xml file + // mImportanceLockedDefaultApp has a different source of truth and so isn't written to + // this xml file out.endTag(null, TAG_CHANNEL); } @@ -1251,7 +1242,6 @@ public final class NotificationChannel implements Parcelable { && Arrays.equals(mVibration, that.mVibration) && Objects.equals(getGroup(), that.getGroup()) && Objects.equals(getAudioAttributes(), that.getAudioAttributes()) - && mImportanceLockedByOEM == that.mImportanceLockedByOEM && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp && mOriginalImportance == that.mOriginalImportance && Objects.equals(getParentChannelId(), that.getParentChannelId()) @@ -1267,7 +1257,7 @@ public final class NotificationChannel implements Parcelable { getUserLockedFields(), isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(), getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles, - mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance, + mImportanceLockedDefaultApp, mOriginalImportance, mParentId, mConversationId, mDemoted, mImportantConvo); result = 31 * result + Arrays.hashCode(mVibration); return result; @@ -1312,7 +1302,6 @@ public final class NotificationChannel implements Parcelable { + ", mAudioAttributes=" + mAudioAttributes + ", mBlockableSystem=" + mBlockableSystem + ", mAllowBubbles=" + mAllowBubbles - + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp + ", mOriginalImp=" + mOriginalImportance + ", mParent=" + mParentId diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java index 5044245166a3..11b840fd4ad4 100644 --- a/core/java/android/app/admin/DevicePolicyResources.java +++ b/core/java/android/app/admin/DevicePolicyResources.java @@ -1498,6 +1498,45 @@ public final class DevicePolicyResources { * Content description for the work profile lock screen. */ public static final String WORK_LOCK_ACCESSIBILITY = PREFIX + "WORK_LOCK_ACCESSIBILITY"; + + /** + * Notification text displayed when screenshots are blocked by an IT admin. + */ + public static final String SCREENSHOT_BLOCKED_BY_ADMIN = + PREFIX + "SCREENSHOT_BLOCKED_BY_ADMIN"; + + /** + * Message shown when user is almost at the limit of password attempts where the + * profile will be removed. Accepts number of failed attempts and remaining failed + * attempts as params. + */ + public static final String KEYGUARD_DIALOG_FAILED_ATTEMPTS_ALMOST_ERASING_PROFILE = + PREFIX + "KEYGUARD_DIALOG_FAILED_ATTEMPTS_ALMOST_ERASING_PROFILE"; + + /** + * Message shown in dialog when user has exceeded the maximum attempts and the profile + * will be removed. Accepts number of failed attempts as a param. + */ + public static final String KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE = + PREFIX + "KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE"; + + /** + * Monitoring dialog subtitle for the section describing VPN. + */ + public static final String QS_DIALOG_MONITORING_VPN_SUBTITLE = + PREFIX + "QS_DIALOG_MONITORING_VPN_SUBTITLE"; + + /** + * Monitoring dialog subtitle for the section describing network logging. + */ + public static final String QS_DIALOG_MONITORING_NETWORK_SUBTITLE = + PREFIX + "QS_DIALOG_MONITORING_NETWORK_SUBTITLE"; + + /** + * Monitoring dialog subtitle for the section describing certificate authorities. + */ + public static final String QS_DIALOG_MONITORING_CA_CERT_SUBTITLE = + PREFIX + "QS_DIALOG_MONITORING_CA_CERT_SUBTITLE"; } /** diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index 9bb048d7a75f..f6de72b43de6 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -665,184 +665,292 @@ public class BackupTransport { @Override public void name(AndroidFuture<String> resultFuture) throws RemoteException { - String result = BackupTransport.this.name(); - resultFuture.complete(result); + try { + String result = BackupTransport.this.name(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void configurationIntent(AndroidFuture<Intent> resultFuture) throws RemoteException { - Intent result = BackupTransport.this.configurationIntent(); - resultFuture.complete(result); + try { + Intent result = BackupTransport.this.configurationIntent(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void currentDestinationString(AndroidFuture<String> resultFuture) throws RemoteException { - String result = BackupTransport.this.currentDestinationString(); - resultFuture.complete(result); + try { + String result = BackupTransport.this.currentDestinationString(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void dataManagementIntent(AndroidFuture<Intent> resultFuture) throws RemoteException { - Intent result = BackupTransport.this.dataManagementIntent(); - resultFuture.complete(result); + try { + Intent result = BackupTransport.this.dataManagementIntent(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void dataManagementIntentLabel(AndroidFuture<CharSequence> resultFuture) throws RemoteException { - CharSequence result = BackupTransport.this.dataManagementIntentLabel(); - resultFuture.complete(result); + try { + CharSequence result = BackupTransport.this.dataManagementIntentLabel(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void transportDirName(AndroidFuture<String> resultFuture) throws RemoteException { - String result = BackupTransport.this.transportDirName(); - resultFuture.complete(result); + try { + String result = BackupTransport.this.transportDirName(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void requestBackupTime(AndroidFuture<Long> resultFuture) throws RemoteException { - long result = BackupTransport.this.requestBackupTime(); - resultFuture.complete(result); + try { + long result = BackupTransport.this.requestBackupTime(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void initializeDevice(ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.initializeDevice(); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.initializeDevice(); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags, ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.performBackup(packageInfo, inFd, flags); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.performBackup(packageInfo, inFd, flags); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void clearBackupData(PackageInfo packageInfo, ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.clearBackupData(packageInfo); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.clearBackupData(packageInfo); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void finishBackup(ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.finishBackup(); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.finishBackup(); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void getAvailableRestoreSets(AndroidFuture<List<RestoreSet>> resultFuture) throws RemoteException { - RestoreSet[] result = BackupTransport.this.getAvailableRestoreSets(); - resultFuture.complete(Arrays.asList(result)); + try { + RestoreSet[] result = BackupTransport.this.getAvailableRestoreSets(); + resultFuture.complete(Arrays.asList(result)); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void getCurrentRestoreSet(AndroidFuture<Long> resultFuture) throws RemoteException { - long result = BackupTransport.this.getCurrentRestoreSet(); - resultFuture.complete(result); + try { + long result = BackupTransport.this.getCurrentRestoreSet(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void startRestore(long token, PackageInfo[] packages, ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.startRestore(token, packages); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.startRestore(token, packages); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void nextRestorePackage(AndroidFuture<RestoreDescription> resultFuture) throws RemoteException { - RestoreDescription result = BackupTransport.this.nextRestorePackage(); - resultFuture.complete(result); + try { + RestoreDescription result = BackupTransport.this.nextRestorePackage(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void getRestoreData(ParcelFileDescriptor outFd, ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.getRestoreData(outFd); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.getRestoreData(outFd); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void finishRestore(ITransportStatusCallback callback) throws RemoteException { - BackupTransport.this.finishRestore(); - callback.onOperationComplete(); + try { + BackupTransport.this.finishRestore(); + callback.onOperationComplete(); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void requestFullBackupTime(AndroidFuture<Long> resultFuture) throws RemoteException { - long result = BackupTransport.this.requestFullBackupTime(); - resultFuture.complete(result); + try { + long result = BackupTransport.this.requestFullBackupTime(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket, int flags, ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.performFullBackup(targetPackage, socket, flags); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.performFullBackup(targetPackage, socket, flags); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void checkFullBackupSize(long size, ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.checkFullBackupSize(size); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.checkFullBackupSize(size); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void sendBackupData(int numBytes, ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.sendBackupData(numBytes); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.sendBackupData(numBytes); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void cancelFullBackup(ITransportStatusCallback callback) throws RemoteException { - BackupTransport.this.cancelFullBackup(); - callback.onOperationComplete(); + try { + BackupTransport.this.cancelFullBackup(); + callback.onOperationComplete(); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup, AndroidFuture<Boolean> resultFuture) throws RemoteException { - boolean result = BackupTransport.this.isAppEligibleForBackup(targetPackage, - isFullBackup); - resultFuture.complete(result); + try { + boolean result = BackupTransport.this.isAppEligibleForBackup(targetPackage, + isFullBackup); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void getBackupQuota(String packageName, boolean isFullBackup, AndroidFuture<Long> resultFuture) throws RemoteException { - long result = BackupTransport.this.getBackupQuota(packageName, isFullBackup); - resultFuture.complete(result); + try { + long result = BackupTransport.this.getBackupQuota(packageName, isFullBackup); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void getTransportFlags(AndroidFuture<Integer> resultFuture) throws RemoteException { - int result = BackupTransport.this.getTransportFlags(); - resultFuture.complete(result); + try { + int result = BackupTransport.this.getTransportFlags(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } } @Override public void getNextFullRestoreDataChunk(ParcelFileDescriptor socket, ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.getNextFullRestoreDataChunk(socket); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.getNextFullRestoreDataChunk(socket); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } @Override public void abortFullRestore(ITransportStatusCallback callback) throws RemoteException { - int result = BackupTransport.this.abortFullRestore(); - callback.onOperationCompleteWithStatus(result); + try { + int result = BackupTransport.this.abortFullRestore(); + callback.onOperationCompleteWithStatus(result); + } catch (RuntimeException e) { + callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); + } } } } diff --git a/core/java/android/companion/virtual/OWNERS b/core/java/android/companion/virtual/OWNERS new file mode 100644 index 000000000000..29681045ac4a --- /dev/null +++ b/core/java/android/companion/virtual/OWNERS @@ -0,0 +1 @@ +include /services/companion/java/com/android/server/companion/virtual/OWNERS diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 907db7df68d5..836bff598ede 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2269,6 +2269,19 @@ public abstract class Context { */ public void sendBroadcastMultiplePermissions(@NonNull Intent intent, @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions) { + sendBroadcastMultiplePermissions(intent, receiverPermissions, excludedPermissions, null); + } + + + /** + * Like {@link #sendBroadcastMultiplePermissions(Intent, String[], String[])}, but also allows + * specification of a list of excluded packages. + * + * @hide + */ + public void sendBroadcastMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions, + @Nullable String[] excludedPackages) { throw new RuntimeException("Not implemented. Must override in a subclass."); } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 4ecd7761ac4f..e6549187e5c5 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -515,8 +515,10 @@ public class ContextWrapper extends Context { /** @hide */ @Override public void sendBroadcastMultiplePermissions(@NonNull Intent intent, - @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions) { - mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions, excludedPermissions); + @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions, + @Nullable String[] excludedPackages) { + mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions, excludedPermissions, + excludedPackages); } /** @hide */ diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java index 1b84686bbfcf..fb41b890ce9c 100644 --- a/core/java/android/content/pm/AppSearchShortcutInfo.java +++ b/core/java/android/content/pm/AppSearchShortcutInfo.java @@ -413,7 +413,10 @@ public class AppSearchShortcutInfo extends GenericDocument { final int iconResId = (int) getPropertyLong(KEY_ICON_RES_ID); final String iconResName = getPropertyString(KEY_ICON_RES_NAME); final String iconUri = getPropertyString(KEY_ICON_URI); - final int disabledReason = Integer.parseInt(getPropertyString(KEY_DISABLED_REASON)); + final String disabledReasonString = getPropertyString(KEY_DISABLED_REASON); + final int disabledReason = !TextUtils.isEmpty(disabledReasonString) + ? Integer.parseInt(getPropertyString(KEY_DISABLED_REASON)) + : ShortcutInfo.DISABLED_REASON_NOT_DISABLED; final Map<String, Map<String, List<String>>> capabilityBindings = parseCapabilityBindings(getPropertyStringArray(KEY_CAPABILITY_BINDINGS)); return new ShortcutInfo( diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java index ae7d91f92cb7..37eb74a58235 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -187,14 +187,24 @@ public class VpnManager { /** * The network that was underlying the VPN when the event occurred, as a {@link Network}. * - * This extra will be null if there was no underlying network at the time of the event. + * <p>This extra will be null if there was no underlying network at the time of the event, or + * the underlying network has no bearing on the event, as in the case of: + * <ul> + * <li>CATEGORY_EVENT_DEACTIVATED_BY_USER + * <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED + * </ul> */ public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK"; /** * The {@link NetworkCapabilities} of the underlying network when the event occurred. * - * This extra will be null if there was no underlying network at the time of the event. + * <p>This extra will be null if there was no underlying network at the time of the event, or + * the underlying network has no bearing on the event, as in the case of: + * <ul> + * <li>CATEGORY_EVENT_DEACTIVATED_BY_USER + * <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED + * </ul> */ public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES = "android.net.extra.UNDERLYING_NETWORK_CAPABILITIES"; @@ -202,7 +212,12 @@ public class VpnManager { /** * The {@link LinkProperties} of the underlying network when the event occurred. * - * This extra will be null if there was no underlying network at the time of the event. + * <p>This extra will be null if there was no underlying network at the time of the event, or + * the underlying network has no bearing on the event, as in the case of: + * <ul> + * <li>CATEGORY_EVENT_DEACTIVATED_BY_USER + * <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED + * </ul> */ public static final String EXTRA_UNDERLYING_LINK_PROPERTIES = "android.net.extra.UNDERLYING_LINK_PROPERTIES"; diff --git a/core/java/android/net/VpnProfileState.java b/core/java/android/net/VpnProfileState.java index c69ea1a8c220..552a2c171f21 100644 --- a/core/java/android/net/VpnProfileState.java +++ b/core/java/android/net/VpnProfileState.java @@ -24,6 +24,8 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; +import java.util.StringJoiner; /** * Describe the state of VPN. @@ -150,4 +152,44 @@ public final class VpnProfileState implements Parcelable { mAlwaysOn = in.readBoolean(); mLockdown = in.readBoolean(); } + + private String convertStateToString(@State int state) { + switch (state) { + case STATE_CONNECTED: + return "CONNECTED"; + case STATE_CONNECTING: + return "CONNECTING"; + case STATE_DISCONNECTED: + return "DISCONNECTED"; + case STATE_FAILED: + return "FAILED"; + default: + return "UNKNOWN"; + } + } + + @Override + public String toString() { + final StringJoiner resultJoiner = new StringJoiner(", ", "{", "}"); + resultJoiner.add("State: " + convertStateToString(getState())); + resultJoiner.add("SessionId: " + getSessionId()); + resultJoiner.add("Always-on: " + isAlwaysOn()); + resultJoiner.add("Lockdown: " + isLockdownEnabled()); + return resultJoiner.toString(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof VpnProfileState)) return false; + final VpnProfileState that = (VpnProfileState) obj; + return (getState() == that.getState() + && Objects.equals(getSessionId(), that.getSessionId()) + && isAlwaysOn() == that.isAlwaysOn() + && isLockdownEnabled() == that.isLockdownEnabled()); + } + + @Override + public int hashCode() { + return Objects.hash(getState(), getSessionId(), isAlwaysOn(), isLockdownEnabled()); + } } diff --git a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java index 4f1f380c3a77..3e1b5f087c10 100644 --- a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java +++ b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java @@ -269,7 +269,7 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment protected CharSequence getItemLabel(DisplayResolveInfo dri) { final PackageManager pm = getContext().getPackageManager(); return getPinLabel(isPinned(dri), - isShortcutTarget() ? "" : dri.getResolveInfo().loadLabel(pm)); + isShortcutTarget() ? mShortcutTitle : dri.getResolveInfo().loadLabel(pm)); } @Nullable diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java index 2247e2f9a9fc..965895f08d6e 100644 --- a/core/java/com/android/internal/app/LocalePickerWithRegion.java +++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java @@ -340,7 +340,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O inflater.inflate(R.menu.language_selection_list, menu); final MenuItem searchMenuItem = menu.findItem(R.id.locale_search_menu); - if (!mAppPackageName.isEmpty() && mOnActionExpandListener != null) { + if (!TextUtils.isEmpty(mAppPackageName) && mOnActionExpandListener != null) { searchMenuItem.setOnActionExpandListener(mOnActionExpandListener); } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index a23f84158366..7e53a5afc315 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1471,10 +1471,14 @@ public class ResolverActivity extends Activity implements mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList.get(0); boolean inWorkProfile = getCurrentProfile() == PROFILE_WORK; - DisplayResolveInfo otherProfileResolveInfo = - mMultiProfilePagerAdapter.getInactiveListAdapter().mDisplayList.get(0); + ResolverListAdapter inactiveAdapter = mMultiProfilePagerAdapter.getInactiveListAdapter(); + DisplayResolveInfo otherProfileResolveInfo = inactiveAdapter.mDisplayList.get(0); + + // Load the icon asynchronously ImageView icon = findViewById(R.id.icon); - // TODO: Set icon drawable to app icon. + ResolverListAdapter.LoadIconTask iconTask = inactiveAdapter.new LoadIconTask( + otherProfileResolveInfo, new ResolverListAdapter.ViewHolder(icon)); + iconTask.execute(); ((TextView) findViewById(R.id.open_cross_profile)).setText( getResources().getString( @@ -1494,8 +1498,7 @@ public class ResolverActivity extends Activity implements prepareIntentForCrossProfileLaunch(intent); } safelyStartActivityAsUser(otherProfileResolveInfo, - mMultiProfilePagerAdapter.getInactiveListAdapter().mResolverListController - .getUserHandle()); + inactiveAdapter.mResolverListController.getUserHandle()); }); } diff --git a/core/res/res/anim-ldrtl/task_fragment_close_enter.xml b/core/res/res/anim-ldrtl/task_fragment_close_enter.xml new file mode 100644 index 000000000000..e5f5707c9a20 --- /dev/null +++ b/core/res/res/anim-ldrtl/task_fragment_close_enter.xml @@ -0,0 +1,38 @@ +<?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. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/linear" + android:startOffset="0" + android:duration="200" /> + + <translate + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="200" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim-ldrtl/task_fragment_close_exit.xml b/core/res/res/anim-ldrtl/task_fragment_close_exit.xml new file mode 100644 index 000000000000..c5a36542cc7d --- /dev/null +++ b/core/res/res/anim-ldrtl/task_fragment_close_exit.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="0.0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/linear" + android:startOffset="35" + android:duration="83" /> + + <translate + android:fromXDelta="0" + android:toXDelta="-10%" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="200" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim-ldrtl/task_fragment_open_enter.xml b/core/res/res/anim-ldrtl/task_fragment_open_enter.xml new file mode 100644 index 000000000000..b6f1af3e7840 --- /dev/null +++ b/core/res/res/anim-ldrtl/task_fragment_open_enter.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?><!-- +/* +** Copyright 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false"> + + <alpha + android:fromAlpha="0" + android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/linear" + android:startOffset="50" + android:duration="83" /> + + <translate + android:fromXDelta="-10%" + android:toXDelta="0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="450" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim-ldrtl/task_fragment_open_exit.xml b/core/res/res/anim-ldrtl/task_fragment_open_exit.xml new file mode 100644 index 000000000000..6cea53c3e934 --- /dev/null +++ b/core/res/res/anim-ldrtl/task_fragment_open_exit.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?><!-- +/* +** Copyright 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false"> + + <alpha + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/standard_accelerate" + android:startOffset="0" + android:duration="450" /> + + <translate + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="450" /> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/task_fragment_close_enter.xml b/core/res/res/anim/task_fragment_close_enter.xml index c940552d53ad..cb6cdbeb71f4 100644 --- a/core/res/res/anim/task_fragment_close_enter.xml +++ b/core/res/res/anim/task_fragment_close_enter.xml @@ -17,16 +17,21 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> - <scale - android:fromXScale="1.1" - android:toXScale="1" - android:fromYScale="1.1" - android:toYScale="1" - android:pivotX="50%" - android:pivotY="50%" + <alpha + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/linear" + android:startOffset="0" + android:duration="200" /> + + <translate android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@interpolator/fast_out_extra_slow_in" - android:duration="400"/> + android:startOffset="0" + android:duration="200" /> </set>
\ No newline at end of file diff --git a/core/res/res/anim/task_fragment_close_exit.xml b/core/res/res/anim/task_fragment_close_exit.xml index 8998f764ff9b..84d8b7e6b5cd 100644 --- a/core/res/res/anim/task_fragment_close_exit.xml +++ b/core/res/res/anim/task_fragment_close_exit.xml @@ -19,24 +19,21 @@ android:shareInterpolator="false" android:zAdjustment="top"> <alpha - android:fromAlpha="1" + android:fromAlpha="1.0" android:toAlpha="0.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@interpolator/linear" - android:startOffset="33" - android:duration="50"/> - <scale - android:fromXScale="1" - android:toXScale="0.9" - android:fromYScale="1" - android:toYScale="0.9" - android:pivotX="50%" - android:pivotY="50%" + android:startOffset="0" + android:duration="83" /> + + <translate + android:fromXDelta="0" + android:toXDelta="10%" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@interpolator/fast_out_extra_slow_in" - android:duration="400"/> + android:duration="200" /> </set> diff --git a/core/res/res/anim/task_fragment_open_enter.xml b/core/res/res/anim/task_fragment_open_enter.xml index 6bc47deb2de4..aa61e6f17070 100644 --- a/core/res/res/anim/task_fragment_open_enter.xml +++ b/core/res/res/anim/task_fragment_open_enter.xml @@ -25,17 +25,13 @@ android:fillAfter="true" android:interpolator="@interpolator/linear" android:startOffset="50" - android:duration="50"/> - <scale - android:fromXScale="0.85" - android:toXScale="1" - android:fromYScale="0.85" - android:toYScale="1" - android:pivotX="50%" - android:pivotY="50%" + android:duration="83" /> + <translate + android:fromXDelta="10%" + android:toXDelta="0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@interpolator/fast_out_extra_slow_in" - android:duration="400"/> + android:duration="400" /> </set> diff --git a/core/res/res/anim/task_fragment_open_exit.xml b/core/res/res/anim/task_fragment_open_exit.xml index 160eb84223da..b4914d21d097 100644 --- a/core/res/res/anim/task_fragment_open_exit.xml +++ b/core/res/res/anim/task_fragment_open_exit.xml @@ -17,16 +17,20 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> - <scale - android:fromXScale="1" - android:toXScale="1.05" - android:fromYScale="1" - android:toYScale="1.05" - android:pivotX="50%" - android:pivotY="50%" + <alpha + android:fromAlpha="1.0" + android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/standard_accelerate" + android:startOffset="0" + android:duration="400" /> + + <translate android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@interpolator/fast_out_extra_slow_in" - android:duration="400"/> + android:duration="400" /> </set>
\ No newline at end of file diff --git a/core/res/res/layout/miniresolver.xml b/core/res/res/layout/miniresolver.xml index 44ed6f2a0676..85fe2835fd5e 100644 --- a/core/res/res/layout/miniresolver.xml +++ b/core/res/res/layout/miniresolver.xml @@ -24,12 +24,11 @@ android:id="@id/contentPanel"> <RelativeLayout - android:id="@+id/title_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alwaysShow="true" android:elevation="@dimen/resolver_elevation" - android:paddingTop="@dimen/resolver_small_margin" + android:paddingTop="24dp" android:paddingStart="@dimen/resolver_edge_margin" android:paddingEnd="@dimen/resolver_edge_margin" android:paddingBottom="@dimen/resolver_title_padding_bottom" @@ -47,8 +46,12 @@ android:id="@+id/open_cross_profile" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:paddingTop="16dp" android:layout_below="@id/icon" android:layout_centerHorizontal="true" + android:textSize="24sp" + android:lineHeight="32sp" + android:gravity="center" android:textColor="?android:textColorPrimary" /> </RelativeLayout> @@ -58,15 +61,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alwaysShow="true" + android:paddingTop="32dp" + android:paddingBottom="@dimen/resolver_button_bar_spacing" android:orientation="vertical" android:background="?attr/colorBackground" android:layout_ignoreOffset="true"> - <View - android:id="@+id/resolver_button_bar_divider" - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?attr/colorBackground" - android:foreground="?attr/dividerVertical" /> <RelativeLayout style="?attr/buttonBarStyle" android:layout_width="match_parent" @@ -77,7 +76,6 @@ android:orientation="horizontal" android:layoutDirection="locale" android:measureWithLargestChild="true" - android:paddingTop="@dimen/resolver_button_bar_spacing" android:paddingBottom="@dimen/resolver_button_bar_spacing" android:paddingStart="@dimen/resolver_edge_margin" android:paddingEnd="@dimen/resolver_small_margin" diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 5df3dde82b3e..b515abc4000f 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -448,5 +448,6 @@ <color name="accessibility_color_inversion_background">#546E7A</color> <!-- Color of camera light when camera is in use --> - <color name="camera_privacy_light">#FFFFFF</color> + <color name="camera_privacy_light_day">#FFFFFF</color> + <color name="camera_privacy_light_night">#FFFFFF</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 21abd2237bfa..df7f34a357c3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2998,12 +2998,6 @@ </string-array> - <!-- When migrating notification settings into the permission framework, whether all existing - apps should be marked as 'user-set' (true) or whether only the apps that have explicitly - modified notification settings should be marked as 'user-set' (false). Users will not see - system generated permission prompts for 'user-set' apps. --> - <bool name="config_notificationForceUserSetOnUpgrade">true</bool> - <!-- Default Gravity setting for the system Toast view. Equivalent to: Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM --> <integer name="config_toastDefaultGravity">0x00000051</integer> @@ -5790,4 +5784,17 @@ <!-- List of the labels of requestable device state config values --> <string-array name="config_deviceStatesAvailableForAppRequests"/> + + <!-- Interval in milliseconds to average light sensor values for camera light brightness --> + <integer name="config_cameraPrivacyLightAlsAveragingIntervalMillis">3000</integer> + <!-- Light sensor's lux value to use as the threshold between using day or night brightness --> + <integer name="config_cameraPrivacyLightAlsNightThreshold">4</integer> + + <!-- List of system components which are allowed to receive ServiceState entries in an + un-sanitized form, even if the location toggle is off. This is intended ONLY for system + components, such as the telephony stack, which require access to the full ServiceState for + tasks such as network registration. --> + <string-array name="config_serviceStateLocationAllowedPackages"> + <item>"com.android.phone"</item> + </string-array> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 678dd9ffa665..4e1839d6e338 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4754,7 +4754,10 @@ <!-- For VirtualDeviceManager --> <java-symbol type="string" name="vdm_camera_access_denied" /> - <java-symbol type="color" name="camera_privacy_light"/> + <java-symbol type="color" name="camera_privacy_light_day"/> + <java-symbol type="color" name="camera_privacy_light_night"/> + <java-symbol type="integer" name="config_cameraPrivacyLightAlsAveragingIntervalMillis"/> + <java-symbol type="integer" name="config_cameraPrivacyLightAlsNightThreshold"/> <java-symbol type="bool" name="config_bg_current_drain_monitor_enabled" /> <java-symbol type="array" name="config_bg_current_drain_threshold_to_restricted_bucket" /> @@ -4773,8 +4776,8 @@ <java-symbol type="integer" name="config_bg_current_drain_exempted_types" /> <java-symbol type="bool" name="config_bg_current_drain_high_threshold_by_bg_location" /> <java-symbol type="drawable" name="ic_swap_horiz" /> - <java-symbol type="bool" name="config_notificationForceUserSetOnUpgrade" /> <java-symbol type="array" name="config_deviceStatesAvailableForAppRequests" /> + <java-symbol type="array" name="config_serviceStateLocationAllowedPackages" /> <!-- For app language picker --> <java-symbol type="string" name="system_locale_title" /> diff --git a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java index 969357f66dde..22feecbd899f 100644 --- a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java +++ b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.platform.test.annotations.Presubmit; import android.util.ArraySet; -import org.junit.Ignore; import org.junit.Test; import java.util.Set; @@ -32,7 +31,6 @@ import java.util.Set; @Presubmit public class AppSearchShortcutInfoTest { - @Ignore("b/208375334") @Test public void testBuildShortcutAndGetValue() { final String category = @@ -51,7 +49,7 @@ public class AppSearchShortcutInfoTest { final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW); final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(/*packageName=*/"", id) .setActivity(activity) - .setLongLabel(id) + .setShortLabel(id) .setIconResName(shortcutIconResName) .setIntent(shortcutIntent) .setPerson(person) @@ -64,11 +62,13 @@ public class AppSearchShortcutInfoTest { assertThat(shortcut.getId()).isEqualTo(id); assertThat(shortcut.getShortLabel()).isEqualTo(id); assertThat(shortcut.getIconResName()).isEqualTo(shortcutIconResName); - assertThat(shortcut.getIntent().toString()).isEqualTo(shortcut.toString()); + assertThat(shortcut.getIntent().toString()).isEqualTo(shortcutIntent.toString()); assertThat(shortcut.getPersons().length).isEqualTo(1); - assertThat(shortcut.getPersons()[0]).isEqualTo(person); + final Person target = shortcut.getPersons()[0]; + assertThat(target.getName()).isEqualTo(person.getName()); + assertThat(target.isBot()).isEqualTo(person.isBot()); + assertThat(target.isImportant()).isEqualTo(person.isImportant()); assertThat(shortcut.getCategories()).isEqualTo(categorySet); - assertThat(shortcut.getFlags()).isEqualTo(ShortcutInfo.FLAG_LONG_LIVED); assertThat(shortcut.getActivity()).isEqualTo(activity); } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java index 9ad6f3adbd33..6fff52a20062 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java @@ -206,6 +206,8 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider { putSignatureImpl("NONEwithECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$NONE"); + putSignatureImpl("Ed25519", + PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$Ed25519"); putSignatureImpl("SHA1withECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA1"); put("Alg.Alias.Signature.ECDSA", "SHA1withECDSA"); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java index 8289671de212..5216a908826b 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java @@ -29,7 +29,10 @@ import libcore.util.EmptyArray; import java.io.ByteArrayOutputStream; import java.security.InvalidKeyException; import java.security.SignatureSpi; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; import java.util.List; +import java.util.Set; /** * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures. @@ -37,6 +40,10 @@ import java.util.List; * @hide */ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase { + private static final Set<String> ACCEPTED_SIGNING_SCHEMES = Set.of( + KeyProperties.KEY_ALGORITHM_EC.toLowerCase(), + NamedParameterSpec.ED25519.getName().toLowerCase(), + "eddsa"); public final static class NONE extends AndroidKeyStoreECDSASignatureSpi { public NONE() { @@ -114,6 +121,18 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature } } + public static final class Ed25519 extends AndroidKeyStoreECDSASignatureSpi { + public Ed25519() { + // Ed25519 uses an internal digest system. + super(KeymasterDefs.KM_DIGEST_NONE); + } + + @Override + protected String getAlgorithm() { + return NamedParameterSpec.ED25519.getName(); + } + } + public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi { public SHA1() { super(KeymasterDefs.KM_DIGEST_SHA1); @@ -174,9 +193,10 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature @Override protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException { - if (!KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) { + if (!ACCEPTED_SIGNING_SCHEMES.contains(key.getAlgorithm().toLowerCase())) { throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm() - + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported"); + + ". Only" + Arrays.toString(ACCEPTED_SIGNING_SCHEMES.stream().toArray()) + + " supported"); } long keySizeBits = -1; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 9713c2735a9c..1cd422087ccb 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -234,13 +234,36 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken) { // If the activity belongs to the current app process, we treat it as a new activity launch. - final Activity activity = ActivityThread.currentActivityThread().getActivity(activityToken); + final Activity activity = getActivity(activityToken); if (activity != null) { onActivityCreated(activity); - updateCallbackIfNecessary(); return; } - // TODO: handle for activity in other process. + + final TaskContainer taskContainer = getTaskContainer(taskId); + if (taskContainer == null || taskContainer.isInPictureInPicture()) { + // We don't embed activity when it is in PIP. + return; + } + + // If the activity belongs to a different app process, we treat it as starting new intent, + // since both actions might result in a new activity that should appear in an organized + // TaskFragment. + final WindowContainerTransaction wct = new WindowContainerTransaction(); + TaskFragmentContainer targetContainer = resolveStartActivityIntent(wct, taskId, + activityIntent, null /* launchingActivity */); + if (targetContainer == null) { + // When there is no split rule matched, try to place it in the top container like a + // normal launch. + targetContainer = taskContainer.getTopTaskFragmentContainer(); + } + if (targetContainer == null) { + return; + } + wct.reparentActivityToTaskFragment(targetContainer.getTaskFragmentToken(), activityToken); + mPresenter.applyTransaction(wct); + // Because the activity does not belong to the organizer process, we wait until + // onTaskFragmentAppeared to trigger updateCallbackIfNecessary(). } /** Called on receiving {@link #onTaskFragmentVanished(TaskFragmentInfo)} for cleanup. */ @@ -327,16 +350,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen */ // TODO(b/190433398): Break down into smaller functions. void handleActivityCreated(@NonNull Activity launchedActivity) { - if (isInPictureInPicture(launchedActivity)) { - // We don't embed activity when it is in PIP. + if (isInPictureInPicture(launchedActivity) || launchedActivity.isFinishing()) { + // We don't embed activity when it is in PIP, or finishing. return; } - final List<EmbeddingRule> splitRules = getSplitRules(); - final TaskFragmentContainer currentContainer = getContainerWithActivity( - launchedActivity.getActivityToken()); + final TaskFragmentContainer currentContainer = getContainerWithActivity(launchedActivity); // Check if the activity is configured to always be expanded. - if (shouldExpand(launchedActivity, null, splitRules)) { + if (shouldExpand(launchedActivity, null /* intent */)) { if (shouldContainerBeExpanded(currentContainer)) { // Make sure that the existing container is expanded mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken()); @@ -375,7 +396,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen IBinder belowToken = ActivityClient.getInstance().getActivityTokenBelow( launchedActivity.getActivityToken()); if (belowToken != null) { - activityBelow = ActivityThread.currentActivityThread().getActivity(belowToken); + activityBelow = getActivity(belowToken); } } if (activityBelow == null) { @@ -384,7 +405,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Check if the split is already set. final TaskFragmentContainer activityBelowContainer = getContainerWithActivity( - activityBelow.getActivityToken()); + activityBelow); if (currentContainer != null && activityBelowContainer != null) { final SplitContainer existingSplit = getActiveSplitForContainers(currentContainer, activityBelowContainer); @@ -394,8 +415,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } - final SplitPairRule splitPairRule = getSplitRule(activityBelow, launchedActivity, - splitRules); + final SplitPairRule splitPairRule = getSplitRule(activityBelow, launchedActivity); if (splitPairRule == null) { return; } @@ -409,8 +429,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // We don't embed activity when it is in PIP. return; } - final TaskFragmentContainer currentContainer = getContainerWithActivity( - activity.getActivityToken()); + final TaskFragmentContainer currentContainer = getContainerWithActivity(activity); if (currentContainer != null) { // Changes to activities in controllers are handled in @@ -443,11 +462,143 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } /** + * When we are trying to handle a new activity Intent, returns the {@link TaskFragmentContainer} + * that we should reparent the new activity to if there is any embedding rule matched. + * + * @param wct {@link WindowContainerTransaction} including all the window change + * requests. The caller is responsible to call + * {@link android.window.TaskFragmentOrganizer#applyTransaction}. + * @param taskId The Task to start the activity in. + * @param intent The {@link Intent} for starting the new launched activity. + * @param launchingActivity The {@link Activity} that starts the new activity. We will + * prioritize to split the new activity with it if it is not + * {@code null}. + * @return the {@link TaskFragmentContainer} to start the new activity in. {@code null} if there + * is no embedding rule matched. + */ + @VisibleForTesting + @Nullable + TaskFragmentContainer resolveStartActivityIntent(@NonNull WindowContainerTransaction wct, + int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) { + /* + * We will check the following to see if there is any embedding rule matched: + * 1. Whether the new activity intent should always expand. + * 2. Whether the launching activity (if set) should be split with the new activity intent. + * 3. Whether the top activity (if any) should be split with the new activity intent. + * 4. Whether the top activity (if any) in other split should be split with the new + * activity intent. + */ + + // 1. Whether the new activity intent should always expand. + if (shouldExpand(null /* activity */, intent)) { + return createEmptyExpandedContainer(wct, taskId, launchingActivity); + } + + // 2. Whether the launching activity (if set) should be split with the new activity intent. + if (launchingActivity != null) { + final TaskFragmentContainer container = getSecondaryContainerForSplitIfAny(wct, + launchingActivity, intent, true /* respectClearTop */); + if (container != null) { + return container; + } + } + + // 3. Whether the top activity (if any) should be split with the new activity intent. + final TaskContainer taskContainer = getTaskContainer(taskId); + if (taskContainer == null || taskContainer.getTopTaskFragmentContainer() == null) { + // There is no other activity in the Task to check split with. + return null; + } + final TaskFragmentContainer topContainer = taskContainer.getTopTaskFragmentContainer(); + final Activity topActivity = topContainer.getTopNonFinishingActivity(); + if (topActivity != null && topActivity != launchingActivity) { + final TaskFragmentContainer container = getSecondaryContainerForSplitIfAny(wct, + topActivity, intent, false /* respectClearTop */); + if (container != null) { + return container; + } + } + + // 4. Whether the top activity (if any) in other split should be split with the new + // activity intent. + final SplitContainer topSplit = getActiveSplitForContainer(topContainer); + if (topSplit == null) { + return null; + } + final TaskFragmentContainer otherTopContainer = + topSplit.getPrimaryContainer() == topContainer + ? topSplit.getSecondaryContainer() + : topSplit.getPrimaryContainer(); + final Activity otherTopActivity = otherTopContainer.getTopNonFinishingActivity(); + if (otherTopActivity != null && otherTopActivity != launchingActivity) { + return getSecondaryContainerForSplitIfAny(wct, otherTopActivity, intent, + false /* respectClearTop */); + } + return null; + } + + /** + * Returns an empty expanded {@link TaskFragmentContainer} that we can launch an activity into. + */ + @Nullable + private TaskFragmentContainer createEmptyExpandedContainer( + @NonNull WindowContainerTransaction wct, int taskId, + @Nullable Activity launchingActivity) { + // We need an activity in the organizer process in the same Task to use as the owner + // activity, as well as to get the Task window info. + final Activity activityInTask; + if (launchingActivity != null) { + activityInTask = launchingActivity; + } else { + final TaskContainer taskContainer = getTaskContainer(taskId); + activityInTask = taskContainer != null + ? taskContainer.getTopNonFinishingActivity() + : null; + } + if (activityInTask == null) { + // Can't find any activity in the Task that we can use as the owner activity. + return null; + } + final TaskFragmentContainer expandedContainer = newContainer(null /* activity */, + activityInTask, taskId); + mPresenter.createTaskFragment(wct, expandedContainer.getTaskFragmentToken(), + activityInTask.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED); + return expandedContainer; + } + + /** + * Returns a container for the new activity intent to launch into as splitting with the primary + * activity. + */ + @Nullable + private TaskFragmentContainer getSecondaryContainerForSplitIfAny( + @NonNull WindowContainerTransaction wct, @NonNull Activity primaryActivity, + @NonNull Intent intent, boolean respectClearTop) { + final SplitPairRule splitRule = getSplitRule(primaryActivity, intent); + if (splitRule == null) { + return null; + } + final TaskFragmentContainer existingContainer = getContainerWithActivity(primaryActivity); + final SplitContainer splitContainer = getActiveSplitForContainer(existingContainer); + if (splitContainer != null && existingContainer == splitContainer.getPrimaryContainer() + && (canReuseContainer(splitRule, splitContainer.getSplitRule()) + // TODO(b/231845476) we should always respect clearTop. + || !respectClearTop)) { + // Can launch in the existing secondary container if the rules share the same + // presentation. + return splitContainer.getSecondaryContainer(); + } + // Create a new TaskFragment to split with the primary activity for the new activity. + return mPresenter.createNewSplitWithEmptySideContainer(wct, primaryActivity, splitRule); + } + + /** * Returns a container that this activity is registered with. An activity can only belong to one * container, or no container at all. */ @Nullable - TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) { + TaskFragmentContainer getContainerWithActivity(@NonNull Activity activity) { + final IBinder activityToken = activity.getActivityToken(); for (int i = mTaskContainers.size() - 1; i >= 0; i--) { final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers; for (TaskFragmentContainer container : containers) { @@ -668,7 +819,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Returns the top active split container that has the provided container, if available. */ @Nullable - private SplitContainer getActiveSplitForContainer(@NonNull TaskFragmentContainer container) { + private SplitContainer getActiveSplitForContainer(@Nullable TaskFragmentContainer container) { + if (container == null) { + return null; + } final List<SplitContainer> splitContainers = container.getTaskContainer().mSplitContainers; if (splitContainers.isEmpty()) { return null; @@ -687,8 +841,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Returns the active split that has the provided containers as primary and secondary or as * secondary and primary, if available. */ + @VisibleForTesting @Nullable - private SplitContainer getActiveSplitForContainers( + SplitContainer getActiveSplitForContainers( @NonNull TaskFragmentContainer firstContainer, @NonNull TaskFragmentContainer secondContainer) { final List<SplitContainer> splitContainers = firstContainer.getTaskContainer() @@ -718,15 +873,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } boolean launchPlaceholderIfNecessary(@NonNull Activity activity) { - final TaskFragmentContainer container = getContainerWithActivity( - activity.getActivityToken()); + final TaskFragmentContainer container = getContainerWithActivity(activity); // Don't launch placeholder if the container is occluded. if (container != null && container != getTopActiveContainer(container.getTaskId())) { return false; } - SplitContainer splitContainer = container != null ? getActiveSplitForContainer(container) - : null; + final SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) { // Don't launch placeholder in primary split container return false; @@ -876,9 +1029,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * if available. */ @Nullable - private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity, - @NonNull Intent secondaryActivityIntent, @NonNull List<EmbeddingRule> splitRules) { - for (EmbeddingRule rule : splitRules) { + private SplitPairRule getSplitRule(@NonNull Activity primaryActivity, + @NonNull Intent secondaryActivityIntent) { + for (EmbeddingRule rule : mSplitRules) { if (!(rule instanceof SplitPairRule)) { continue; } @@ -894,9 +1047,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Returns a split rule for the provided pair of primary and secondary activities if available. */ @Nullable - private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity, - @NonNull Activity secondaryActivity, @NonNull List<EmbeddingRule> splitRules) { - for (EmbeddingRule rule : splitRules) { + private SplitPairRule getSplitRule(@NonNull Activity primaryActivity, + @NonNull Activity secondaryActivity) { + for (EmbeddingRule rule : mSplitRules) { if (!(rule instanceof SplitPairRule)) { continue; } @@ -933,16 +1086,24 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return mHandler; } + int getTaskId(@NonNull Activity activity) { + // Prefer to get the taskId from TaskFragmentContainer because Activity.getTaskId() is an + // IPC call. + final TaskFragmentContainer container = getContainerWithActivity(activity); + return container != null ? container.getTaskId() : activity.getTaskId(); + } + + @Nullable + Activity getActivity(@NonNull IBinder activityToken) { + return ActivityThread.currentActivityThread().getActivity(activityToken); + } + /** * Returns {@code true} if an Activity with the provided component name should always be * expanded to occupy full task bounds. Such activity must not be put in a split. */ - private static boolean shouldExpand(@Nullable Activity activity, @Nullable Intent intent, - List<EmbeddingRule> splitRules) { - if (splitRules == null) { - return false; - } - for (EmbeddingRule rule : splitRules) { + private boolean shouldExpand(@Nullable Activity activity, @Nullable Intent intent) { + for (EmbeddingRule rule : mSplitRules) { if (!(rule instanceof ActivityRule)) { continue; } @@ -996,8 +1157,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen */ boolean shouldRetainAssociatedActivity(@NonNull TaskFragmentContainer finishingContainer, @NonNull Activity associatedActivity) { - TaskFragmentContainer associatedContainer = getContainerWithActivity( - associatedActivity.getActivityToken()); + final TaskFragmentContainer associatedContainer = getContainerWithActivity( + associatedActivity); if (associatedContainer == null) { return false; } @@ -1085,130 +1246,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return super.onStartActivity(who, intent, options); } - if (shouldExpand(null, intent, getSplitRules())) { - setLaunchingInExpandedContainer(launchingActivity, options); - } else if (!splitWithLaunchingActivity(launchingActivity, intent, options)) { - setLaunchingInSameSideContainer(launchingActivity, intent, options); + final int taskId = getTaskId(launchingActivity); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + final TaskFragmentContainer launchedInTaskFragment = resolveStartActivityIntent(wct, + taskId, intent, launchingActivity); + if (launchedInTaskFragment != null) { + mPresenter.applyTransaction(wct); + // Amend the request to let the WM know that the activity should be placed in the + // dedicated container. + options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN, + launchedInTaskFragment.getTaskFragmentToken()); } return super.onStartActivity(who, intent, options); } - - private void setLaunchingInExpandedContainer(Activity launchingActivity, Bundle options) { - TaskFragmentContainer newContainer = mPresenter.createNewExpandedContainer( - launchingActivity); - - // Amend the request to let the WM know that the activity should be placed in the - // dedicated container. - options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN, - newContainer.getTaskFragmentToken()); - } - - /** - * Returns {@code true} if the activity that is going to be started via the - * {@code intent} should be paired with the {@code launchingActivity} and is set to be - * launched in the side container. - */ - private boolean splitWithLaunchingActivity(Activity launchingActivity, Intent intent, - Bundle options) { - final SplitPairRule splitPairRule = getSplitRule(launchingActivity, intent, - getSplitRules()); - if (splitPairRule == null) { - return false; - } - - // Check if there is any existing side container to launch into. - TaskFragmentContainer secondaryContainer = findSideContainerForNewLaunch( - launchingActivity, splitPairRule); - if (secondaryContainer == null) { - // Create a new split with an empty side container. - secondaryContainer = mPresenter - .createNewSplitWithEmptySideContainer(launchingActivity, splitPairRule); - } - - // Amend the request to let the WM know that the activity should be placed in the - // dedicated container. - options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN, - secondaryContainer.getTaskFragmentToken()); - return true; - } - - /** - * Finds if there is an existing split side {@link TaskFragmentContainer} that can be used - * for the new rule. - */ - @Nullable - private TaskFragmentContainer findSideContainerForNewLaunch(Activity launchingActivity, - SplitPairRule splitPairRule) { - final TaskFragmentContainer launchingContainer = getContainerWithActivity( - launchingActivity.getActivityToken()); - if (launchingContainer == null) { - return null; - } - - // We only check if the launching activity is the primary of the split. We will check - // if the launching activity is the secondary in #setLaunchingInSameSideContainer. - final SplitContainer splitContainer = getActiveSplitForContainer(launchingContainer); - if (splitContainer == null - || splitContainer.getPrimaryContainer() != launchingContainer) { - return null; - } - - if (canReuseContainer(splitPairRule, splitContainer.getSplitRule())) { - return splitContainer.getSecondaryContainer(); - } - return null; - } - - /** - * Checks if the activity that is going to be started via the {@code intent} should be - * paired with the existing top activity which is currently paired with the - * {@code launchingActivity}. If so, set the activity to be launched in the same side - * container of the {@code launchingActivity}. - */ - private void setLaunchingInSameSideContainer(Activity launchingActivity, Intent intent, - Bundle options) { - final TaskFragmentContainer launchingContainer = getContainerWithActivity( - launchingActivity.getActivityToken()); - if (launchingContainer == null) { - return; - } - - final SplitContainer splitContainer = getActiveSplitForContainer(launchingContainer); - if (splitContainer == null) { - return; - } - - if (splitContainer.getSecondaryContainer() != launchingContainer) { - return; - } - - // The launching activity is on the secondary container. Retrieve the primary - // activity from the other container. - Activity primaryActivity = - splitContainer.getPrimaryContainer().getTopNonFinishingActivity(); - if (primaryActivity == null) { - return; - } - - final SplitPairRule splitPairRule = getSplitRule(primaryActivity, intent, - getSplitRules()); - if (splitPairRule == null) { - return; - } - - // Can only launch in the same container if the rules share the same presentation. - if (!canReuseContainer(splitPairRule, splitContainer.getSplitRule())) { - return; - } - - // Amend the request to let the WM know that the activity should be placed in the - // dedicated container. This is necessary for the case that the activity is started - // into a new Task, or new Task will be escaped from the current host Task and be - // displayed in fullscreen. - options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN, - launchingContainer.getTaskFragmentToken()); - } } /** @@ -1228,8 +1279,15 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (!isContainerReusableRule(rule1) || !isContainerReusableRule(rule2)) { return false; } - return rule1.getSplitRatio() == rule2.getSplitRatio() - && rule1.getLayoutDirection() == rule2.getLayoutDirection(); + final SplitPairRule pairRule1 = (SplitPairRule) rule1; + final SplitPairRule pairRule2 = (SplitPairRule) rule2; + // TODO(b/231655482): add util method to do the comparison in SplitPairRule. + return pairRule1.getSplitRatio() == pairRule2.getSplitRatio() + && pairRule1.getLayoutDirection() == pairRule2.getLayoutDirection() + && pairRule1.getFinishPrimaryWithSecondary() + == pairRule2.getFinishPrimaryWithSecondary() + && pairRule1.getFinishSecondaryWithPrimary() + == pairRule2.getFinishSecondaryWithPrimary(); } /** diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index b32f4fa67906..43d0402c1525 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -16,8 +16,6 @@ package androidx.window.extensions.embedding; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; - import android.app.Activity; import android.app.WindowConfiguration; import android.app.WindowConfiguration.WindowingMode; @@ -100,10 +98,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { * Creates a new split with the primary activity and an empty secondary container. * @return The newly created secondary container. */ - TaskFragmentContainer createNewSplitWithEmptySideContainer(@NonNull Activity primaryActivity, + @NonNull + TaskFragmentContainer createNewSplitWithEmptySideContainer( + @NonNull WindowContainerTransaction wct, @NonNull Activity primaryActivity, @NonNull SplitPairRule rule) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - final Rect parentBounds = getParentContainerBounds(primaryActivity); final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule, isLtr(primaryActivity, rule)); @@ -127,8 +125,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); - applyTransaction(wct); - return secondaryContainer; } @@ -155,8 +151,15 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule, isLtr(primaryActivity, rule)); + final TaskFragmentContainer curSecondaryContainer = mController.getContainerWithActivity( + secondaryActivity); + TaskFragmentContainer containerToAvoid = primaryContainer; + if (rule.shouldClearTop() && curSecondaryContainer != null) { + // Do not reuse the current TaskFragment if the rule is to clear top. + containerToAvoid = curSecondaryContainer; + } final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct, - secondaryActivity, secondaryRectBounds, primaryContainer); + secondaryActivity, secondaryRectBounds, containerToAvoid); // Set adjacent to each other so that the containers below will be invisible. setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); @@ -167,21 +170,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } /** - * Creates a new expanded container. - */ - TaskFragmentContainer createNewExpandedContainer(@NonNull Activity launchingActivity) { - final TaskFragmentContainer newContainer = mController.newContainer(null /* activity */, - launchingActivity, launchingActivity.getTaskId()); - - final WindowContainerTransaction wct = new WindowContainerTransaction(); - createTaskFragment(wct, newContainer.getTaskFragmentToken(), - launchingActivity.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED); - - applyTransaction(wct); - return newContainer; - } - - /** * Creates a new container or resizes an existing container for activity to the provided bounds. * @param activity The activity to be re-parented to the container if necessary. * @param containerToAvoid Re-parent from this container if an activity is already in it. @@ -189,8 +177,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { private TaskFragmentContainer prepareContainerForActivity( @NonNull WindowContainerTransaction wct, @NonNull Activity activity, @NonNull Rect bounds, @Nullable TaskFragmentContainer containerToAvoid) { - TaskFragmentContainer container = mController.getContainerWithActivity( - activity.getActivityToken()); + TaskFragmentContainer container = mController.getContainerWithActivity(activity); final int taskId = container != null ? container.getTaskId() : activity.getTaskId(); if (container == null || container == containerToAvoid) { container = mController.newContainer(activity, taskId); @@ -230,7 +217,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { isLtr(launchingActivity, rule)); TaskFragmentContainer primaryContainer = mController.getContainerWithActivity( - launchingActivity.getActivityToken()); + launchingActivity); if (primaryContainer == null) { primaryContainer = mController.newContainer(launchingActivity, launchingActivity.getTaskId()); @@ -460,8 +447,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @NonNull Rect getParentContainerBounds(@NonNull Activity activity) { - final TaskFragmentContainer container = mController.getContainerWithActivity( - activity.getActivityToken()); + final TaskFragmentContainer container = mController.getContainerWithActivity(activity); if (container != null) { return getParentContainerBounds(container); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index dba71ef21946..0ea5603b1f3d 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -142,4 +142,23 @@ class TaskContainer { container.removePendingAppearedActivity(pendingAppearedActivity); } } + + @Nullable + TaskFragmentContainer getTopTaskFragmentContainer() { + if (mContainers.isEmpty()) { + return null; + } + return mContainers.get(mContainers.size() - 1); + } + + @Nullable + Activity getTopNonFinishingActivity() { + for (int i = mContainers.size() - 1; i >= 0; i--) { + final Activity activity = mContainers.get(i).getTopNonFinishingActivity(); + if (activity != null) { + return activity; + } + } + return null; + } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java index b3becad3dc5a..cdee9e386b33 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java @@ -17,6 +17,8 @@ package androidx.window.extensions.embedding; import static android.graphics.Matrix.MSCALE_X; +import static android.graphics.Matrix.MTRANS_X; +import static android.graphics.Matrix.MTRANS_Y; import android.graphics.Rect; import android.view.Choreographer; @@ -96,22 +98,20 @@ class TaskFragmentAnimationAdapter { mTarget.localBounds.left, mTarget.localBounds.top); t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix); t.setAlpha(mLeash, mTransformation.getAlpha()); - - // Open/close animation may scale up the surface. Apply an inverse scale to the window crop - // so that it will not be covering other windows. - mVecs[1] = mVecs[2] = 0; - mVecs[0] = mVecs[3] = 1; - mTransformation.getMatrix().mapVectors(mVecs); - mVecs[0] = 1.f / mVecs[0]; - mVecs[3] = 1.f / mVecs[3]; - final Rect clipRect = mTarget.localBounds; - mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f); - mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f); - mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f); - mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f); - mRect.offsetTo(Math.round(mTarget.localBounds.width() * (1 - mVecs[0]) / 2.f), - Math.round(mTarget.localBounds.height() * (1 - mVecs[3]) / 2.f)); - t.setWindowCrop(mLeash, mRect); + // Get current animation position. + final int positionX = Math.round(mMatrix[MTRANS_X]); + final int positionY = Math.round(mMatrix[MTRANS_Y]); + // The exiting surface starts at position: mTarget.localBounds and moves with + // positionX varying. Offset our crop region by the amount we have slided so crop + // regions stays exactly on the original container in split. + final int cropOffsetX = mTarget.localBounds.left - positionX; + final int cropOffsetY = mTarget.localBounds.top - positionY; + final Rect cropRect = new Rect(); + cropRect.set(mTarget.localBounds); + // Because window crop uses absolute position. + cropRect.offsetTo(0, 0); + cropRect.offset(cropOffsetX, cropOffsetY); + t.setCrop(mLeash, cropRect); } /** Called after animation finished. */ diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 6693755ee102..3bbeda9f0033 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -21,7 +21,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; -import android.app.ActivityThread; import android.app.WindowConfiguration.WindowingMode; import android.graphics.Rect; import android.os.Binder; @@ -133,9 +132,8 @@ class TaskFragmentContainer { if (mInfo == null) { return allActivities; } - ActivityThread activityThread = ActivityThread.currentActivityThread(); for (IBinder token : mInfo.getActivities()) { - Activity activity = activityThread.getActivity(token); + Activity activity = mController.getActivity(token); if (activity != null && !activity.isFinishing() && !allActivities.contains(activity)) { allActivities.add(activity); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 34cde9bca763..e8d9960c4bb3 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -16,6 +16,9 @@ package androidx.window.extensions.embedding; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -24,23 +27,31 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import android.annotation.NonNull; import android.app.Activity; +import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.util.Pair; import android.window.TaskFragmentInfo; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -53,6 +64,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -67,8 +79,8 @@ import java.util.List; public class SplitControllerTest { private static final int TASK_ID = 10; private static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200); + private static final float SPLIT_RATIO = 0.5f; - @Mock private Activity mActivity; @Mock private Resources mActivityResources; @@ -92,9 +104,9 @@ public class SplitControllerTest { final Configuration activityConfig = new Configuration(); activityConfig.windowConfiguration.setBounds(TASK_BOUNDS); activityConfig.windowConfiguration.setMaxBounds(TASK_BOUNDS); - doReturn(mActivityResources).when(mActivity).getResources(); doReturn(activityConfig).when(mActivityResources).getConfiguration(); doReturn(mHandler).when(mSplitController).getHandler(); + mActivity = createMockActivity(); } @Test @@ -260,6 +272,246 @@ public class SplitControllerTest { mSplitController.updateContainer(mTransaction, tf); - verify(mSplitPresenter).updateSplitContainer(eq(splitContainer), eq(tf), eq(mTransaction)); + verify(mSplitPresenter).updateSplitContainer(splitContainer, tf, mTransaction); + } + + @Test + public void testOnActivityReparentToTask_sameProcess() { + mSplitController.onActivityReparentToTask(TASK_ID, new Intent(), + mActivity.getActivityToken()); + + // Treated as on activity created. + verify(mSplitController).onActivityCreated(mActivity); + } + + @Test + public void testOnActivityReparentToTask_diffProcess() { + // Create an empty TaskFragment to initialize for the Task. + mSplitController.newContainer(null, mActivity, TASK_ID); + final IBinder activityToken = new Binder(); + final Intent intent = new Intent(); + + mSplitController.onActivityReparentToTask(TASK_ID, intent, activityToken); + + // Treated as starting new intent + verify(mSplitController, never()).onActivityCreated(mActivity); + verify(mSplitController).resolveStartActivityIntent(any(), eq(TASK_ID), eq(intent), + isNull()); + } + + @Test + public void testResolveStartActivityIntent_withoutLaunchingActivity() { + final Intent intent = new Intent(); + final ActivityRule expandRule = new ActivityRule.Builder(r -> false, i -> i == intent) + .setShouldAlwaysExpand(true) + .build(); + mSplitController.setEmbeddingRules(Collections.singleton(expandRule)); + + // No other activity available in the Task. + TaskFragmentContainer container = mSplitController.resolveStartActivityIntent(mTransaction, + TASK_ID, intent, null /* launchingActivity */); + assertNull(container); + + // Task contains another activity that can be used as owner activity. + createMockTaskFragmentContainer(mActivity); + container = mSplitController.resolveStartActivityIntent(mTransaction, + TASK_ID, intent, null /* launchingActivity */); + assertNotNull(container); + } + + @Test + public void testResolveStartActivityIntent_shouldExpand() { + final Intent intent = new Intent(); + setupExpandRule(intent); + final TaskFragmentContainer container = mSplitController.resolveStartActivityIntent( + mTransaction, TASK_ID, intent, mActivity); + + assertNotNull(container); + assertTrue(container.areLastRequestedBoundsEqual(null)); + assertTrue(container.isLastRequestedWindowingModeEqual(WINDOWING_MODE_UNDEFINED)); + assertFalse(container.hasActivity(mActivity.getActivityToken())); + verify(mSplitPresenter).createTaskFragment(mTransaction, container.getTaskFragmentToken(), + mActivity.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED); + } + + @Test + public void testResolveStartActivityIntent_shouldSplitWithLaunchingActivity() { + final Intent intent = new Intent(); + setupSplitRule(mActivity, intent); + + final TaskFragmentContainer container = mSplitController.resolveStartActivityIntent( + mTransaction, TASK_ID, intent, mActivity); + final TaskFragmentContainer primaryContainer = mSplitController.getContainerWithActivity( + mActivity); + + assertSplitPair(primaryContainer, container); + } + + @Test + public void testResolveStartActivityIntent_shouldSplitWithTopExpandActivity() { + final Intent intent = new Intent(); + setupSplitRule(mActivity, intent); + createMockTaskFragmentContainer(mActivity); + + final TaskFragmentContainer container = mSplitController.resolveStartActivityIntent( + mTransaction, TASK_ID, intent, null /* launchingActivity */); + final TaskFragmentContainer primaryContainer = mSplitController.getContainerWithActivity( + mActivity); + + assertSplitPair(primaryContainer, container); + } + + @Test + public void testResolveStartActivityIntent_shouldSplitWithTopSecondaryActivity() { + final Intent intent = new Intent(); + setupSplitRule(mActivity, intent); + final Activity primaryActivity = createMockActivity(); + addSplitTaskFragments(primaryActivity, mActivity); + + final TaskFragmentContainer container = mSplitController.resolveStartActivityIntent( + mTransaction, TASK_ID, intent, null /* launchingActivity */); + final TaskFragmentContainer primaryContainer = mSplitController.getContainerWithActivity( + mActivity); + + assertSplitPair(primaryContainer, container); + } + + @Test + public void testResolveStartActivityIntent_shouldSplitWithTopPrimaryActivity() { + final Intent intent = new Intent(); + setupSplitRule(mActivity, intent); + final Activity secondaryActivity = createMockActivity(); + addSplitTaskFragments(mActivity, secondaryActivity); + + final TaskFragmentContainer container = mSplitController.resolveStartActivityIntent( + mTransaction, TASK_ID, intent, null /* launchingActivity */); + final TaskFragmentContainer primaryContainer = mSplitController.getContainerWithActivity( + mActivity); + + assertSplitPair(primaryContainer, container); + } + + /** Creates a mock activity in the organizer process. */ + private Activity createMockActivity() { + final Activity activity = mock(Activity.class); + doReturn(mActivityResources).when(activity).getResources(); + final IBinder activityToken = new Binder(); + doReturn(activityToken).when(activity).getActivityToken(); + doReturn(activity).when(mSplitController).getActivity(activityToken); + return activity; + } + + /** Creates a mock TaskFragmentInfo for the given TaskFragment. */ + private TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container, + @NonNull Activity activity) { + return new TaskFragmentInfo(container.getTaskFragmentToken(), + mock(WindowContainerToken.class), + new Configuration(), + 1, + true /* isVisible */, + Collections.singletonList(activity.getActivityToken()), + new Point(), + false /* isTaskClearedForReuse */, + false /* isTaskFragmentClearedForPip */); + } + + /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */ + private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) { + final TaskFragmentContainer container = mSplitController.newContainer(activity, TASK_ID); + final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity); + container.setInfo(createMockTaskFragmentInfo(container, activity)); + mSplitPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), info); + return container; + } + + /** Setups a rule to always expand the given intent. */ + private void setupExpandRule(@NonNull Intent expandIntent) { + final ActivityRule expandRule = new ActivityRule.Builder(r -> false, expandIntent::equals) + .setShouldAlwaysExpand(true) + .build(); + mSplitController.setEmbeddingRules(Collections.singleton(expandRule)); + } + + /** Setups a rule to always split the given activities. */ + private void setupSplitRule(@NonNull Activity primaryActivity, + @NonNull Intent secondaryIntent) { + final SplitRule splitRule = createSplitRule(primaryActivity, secondaryIntent); + mSplitController.setEmbeddingRules(Collections.singleton(splitRule)); + } + + /** Creates a rule to always split the given activity and the given intent. */ + private SplitRule createSplitRule(@NonNull Activity primaryActivity, + @NonNull Intent secondaryIntent) { + final Pair<Activity, Intent> targetPair = new Pair<>(primaryActivity, secondaryIntent); + return new SplitPairRule.Builder( + activityPair -> false, + targetPair::equals, + w -> true) + .setSplitRatio(SPLIT_RATIO) + .setShouldClearTop(true) + .build(); + } + + /** Creates a rule to always split the given activities. */ + private SplitRule createSplitRule(@NonNull Activity primaryActivity, + @NonNull Activity secondaryActivity) { + final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity); + return new SplitPairRule.Builder( + targetPair::equals, + activityIntentPair -> false, + w -> true) + .setSplitRatio(SPLIT_RATIO) + .setShouldClearTop(true) + .build(); + } + + /** Adds a pair of TaskFragments as split for the given activities. */ + private void addSplitTaskFragments(@NonNull Activity primaryActivity, + @NonNull Activity secondaryActivity) { + final TaskFragmentContainer primaryContainer = createMockTaskFragmentContainer( + primaryActivity); + final TaskFragmentContainer secondaryContainer = createMockTaskFragmentContainer( + secondaryActivity); + mSplitController.registerSplit( + mock(WindowContainerTransaction.class), + primaryContainer, + primaryActivity, + secondaryContainer, + createSplitRule(primaryActivity, secondaryActivity)); + + // We need to set those in case we are not respecting clear top. + // TODO(b/231845476) we should always respect clearTop. + final int windowingMode = mSplitController.getTaskContainer(TASK_ID) + .getWindowingModeForSplitTaskFragment(TASK_BOUNDS); + primaryContainer.setLastRequestedWindowingMode(windowingMode); + secondaryContainer.setLastRequestedWindowingMode(windowingMode); + primaryContainer.setLastRequestedBounds(getSplitBounds(true /* isPrimary */)); + secondaryContainer.setLastRequestedBounds(getSplitBounds(false /* isPrimary */)); + } + + /** Gets the bounds of a TaskFragment that is in split. */ + private Rect getSplitBounds(boolean isPrimary) { + final int width = (int) (TASK_BOUNDS.width() * SPLIT_RATIO); + return isPrimary + ? new Rect(TASK_BOUNDS.left, TASK_BOUNDS.top, TASK_BOUNDS.left + width, + TASK_BOUNDS.bottom) + : new Rect(TASK_BOUNDS.left + width, TASK_BOUNDS.top, TASK_BOUNDS.right, + TASK_BOUNDS.bottom); + } + + /** Asserts that the two given TaskFragments are in split. */ + private void assertSplitPair(@NonNull TaskFragmentContainer primaryContainer, + @NonNull TaskFragmentContainer secondaryContainer) { + assertNotNull(primaryContainer); + assertNotNull(secondaryContainer); + assertTrue(primaryContainer.areLastRequestedBoundsEqual( + getSplitBounds(true /* isPrimary */))); + assertTrue(secondaryContainer.areLastRequestedBoundsEqual( + getSplitBounds(false /* isPrimary */))); + assertTrue(primaryContainer.isLastRequestedWindowingModeEqual(WINDOWING_MODE_MULTI_WINDOW)); + assertTrue(secondaryContainer.isLastRequestedWindowingModeEqual( + WINDOWING_MODE_MULTI_WINDOW)); + assertNotNull(mSplitController.getActiveSplitForContainers(primaryContainer, + secondaryContainer)); } } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index 0de94b0dc26f..f1042ab6ce7d 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -24,8 +24,12 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import android.app.Activity; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -147,4 +151,38 @@ public class TaskContainerTest { assertFalse(taskContainer.isEmpty()); } + + @Test + public void testGetTopTaskFragmentContainer() { + final TaskContainer taskContainer = new TaskContainer(TASK_ID); + assertNull(taskContainer.getTopTaskFragmentContainer()); + + final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */, + taskContainer, mController); + assertEquals(tf0, taskContainer.getTopTaskFragmentContainer()); + + final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, + taskContainer, mController); + assertEquals(tf1, taskContainer.getTopTaskFragmentContainer()); + } + + @Test + public void testGetTopNonFinishingActivity() { + final TaskContainer taskContainer = new TaskContainer(TASK_ID); + assertNull(taskContainer.getTopNonFinishingActivity()); + + final TaskFragmentContainer tf0 = mock(TaskFragmentContainer.class); + taskContainer.mContainers.add(tf0); + final Activity activity0 = mock(Activity.class); + doReturn(activity0).when(tf0).getTopNonFinishingActivity(); + assertEquals(activity0, taskContainer.getTopNonFinishingActivity()); + + final TaskFragmentContainer tf1 = mock(TaskFragmentContainer.class); + taskContainer.mContainers.add(tf1); + assertEquals(activity0, taskContainer.getTopNonFinishingActivity()); + + final Activity activity1 = mock(Activity.class); + doReturn(activity1).when(tf1).getTopNonFinishingActivity(); + assertEquals(activity1, taskContainer.getTopNonFinishingActivity()); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java index b04a1fa93bbd..f1ee8fa38485 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java @@ -183,7 +183,7 @@ public class BadgedImageView extends ConstraintLayout { getDrawingRect(mTempBounds); - mDrawParams.color = mDotColor; + mDrawParams.dotColor = mDotColor; mDrawParams.iconBounds = mTempBounds; mDrawParams.leftAlign = mOnLeft; mDrawParams.scale = mDotScale; 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 63774fb0d8ca..1d976cea96a3 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 @@ -1115,7 +1115,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); mDividerFadeInAnimator = ValueAnimator.ofFloat(0f, 1f); mDividerFadeInAnimator.addUpdateListener(animation -> { - if (dividerLeash == null) { + if (dividerLeash == null || !dividerLeash.isValid()) { mDividerFadeInAnimator.cancel(); return; } @@ -1125,7 +1125,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDividerFadeInAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - if (dividerLeash == null) { + if (dividerLeash == null || !dividerLeash.isValid()) { mDividerFadeInAnimator.cancel(); return; } diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java index 44b4662cdca5..fe58cca9395f 100644 --- a/media/java/android/media/AudioDeviceVolumeManager.java +++ b/media/java/android/media/AudioDeviceVolumeManager.java @@ -298,6 +298,28 @@ public class AudioDeviceVolumeManager { "removeOnDeviceVolumeBehaviorChangedListener"); } + /** + * Return human-readable name for volume behavior + * @param behavior one of the volume behaviors defined in AudioManager + * @return a string for the given behavior + */ + public static String volumeBehaviorName(@AudioManager.DeviceVolumeBehavior int behavior) { + switch (behavior) { + case AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE: + return "DEVICE_VOLUME_BEHAVIOR_VARIABLE"; + case AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL: + return "DEVICE_VOLUME_BEHAVIOR_FULL"; + case AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED: + return "DEVICE_VOLUME_BEHAVIOR_FIXED"; + case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE: + return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE"; + case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE: + return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE"; + default: + return "invalid volume behavior " + behavior; + } + } + private final class DeviceVolumeBehaviorDispatcherStub extends IDeviceVolumeBehaviorDispatcher.Stub implements CallbackUtil.DispatcherStub { public void register(boolean register) { diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp index f4a39b3469bb..b7ad6dcf9354 100644 --- a/media/jni/android_media_Utils.cpp +++ b/media/jni/android_media_Utils.cpp @@ -18,10 +18,10 @@ #define LOG_TAG "AndroidMediaUtils" #include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h> -#include <hardware/camera3.h> #include <ui/GraphicBufferMapper.h> #include <ui/GraphicTypes.h> #include <utils/Log.h> + #include "android_media_Utils.h" #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) ) @@ -122,8 +122,8 @@ uint32_t Image_getBlobSize(LockedImage* buffer, bool usingRGBAOverride) { } // First check for BLOB transport header at the end of the buffer - uint8_t* header = blobBuffer + (width - sizeof(struct camera3_jpeg_blob)); - struct camera3_jpeg_blob *blob = (struct camera3_jpeg_blob*)(header); + uint8_t* header = blobBuffer + (width - sizeof(struct camera3_jpeg_blob_v2)); + struct camera3_jpeg_blob_v2 *blob = (struct camera3_jpeg_blob_v2*)(header); if (blob->jpeg_blob_id == CAMERA3_JPEG_BLOB_ID || blob->jpeg_blob_id == CAMERA3_HEIC_BLOB_ID) { size = blob->jpeg_size; diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h index 4feb4f516f1e..c12cec129ede 100644 --- a/media/jni/android_media_Utils.h +++ b/media/jni/android_media_Utils.h @@ -50,6 +50,20 @@ int getBufferWidth(BufferItem *buffer); int getBufferHeight(BufferItem *buffer); +// Must be in sync with AIDL CameraBlob : android.hardware.camera.device.CameraBlob +// HALs must NOT copy this definition. +// for details: http://b/229688810 +typedef struct camera3_jpeg_blob_v2 { + uint32_t jpeg_blob_id; + uint32_t jpeg_size; +} camera3_jpeg_blobv2_t; + +// Must be in sync with AIDL CameraBlob : android.hardware.camera.device.CameraBlobId +enum { + CAMERA3_JPEG_BLOB_ID = 0x00FF, + CAMERA3_JPEG_APP_SEGMENTS_BLOB_ID = 0x0100, +}; + }; // namespace android #endif // _ANDROID_MEDIA_UTILS_H_ diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java index f333b86d4d52..fc5ff085139c 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java @@ -123,6 +123,7 @@ public class CompanionDeviceDiscoveryService extends Service { intent.setAction(ACTION_START_DISCOVERY); intent.putExtra(EXTRA_ASSOCIATION_REQUEST, associationRequest); sStateLiveData.setValue(DiscoveryState.STARTING); + sScanResultsLiveData.setValue(Collections.emptyList()); context.startService(intent); } @@ -173,7 +174,6 @@ public class CompanionDeviceDiscoveryService extends Service { @Override public void onDestroy() { - sScanResultsLiveData.setValue(Collections.emptyList()); super.onDestroy(); if (DEBUG) Log.d(TAG, "onDestroy()"); } @@ -187,7 +187,6 @@ public class CompanionDeviceDiscoveryService extends Service { mStopAfterFirstMatch = request.isSingleDevice(); mDiscoveryStarted = true; sStateLiveData.setValue(DiscoveryState.DISCOVERY_IN_PROGRESS); - sScanResultsLiveData.setValue(Collections.emptyList()); final List<DeviceFilter<?>> allFilters = request.getDeviceFilters(); final List<BluetoothDeviceFilter> btFilters = diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceFilterPair.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceFilterPair.java index faca1ae3f058..1f59d30207d8 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceFilterPair.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceFilterPair.java @@ -47,7 +47,7 @@ class DeviceFilterPair<T extends Parcelable> { } String getDisplayName() { - if (mFilter != null) mFilter.getDeviceDisplayName(mDevice); + if (mFilter != null) return mFilter.getDeviceDisplayName(mDevice); if (mDevice instanceof BluetoothDevice) { return getDeviceDisplayNameInternal((BluetoothDevice) mDevice); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 0c6d40aa9910..5088533ccdd8 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -1074,13 +1074,63 @@ public class SettingsBackupAgent extends BackupAgentHelper { if (DEBUG) Log.d(TAG, "Successfully unMarshaled SoftApConfiguration "); // Depending on device hardware, we may need to notify the user of a setting change SoftApConfiguration storedConfig = mWifiManager.getSoftApConfiguration(); - if (!storedConfig.equals(configInCloud)) { + + if (isNeedToNotifyUserConfigurationHasChanged(configInCloud, storedConfig)) { Log.d(TAG, "restored ap configuration requires a conversion, notify the user"); WifiSoftApConfigChangedNotifier.notifyUserOfConfigConversion(this); } } } + private boolean isNeedToNotifyUserConfigurationHasChanged(SoftApConfiguration configInCloud, + SoftApConfiguration storedConfig) { + // Check if the cloud configuration was modified when restored to the device. + // All elements of the configuration are compared except: + // 1. Persistent randomized MAC address (which is per device) + // 2. The flag indicating whether the configuration is "user modified" + return !(Objects.equals(configInCloud.getWifiSsid(), storedConfig.getWifiSsid()) + && Objects.equals(configInCloud.getBssid(), storedConfig.getBssid()) + && Objects.equals(configInCloud.getPassphrase(), storedConfig.getPassphrase()) + && configInCloud.isHiddenSsid() == storedConfig.isHiddenSsid() + && configInCloud.getChannels().toString().equals( + storedConfig.getChannels().toString()) + && configInCloud.getSecurityType() == storedConfig.getSecurityType() + && configInCloud.getMaxNumberOfClients() == storedConfig.getMaxNumberOfClients() + && configInCloud.isAutoShutdownEnabled() == storedConfig.isAutoShutdownEnabled() + && configInCloud.getShutdownTimeoutMillis() + == storedConfig.getShutdownTimeoutMillis() + && configInCloud.isClientControlByUserEnabled() + == storedConfig.isClientControlByUserEnabled() + && Objects.equals(configInCloud.getBlockedClientList(), + storedConfig.getBlockedClientList()) + && Objects.equals(configInCloud.getAllowedClientList(), + storedConfig.getAllowedClientList()) + && configInCloud.getMacRandomizationSetting() + == storedConfig.getMacRandomizationSetting() + && configInCloud.isBridgedModeOpportunisticShutdownEnabled() + == storedConfig.isBridgedModeOpportunisticShutdownEnabled() + && configInCloud.isIeee80211axEnabled() == storedConfig.isIeee80211axEnabled() + && configInCloud.isIeee80211beEnabled() == storedConfig.isIeee80211beEnabled() + && configInCloud.getBridgedModeOpportunisticShutdownTimeoutMillis() + == storedConfig.getBridgedModeOpportunisticShutdownTimeoutMillis() + && Objects.equals(configInCloud.getVendorElements(), + storedConfig.getVendorElements()) + && (configInCloud.getPersistentRandomizedMacAddress() != null + ? Objects.equals(configInCloud.getPersistentRandomizedMacAddress(), + storedConfig.getPersistentRandomizedMacAddress()) : true) + && Arrays.equals(configInCloud.getAllowedAcsChannels( + SoftApConfiguration.BAND_2GHZ), + storedConfig.getAllowedAcsChannels(SoftApConfiguration.BAND_2GHZ)) + && Arrays.equals(configInCloud.getAllowedAcsChannels( + SoftApConfiguration.BAND_5GHZ), + storedConfig.getAllowedAcsChannels(SoftApConfiguration.BAND_5GHZ)) + && Arrays.equals(configInCloud.getAllowedAcsChannels( + SoftApConfiguration.BAND_6GHZ), + storedConfig.getAllowedAcsChannels(SoftApConfiguration.BAND_6GHZ)) + && configInCloud.getMaxChannelBandwidth() == storedConfig.getMaxChannelBandwidth() + ); + } + private byte[] getNetworkPolicies() { NetworkPolicyManager networkPolicyManager = (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE); diff --git a/packages/SystemUI/res/drawable/ic_chevron_icon.xml b/packages/SystemUI/res/drawable/ic_chevron_icon.xml index acbbbcb21503..d60cc8c74e80 100644 --- a/packages/SystemUI/res/drawable/ic_chevron_icon.xml +++ b/packages/SystemUI/res/drawable/ic_chevron_icon.xml @@ -15,14 +15,6 @@ ~ limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="18dp" - android:height="31dp" - android:viewportWidth="18" - android:viewportHeight="31"> - <path - android:pathData="M0.0061,27.8986L2.6906,30.5831L17.9219,15.3518L2.6906,0.1206L0.0061,2.8051L12.5338,15.3518" - android:strokeAlpha="0.7" - android:fillColor="#FFFFFF" - android:fillAlpha="0.7"/> -</vector> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"> + <path android:fillColor="@android:color/white" android:pathData="M9.4,18 L8,16.6 12.6,12 8,7.4 9.4,6 15.4,12Z"/> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/media_output_status_check.xml b/packages/SystemUI/res/drawable/media_output_status_check.xml index 5fbc42b245b8..3d64f83eabe3 100644 --- a/packages/SystemUI/res/drawable/media_output_status_check.xml +++ b/packages/SystemUI/res/drawable/media_output_status_check.xml @@ -21,6 +21,6 @@ android:viewportHeight="24" android:tint="?attr/colorControlNormal"> <path - android:fillColor="@color/media_dialog_item_main_content" - android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/> + android:fillColor="@android:color/white" + android:pathData="M12,22Q9.925,22 8.1,21.212Q6.275,20.425 4.925,19.075Q3.575,17.725 2.788,15.9Q2,14.075 2,12Q2,9.925 2.788,8.1Q3.575,6.275 4.925,4.925Q6.275,3.575 8.1,2.787Q9.925,2 12,2Q14.075,2 15.9,2.787Q17.725,3.575 19.075,4.925Q20.425,6.275 21.212,8.1Q22,9.925 22,12Q22,14.075 21.212,15.9Q20.425,17.725 19.075,19.075Q17.725,20.425 15.9,21.212Q14.075,22 12,22ZM10.6,16.6 L17.65,9.55 16.25,8.15 10.6,13.8 7.75,10.95 6.35,12.35Z"/> </vector> diff --git a/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml b/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml index 1122ca6bd4dc..1c09e81f92ca 100644 --- a/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml +++ b/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml @@ -12,6 +12,7 @@ android:layout_height="48dp" android:layout_marginTop="8dp" android:layout_marginStart="12dp" + android:paddingHorizontal="16dp" android:background="@drawable/overlay_button_background" android:text="@string/clipboard_edit_text_done" app:layout_constraintStart_toStartOf="parent" diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml index 085a5810608f..1712b4876b31 100644 --- a/packages/SystemUI/res/layout/clipboard_overlay.xml +++ b/packages/SystemUI/res/layout/clipboard_overlay.xml @@ -67,9 +67,9 @@ android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="@dimen/overlay_offset_x" - android:layout_marginBottom="8dp" + android:layout_marginBottom="12dp" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toBottomOf="@id/actions_container_background" + app:layout_constraintBottom_toBottomOf="parent" android:elevation="7dp" app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end" app:layout_constraintTop_toTopOf="@id/clipboard_preview_top" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 2426f017e20e..2c3d947ba9e2 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2484,6 +2484,12 @@ <string name="clipboard_send_nearby_description">Send to nearby device</string> <!-- Text informing user that copied content is hidden [CHAR LIMIT=NONE] --> <string name="clipboard_text_hidden">Tap to view</string> + <!-- Accessibility announcement informing user that text has been copied [CHAR LIMIT=NONE] --> + <string name="clipboard_text_copied">Text copied</string> + <!-- Accessibility announcement informing user that text has been copied [CHAR LIMIT=NONE] --> + <string name="clipboard_image_copied">Image copied</string> + <!-- Accessibility announcement informing user that something has been copied [CHAR LIMIT=NONE] --> + <string name="clipboard_content_copied">Content copied</string> <!-- Generic "add" string [CHAR LIMIT=NONE] --> <string name="add">Add</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index 804d14681812..12fa401d7fea 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -71,13 +71,16 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> public void onTrustGrantedWithFlags(int flags, int userId) { if (userId != KeyguardUpdateMonitor.getCurrentUser()) return; boolean bouncerVisible = mView.isVisibleToUser(); + boolean temporaryAndRenewable = + (flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) + != 0; boolean initiatedByUser = (flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0; boolean dismissKeyguard = (flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0; if (initiatedByUser || dismissKeyguard) { - if (mViewMediatorCallback.isScreenOn() + if ((mViewMediatorCallback.isScreenOn() || temporaryAndRenewable) && (bouncerVisible || dismissKeyguard)) { if (!bouncerVisible) { // The trust agent dismissed the keyguard without the user proving diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index f8c0590a8d75..cce516d981a5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -15,6 +15,8 @@ */ package com.android.keyguard; +import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_DIALOG_FAILED_ATTEMPTS_ALMOST_ERASING_PROFILE; +import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; @@ -32,6 +34,7 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.Activity; import android.app.AlertDialog; +import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -676,7 +679,11 @@ public class KeyguardSecurityContainer extends FrameLayout { attempts, remaining); break; case USER_TYPE_WORK_PROFILE: - message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile, + message = mContext.getSystemService(DevicePolicyManager.class).getResources() + .getString(KEYGUARD_DIALOG_FAILED_ATTEMPTS_ALMOST_ERASING_PROFILE, + () -> mContext.getString( + R.string.kg_failed_attempts_almost_at_erase_profile, + attempts, remaining), attempts, remaining); break; } @@ -695,7 +702,10 @@ public class KeyguardSecurityContainer extends FrameLayout { attempts); break; case USER_TYPE_WORK_PROFILE: - message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile, + message = mContext.getSystemService(DevicePolicyManager.class).getResources() + .getString(KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE, + () -> mContext.getString( + R.string.kg_failed_attempts_now_erasing_profile, attempts), attempts); break; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index e0f1b657e48c..13690f30ab3b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -2417,7 +2417,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Triggers: final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant(); final boolean awakeKeyguard = mBouncerFullyShown || mUdfpsBouncerShowing - || (mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep + || (mKeyguardIsVisible && !mGoingToSleep && mStatusBarState != StatusBarState.SHADE_LOCKED); // Gates: diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index d79b1454514e..ab831be0f8e0 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -123,7 +123,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private float mHeightPixels; private float mWidthPixels; private int mBottomPaddingPx; - private int mScaledPaddingPx; + private int mDefaultPaddingPx; private boolean mShowUnlockIcon; private boolean mShowLockIcon; @@ -340,6 +340,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mWidthPixels = bounds.right; mHeightPixels = bounds.bottom; mBottomPaddingPx = getResources().getDimensionPixelSize(R.dimen.lock_icon_margin_bottom); + mDefaultPaddingPx = + getResources().getDimensionPixelSize(R.dimen.lock_icon_padding); mUnlockedLabel = mView.getContext().getResources().getString( R.string.accessibility_unlock_button); @@ -348,17 +350,16 @@ public class LockIconViewController extends ViewController<LockIconView> impleme } private void updateLockIconLocation() { + final float scaleFactor = mAuthController.getScaleFactor(); + final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor); if (mUdfpsSupported) { - final int defaultPaddingPx = - getResources().getDimensionPixelSize(R.dimen.lock_icon_padding); - mScaledPaddingPx = (int) (defaultPaddingPx * mAuthController.getScaleFactor()); mView.setCenterLocation(mAuthController.getUdfpsLocation(), - mAuthController.getUdfpsRadius(), mScaledPaddingPx); + mAuthController.getUdfpsRadius(), scaledPadding); } else { mView.setCenterLocation( new PointF(mWidthPixels / 2, - mHeightPixels - mBottomPaddingPx - sLockIconRadiusPx), - sLockIconRadiusPx, mScaledPaddingPx); + mHeightPixels - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor)), + sLockIconRadiusPx * scaleFactor, scaledPadding); } } diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt index e51a63fbcd10..032a27a6fbcd 100644 --- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt +++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt @@ -54,7 +54,7 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) @JvmField val displayInfo = DisplayInfo() - @JvmField protected var pendingRotationChange = false + @JvmField protected var pendingConfigChange = false @JvmField protected val paint = Paint() @JvmField protected val cutoutPath = Path() @@ -145,7 +145,7 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) open fun updateCutout() { - if (pendingRotationChange) { + if (pendingConfigChange) { return } cutoutPath.reset() @@ -225,7 +225,7 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { } protected open fun updateProtectionBoundingPath() { - if (pendingRotationChange) { + if (pendingConfigChange) { return } val m = Matrix() diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index ca731c50f48c..685c585a52be 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -53,6 +53,7 @@ import android.provider.Settings.Secure; import android.util.DisplayUtils; import android.util.Log; import android.util.Size; +import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayCutout.BoundsPosition; import android.view.DisplayInfo; @@ -151,12 +152,13 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab private SettingObserver mColorInversionSetting; private DelayableExecutor mExecutor; private Handler mHandler; - boolean mPendingRotationChange; + boolean mPendingConfigChange; @VisibleForTesting String mDisplayUniqueId; private int mTintColor = Color.BLACK; @VisibleForTesting protected DisplayDecorationSupport mHwcScreenDecorationSupport; + private Display.Mode mDisplayMode; private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback = new CameraAvailabilityListener.CameraTransitionCallback() { @@ -324,6 +326,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mWindowManager = mContext.getSystemService(WindowManager.class); mDisplayManager = mContext.getSystemService(DisplayManager.class); mRotation = mContext.getDisplay().getRotation(); + mDisplayMode = mContext.getDisplay().getMode(); mDisplayUniqueId = mContext.getDisplay().getUniqueId(); mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(), mDisplayUniqueId); @@ -349,8 +352,10 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab @Override public void onDisplayChanged(int displayId) { final int newRotation = mContext.getDisplay().getRotation(); + final Display.Mode newDisplayMode = mContext.getDisplay().getMode(); if ((mOverlays != null || mScreenDecorHwcWindow != null) - && mRotation != newRotation) { + && (mRotation != newRotation + || displayModeChanged(mDisplayMode, newDisplayMode))) { // We cannot immediately update the orientation. Otherwise // WindowManager is still deferring layout until it has finished dispatching // the config changes, which may cause divergence between what we draw @@ -358,10 +363,16 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab // Instead we wait until either: // - we are trying to redraw. This because WM resized our window and told us to. // - the config change has been dispatched, so WM is no longer deferring layout. - mPendingRotationChange = true; + mPendingConfigChange = true; if (DEBUG) { - Log.i(TAG, "Rotation changed, deferring " + newRotation + ", staying at " - + mRotation); + if (mRotation != newRotation) { + Log.i(TAG, "Rotation changed, deferring " + newRotation + + ", staying at " + mRotation); + } + if (displayModeChanged(mDisplayMode, newDisplayMode)) { + Log.i(TAG, "Resolution changed, deferring " + newDisplayMode + + ", staying at " + mDisplayMode); + } } if (mOverlays != null) { @@ -369,7 +380,8 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab if (mOverlays[i] != null) { final ViewGroup overlayView = mOverlays[i].getRootView(); overlayView.getViewTreeObserver().addOnPreDrawListener( - new RestartingPreDrawListener(overlayView, i, newRotation)); + new RestartingPreDrawListener( + overlayView, i, newRotation, newDisplayMode)); } } } @@ -379,7 +391,10 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab new RestartingPreDrawListener( mScreenDecorHwcWindow, -1, // Pass -1 for views with no specific position. - newRotation)); + newRotation, newDisplayMode)); + } + if (mScreenDecorHwcLayer != null) { + mScreenDecorHwcLayer.pendingConfigChange = true; } } @@ -435,7 +450,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab }; mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); - updateOrientation(); + updateConfiguration(); } @Nullable @@ -807,6 +822,17 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } } + private static boolean displayModeChanged(Display.Mode oldMode, Display.Mode newMode) { + if (oldMode == null) { + return true; + } + + // We purposely ignore refresh rate and id changes here, because we don't need to + // invalidate for those, and they can trigger the refresh rate to increase + return oldMode.getPhysicalWidth() != newMode.getPhysicalWidth() + || oldMode.getPhysicalHeight() != newMode.getPhysicalHeight(); + } + private int getOverlayWindowGravity(@BoundsPosition int pos) { final int rotated = getBoundPositionFromRotation(pos, mRotation); switch (rotated) { @@ -913,8 +939,8 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mExecutor.execute(() -> { int oldRotation = mRotation; - mPendingRotationChange = false; - updateOrientation(); + mPendingConfigChange = false; + updateConfiguration(); if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation); setupDecorations(); if (mOverlays != null) { @@ -941,7 +967,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab pw.println(" DEBUG_DISABLE_SCREEN_DECORATIONS:" + DEBUG_DISABLE_SCREEN_DECORATIONS); pw.println(" mIsPrivacyDotEnabled:" + isPrivacyDotEnabled()); pw.println(" isOnlyPrivacyDotInSwLayer:" + isOnlyPrivacyDotInSwLayer()); - pw.println(" mPendingRotationChange:" + mPendingRotationChange); + pw.println(" mPendingConfigChange:" + mPendingConfigChange); if (mHwcScreenDecorationSupport != null) { pw.println(" mHwcScreenDecorationSupport:"); pw.println(" format=" @@ -973,7 +999,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mRoundedCornerResDelegate.dump(pw, args); } - private void updateOrientation() { + private void updateConfiguration() { Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(), "must call on " + mHandler.getLooper().getThread() + ", but was " + Thread.currentThread()); @@ -982,11 +1008,14 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab if (mRotation != newRotation) { mDotViewController.setNewRotation(newRotation); } + final Display.Mode newMod = mContext.getDisplay().getMode(); - if (!mPendingRotationChange && newRotation != mRotation) { + if (!mPendingConfigChange + && (newRotation != mRotation || displayModeChanged(mDisplayMode, newMod))) { mRotation = newRotation; + mDisplayMode = newMod; if (mScreenDecorHwcLayer != null) { - mScreenDecorHwcLayer.pendingRotationChange = false; + mScreenDecorHwcLayer.pendingConfigChange = false; mScreenDecorHwcLayer.updateRotation(mRotation); updateHwLayerRoundedCornerExistAndSize(); updateHwLayerRoundedCornerDrawable(); @@ -1197,7 +1226,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) @Override public void updateCutout() { - if (!isAttachedToWindow() || pendingRotationChange) { + if (!isAttachedToWindow() || pendingConfigChange) { return; } mPosition = getBoundPositionFromRotation(mInitialPosition, mRotation); @@ -1338,40 +1367,47 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab private final View mView; private final int mTargetRotation; + private final Display.Mode mTargetDisplayMode; // Pass -1 for ScreenDecorHwcLayer since it's a fullscreen window and has no specific // position. private final int mPosition; private RestartingPreDrawListener(View view, @BoundsPosition int position, - int targetRotation) { + int targetRotation, Display.Mode targetDisplayMode) { mView = view; mTargetRotation = targetRotation; + mTargetDisplayMode = targetDisplayMode; mPosition = position; } @Override public boolean onPreDraw() { mView.getViewTreeObserver().removeOnPreDrawListener(this); - - if (mTargetRotation == mRotation) { + if (mTargetRotation == mRotation + && !displayModeChanged(mDisplayMode, mTargetDisplayMode)) { if (DEBUG) { final String title = mPosition < 0 ? "ScreenDecorHwcLayer" : getWindowTitleByPos(mPosition); Log.i(TAG, title + " already in target rot " - + mTargetRotation + ", allow draw without restarting it"); + + mTargetRotation + " and in target resolution " + + mTargetDisplayMode.getPhysicalWidth() + "x" + + mTargetDisplayMode.getPhysicalHeight() + + ", allow draw without restarting it"); } return true; } - mPendingRotationChange = false; + mPendingConfigChange = false; // This changes the window attributes - we need to restart the traversal for them to // take effect. - updateOrientation(); + updateConfiguration(); if (DEBUG) { final String title = mPosition < 0 ? "ScreenDecorHwcLayer" : getWindowTitleByPos(mPosition); Log.i(TAG, title - + " restarting listener fired, restarting draw for rot " + mRotation); + + " restarting listener fired, restarting draw for rot " + mRotation + + ", resolution " + mDisplayMode.getPhysicalWidth() + "x" + + mDisplayMode.getPhysicalHeight()); } mView.invalidate(); return false; @@ -1379,8 +1415,8 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } /** - * A pre-draw listener, that validates that the rotation we draw in matches the displays - * rotation before continuing the draw. + * A pre-draw listener, that validates that the rotation and display resolution we draw in + * matches the display's rotation and resolution before continuing the draw. * * This is to prevent a race condition, where we have not received the display changed event * yet, and would thus draw in an old orientation. @@ -1396,10 +1432,20 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab @Override public boolean onPreDraw() { final int displayRotation = mContext.getDisplay().getRotation(); - if (displayRotation != mRotation && !mPendingRotationChange) { + final Display.Mode displayMode = mContext.getDisplay().getMode(); + if (displayRotation != mRotation && displayModeChanged(mDisplayMode, displayMode) + && !mPendingConfigChange) { if (DEBUG) { - Log.i(TAG, "Drawing rot " + mRotation + ", but display is at rot " - + displayRotation + ". Restarting draw"); + if (displayRotation != mRotation) { + Log.i(TAG, "Drawing rot " + mRotation + ", but display is at rot " + + displayRotation + ". Restarting draw"); + } + if (displayModeChanged(mDisplayMode, displayMode)) { + Log.i(TAG, "Drawing at " + mDisplayMode.getPhysicalWidth() + + "x" + mDisplayMode.getPhysicalHeight() + ", but display is at " + + displayMode.getPhysicalWidth() + "x" + + displayMode.getPhysicalHeight() + ". Restarting draw"); + } } mView.invalidate(); return false; diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index 50550567ac16..d8f4fa4096d0 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -302,6 +302,7 @@ public class ClipboardOverlayController { mExitAnimator.cancel(); } reset(); + String accessibilityAnnouncement; boolean isSensitive = clipData != null && clipData.getDescription().getExtras() != null && clipData.getDescription().getExtras() @@ -310,6 +311,7 @@ public class ClipboardOverlayController { showTextPreview( mContext.getResources().getString(R.string.clipboard_overlay_text_copied), mTextPreview); + accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied); } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) { ClipData.Item item = clipData.getItemAt(0); if (item.getTextLinks() != null) { @@ -321,13 +323,18 @@ public class ClipboardOverlayController { } else { showEditableText(item.getText(), false); } + accessibilityAnnouncement = mContext.getString(R.string.clipboard_text_copied); } else if (clipData.getItemAt(0).getUri() != null) { - // How to handle non-image URIs? - showEditableImage(clipData.getItemAt(0).getUri(), isSensitive); + if (tryShowEditableImage(clipData.getItemAt(0).getUri(), isSensitive)) { + accessibilityAnnouncement = mContext.getString(R.string.clipboard_image_copied); + } else { + accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied); + } } else { showTextPreview( mContext.getResources().getString(R.string.clipboard_overlay_text_copied), mTextPreview); + accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied); } Intent remoteCopyIntent = getRemoteCopyIntent(clipData); // Only show remote copy if it's available. @@ -344,7 +351,12 @@ public class ClipboardOverlayController { } else { mRemoteCopyChip.setVisibility(View.GONE); } - withWindowAttached(() -> mView.post(this::animateIn)); + withWindowAttached(() -> { + updateInsets( + mWindowManager.getCurrentWindowMetrics().getWindowInsets()); + mView.post(this::animateIn); + mView.announceForAccessibility(accessibilityAnnouncement); + }); mTimeoutHandler.resetTimeout(); } @@ -476,33 +488,46 @@ public class ClipboardOverlayController { textView.setOnClickListener(listener); } - private void showEditableImage(Uri uri, boolean isSensitive) { - mEditChip.setAlpha(1f); - mActionContainerBackground.setVisibility(View.VISIBLE); + private boolean tryShowEditableImage(Uri uri, boolean isSensitive) { View.OnClickListener listener = v -> editImage(uri); + ContentResolver resolver = mContext.getContentResolver(); + String mimeType = resolver.getType(uri); + boolean isEditableImage = mimeType != null && mimeType.startsWith("image"); if (isSensitive) { showSinglePreview(mHiddenImagePreview); - mHiddenImagePreview.setOnClickListener(listener); - } else { - showSinglePreview(mImagePreview); - ContentResolver resolver = mContext.getContentResolver(); + if (isEditableImage) { + mHiddenImagePreview.setOnClickListener(listener); + } + } else if (isEditableImage) { // if the MIMEtype is image, try to load try { int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale); // The width of the view is capped, height maintains aspect ratio, so allow it to be // taller if needed. Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null); + showSinglePreview(mImagePreview); mImagePreview.setImageBitmap(thumbnail); + mImagePreview.setOnClickListener(listener); } catch (IOException e) { Log.e(TAG, "Thumbnail loading failed", e); showTextPreview( mContext.getResources().getString(R.string.clipboard_overlay_text_copied), mTextPreview); + isEditableImage = false; } - mImagePreview.setOnClickListener(listener); + } else { + showTextPreview( + mContext.getResources().getString(R.string.clipboard_overlay_text_copied), + mTextPreview); } - mEditChip.setOnClickListener(listener); - mEditChip.setContentDescription( - mContext.getString(R.string.clipboard_edit_image_description)); + if (isEditableImage) { + mEditChip.setVisibility(View.VISIBLE); + mEditChip.setAlpha(1f); + mActionContainerBackground.setVisibility(View.VISIBLE); + mEditChip.setOnClickListener(listener); + mEditChip.setContentDescription( + mContext.getString(R.string.clipboard_edit_image_description)); + } + return isEditableImage; } private Intent getRemoteCopyIntent(ClipData clipData) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index a4f9f3a9bc08..6a9aaf865251 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -472,7 +472,6 @@ class ControlViewHolder( updateContentDescription() status.setTextColor(color) - chevronIcon.imageTintList = color control?.getCustomIcon()?.let { icon.setImageIcon(it) @@ -495,10 +494,13 @@ class ControlViewHolder( icon.imageTintList = color } } + + chevronIcon.imageTintList = icon.imageTintList } private fun setEnabled(enabled: Boolean) { - status.setEnabled(enabled) - icon.setEnabled(enabled) + status.isEnabled = enabled + icon.isEnabled = enabled + chevronIcon.isEnabled = enabled } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 404e5311961b..6dfc5e192abb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -44,6 +44,7 @@ import com.android.systemui.shared.system.QuickStepContract import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController import com.android.systemui.shared.system.smartspace.SmartspaceState +import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.policy.KeyguardStateController @@ -142,7 +143,8 @@ class KeyguardUnlockAnimationController @Inject constructor( private val keyguardViewController: KeyguardViewController, private val featureFlags: FeatureFlags, private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>, - private val statusBarStateController: SysuiStatusBarStateController + private val statusBarStateController: SysuiStatusBarStateController, + private val notificationShadeWindowController: NotificationShadeWindowController ) : KeyguardStateController.Callback, ISysuiUnlockAnimationController.Stub() { interface KeyguardUnlockAnimationListener { @@ -362,6 +364,9 @@ class KeyguardUnlockAnimationController @Inject constructor( */ fun canPerformInWindowLauncherAnimations(): Boolean { return isNexusLauncherUnderneath() && + // If the launcher is underneath, but we're about to launch an activity, don't do + // the animations since they won't be visible. + !notificationShadeWindowController.isLaunchingActivity && launcherUnlockController != null && !keyguardStateController.isDismissingFromSwipe && // Temporarily disable for foldables since foldable launcher has two first pages, @@ -413,7 +418,6 @@ class KeyguardUnlockAnimationController @Inject constructor( (lockscreenSmartspace as BcSmartspaceDataPlugin.SmartspaceView?)?.selectedPage ?: -1 try { - // Let the launcher know to prepare for this animation. launcherUnlockController?.prepareForUnlock( willUnlockWithSmartspaceTransition, /* willAnimateSmartspace */ diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index 8dcca3d55c28..1a727f8c3323 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -76,6 +76,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements private static final String PREF_NAME = "MediaOutputDialog"; private static final String PREF_IS_LE_BROADCAST_FIRST_LAUNCH = "PrefIsLeBroadcastFirstLaunch"; private static final boolean DEBUG = true; + private static final int HANDLE_BROADCAST_FAILED_DELAY = 3000; private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); private final RecyclerView.LayoutManager mLayoutManager; @@ -119,7 +120,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements Log.d(TAG, "onBroadcastStarted(), reason = " + reason + ", broadcastId = " + broadcastId); } - mMainThreadHandler.post(() -> startLeBroadcastDialog()); + mMainThreadHandler.post(() -> handleLeBroadcastStarted()); } @Override @@ -127,7 +128,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements if (DEBUG) { Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason); } - handleLeBroadcastStartFailed(); + mMainThreadHandler.postDelayed(() -> handleLeBroadcastStartFailed(), + HANDLE_BROADCAST_FAILED_DELAY); } @Override @@ -137,7 +139,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId + ", metadata = " + metadata); } - mMainThreadHandler.post(() -> refresh()); + mMainThreadHandler.post(() -> handleLeBroadcastMetadataChanged()); } @Override @@ -146,7 +148,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements Log.d(TAG, "onBroadcastStopped(), reason = " + reason + ", broadcastId = " + broadcastId); } - mMainThreadHandler.post(() -> refresh()); + mMainThreadHandler.post(() -> handleLeBroadcastStopped()); } @Override @@ -154,7 +156,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements if (DEBUG) { Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason); } - mMainThreadHandler.post(() -> refresh()); + mMainThreadHandler.post(() -> handleLeBroadcastStopFailed()); } @Override @@ -163,7 +165,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements Log.d(TAG, "onBroadcastUpdated(), reason = " + reason + ", broadcastId = " + broadcastId); } - mMainThreadHandler.post(() -> refresh()); + mMainThreadHandler.post(() -> handleLeBroadcastUpdated()); } @Override @@ -172,7 +174,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements Log.d(TAG, "onBroadcastUpdateFailed(), reason = " + reason + ", broadcastId = " + broadcastId); } - mMainThreadHandler.post(() -> refresh()); + mMainThreadHandler.post(() -> handleLeBroadcastUpdateFailed()); } @Override @@ -384,10 +386,34 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements Bitmap.createScaledBitmap(bitmap, size, size, false)); } - protected void handleLeBroadcastStartFailed() { + public void handleLeBroadcastStarted() { + startLeBroadcastDialog(); + } + + public void handleLeBroadcastStartFailed() { mStopButton.setText(R.string.media_output_broadcast_start_failed); mStopButton.setEnabled(false); - mMainThreadHandler.postDelayed(() -> refresh(), 3000); + refresh(); + } + + public void handleLeBroadcastMetadataChanged() { + refresh(); + } + + public void handleLeBroadcastStopped() { + refresh(); + } + + public void handleLeBroadcastStopFailed() { + refresh(); + } + + public void handleLeBroadcastUpdated() { + refresh(); + } + + public void handleLeBroadcastUpdateFailed() { + refresh(); } protected void startLeBroadcast() { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java index dd4f1d6c9015..8f065461c22d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java @@ -61,6 +61,10 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { private ImageView mBroadcastCodeEdit; private AlertDialog mAlertDialog; private TextView mBroadcastErrorMessage; + private int mRetryCount = 0; + private String mCurrentBroadcastName; + private String mCurrentBroadcastCode; + private boolean mIsStopbyUpdateBroadcastCode = false; static final int METADATA_BROADCAST_NAME = 0; static final int METADATA_BROADCAST_CODE = 1; @@ -144,8 +148,6 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { //init UI component mBroadcastQrCodeView = getDialogView().requireViewById(R.id.qrcode_view); - //Set the QR code view - setQrCodeView(); mBroadcastNotify = getDialogView().requireViewById(R.id.broadcast_info); mBroadcastNotify.setOnClickListener(v -> { @@ -171,8 +173,16 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { launchBroadcastUpdatedDialog(true, mBroadcastCode.getText().toString()); }); - mBroadcastName.setText(getBroadcastMetadataInfo(METADATA_BROADCAST_NAME)); - mBroadcastCode.setText(getBroadcastMetadataInfo(METADATA_BROADCAST_CODE)); + refreshUi(); + } + + private void refreshUi() { + setQrCodeView(); + + mCurrentBroadcastName = getBroadcastMetadataInfo(METADATA_BROADCAST_NAME); + mCurrentBroadcastCode = getBroadcastMetadataInfo(METADATA_BROADCAST_CODE); + mBroadcastName.setText(mCurrentBroadcastName); + mBroadcastCode.setText(mCurrentBroadcastCode); } private void inflateBroadcastInfoArea() { @@ -239,52 +249,99 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { } if (isBroadcastCode) { - handleBroadcastCodeUpdated(updatedString); + /* If the user wants to update the Broadcast Code, the Broadcast session should be + * stopped then used the new Broadcast code to start the Broadcast. + */ + mIsStopbyUpdateBroadcastCode = true; + mMediaOutputController.setBroadcastCode(updatedString); + if (!mMediaOutputController.stopBluetoothLeBroadcast()) { + handleLeBroadcastStopFailed(); + return; + } } else { - handleBroadcastNameUpdated(updatedString); + /* If the user wants to update the Broadcast Name, we don't need to stop the Broadcast + * session. Only use the new Broadcast name to update the broadcast session. + */ + mMediaOutputController.setBroadcastName(updatedString); + if (!mMediaOutputController.updateBluetoothLeBroadcast()) { + handleLeBroadcastUpdateFailed(); + } } } - private void handleBroadcastNameUpdated(String name) { - // TODO(b/230473995) Add the retry mechanism and error handling when update fails - String currentName = mMediaOutputController.getBroadcastName(); - int retryCount = MAX_BROADCAST_INFO_UPDATE; - mMediaOutputController.setBroadcastName(name); - if (!mMediaOutputController.updateBluetoothLeBroadcast()) { - mMediaOutputController.setBroadcastName(currentName); - handleLeUpdateBroadcastFailed(retryCount); + @Override + public void handleLeBroadcastStarted() { + mRetryCount = 0; + if (mAlertDialog != null) { + mAlertDialog.dismiss(); } + refreshUi(); } - private void handleBroadcastCodeUpdated(String newPassword) { - // TODO(b/230473995) Add the retry mechanism and error handling when update fails - String currentPassword = mMediaOutputController.getBroadcastCode(); - int retryCount = MAX_BROADCAST_INFO_UPDATE; - if (!mMediaOutputController.stopBluetoothLeBroadcast()) { - mMediaOutputController.setBroadcastCode(currentPassword); - handleLeUpdateBroadcastFailed(retryCount); - return; + @Override + public void handleLeBroadcastStartFailed() { + mMediaOutputController.setBroadcastCode(mCurrentBroadcastCode); + mRetryCount++; + + handleUpdateFailedUi(); + } + + @Override + public void handleLeBroadcastMetadataChanged() { + refreshUi(); + } + + @Override + public void handleLeBroadcastUpdated() { + mRetryCount = 0; + if (mAlertDialog != null) { + mAlertDialog.dismiss(); } + refreshUi(); + } - mMediaOutputController.setBroadcastCode(newPassword); - if (!mMediaOutputController.startBluetoothLeBroadcast()) { - mMediaOutputController.setBroadcastCode(currentPassword); - handleLeUpdateBroadcastFailed(retryCount); - return; + @Override + public void handleLeBroadcastUpdateFailed() { + //Change the value in shared preferences back to it original value + mMediaOutputController.setBroadcastName(mCurrentBroadcastName); + mRetryCount++; + + handleUpdateFailedUi(); + } + + @Override + public void handleLeBroadcastStopped() { + if (mIsStopbyUpdateBroadcastCode) { + mIsStopbyUpdateBroadcastCode = false; + mRetryCount = 0; + if (!mMediaOutputController.startBluetoothLeBroadcast()) { + handleLeBroadcastStartFailed(); + return; + } + } else { + dismiss(); } + } + + @Override + public void handleLeBroadcastStopFailed() { + //Change the value in shared preferences back to it original value + mMediaOutputController.setBroadcastCode(mCurrentBroadcastCode); + mRetryCount++; - mAlertDialog.dismiss(); + handleUpdateFailedUi(); } - private void handleLeUpdateBroadcastFailed(int retryCount) { + private void handleUpdateFailedUi() { final Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE); mBroadcastErrorMessage.setVisibility(View.VISIBLE); - if (retryCount < MAX_BROADCAST_INFO_UPDATE) { + if (mRetryCount < MAX_BROADCAST_INFO_UPDATE) { if (positiveBtn != null) { positiveBtn.setEnabled(true); } mBroadcastErrorMessage.setText(R.string.media_output_broadcast_update_error); } else { + mRetryCount = 0; mBroadcastErrorMessage.setText(R.string.media_output_broadcast_last_update_error); } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java index d27b71673ce5..622f5a279a5f 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java @@ -273,9 +273,8 @@ public class KeyButtonView extends ImageView implements ButtonInterface { mLongClicked = false; setPressed(true); - // Use raw X and Y to detect gestures in case a parent changes the x and y values - mTouchDownX = (int) ev.getRawX(); - mTouchDownY = (int) ev.getRawY(); + mTouchDownX = (int) ev.getX(); + mTouchDownY = (int) ev.getY(); if (mCode != KEYCODE_UNKNOWN) { sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime); } else { @@ -289,8 +288,8 @@ public class KeyButtonView extends ImageView implements ButtonInterface { postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout()); break; case MotionEvent.ACTION_MOVE: - x = (int)ev.getRawX(); - y = (int)ev.getRawY(); + x = (int) ev.getX(); + y = (int) ev.getY(); float slop = QuickStepContract.getQuickStepTouchSlopPx(getContext()); if (Math.abs(x - mTouchDownX) > slop || Math.abs(y - mTouchDownY) > slop) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index dd99db49c1d2..584de6e8c28f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -22,6 +22,9 @@ import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_NETWORK; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_TITLE; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN; +import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MONITORING_CA_CERT_SUBTITLE; +import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MONITORING_NETWORK_SUBTITLE; +import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MONITORING_VPN_SUBTITLE; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_NAMED_MANAGEMENT; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_VIEW_POLICIES; @@ -92,6 +95,7 @@ import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.util.ViewController; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; import javax.inject.Inject; import javax.inject.Named; @@ -106,7 +110,7 @@ class QSSecurityFooter extends ViewController<View> private final TextView mFooterText; private final ImageView mPrimaryFooterIcon; - private final Context mContext; + private Context mContext; private final DevicePolicyManager mDpm; private final Callback mCallback = new Callback(); private final SecurityController mSecurityController; @@ -141,6 +145,63 @@ class QSSecurityFooter extends ViewController<View> } }; + private Supplier<String> mManagementTitleSupplier = () -> + mContext == null ? null : mContext.getString(R.string.monitoring_title_device_owned); + + private Supplier<String> mManagementMessageSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.quick_settings_disclosure_management); + + private Supplier<String> mManagementMonitoringStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.quick_settings_disclosure_management_monitoring); + + private Supplier<String> mManagementMultipleVpnStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.quick_settings_disclosure_management_vpns); + + private Supplier<String> mWorkProfileMonitoringStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.quick_settings_disclosure_managed_profile_monitoring); + + private Supplier<String> mWorkProfileNetworkStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.quick_settings_disclosure_managed_profile_network_activity); + + private Supplier<String> mMonitoringSubtitleCaCertStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.monitoring_subtitle_ca_certificate); + + private Supplier<String> mMonitoringSubtitleNetworkStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.monitoring_subtitle_network_logging); + + private Supplier<String> mMonitoringSubtitleVpnStringSupplier = () -> + mContext == null ? null : mContext.getString(R.string.monitoring_subtitle_vpn); + + private Supplier<String> mViewPoliciesButtonStringSupplier = () -> + mContext == null ? null : mContext.getString(R.string.monitoring_button_view_policies); + + private Supplier<String> mManagementDialogStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.monitoring_description_management); + + private Supplier<String> mManagementDialogCaCertStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.monitoring_description_management_ca_certificate); + + private Supplier<String> mWorkProfileDialogCaCertStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.monitoring_description_managed_profile_ca_certificate); + + private Supplier<String> mManagementDialogNetworkStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.monitoring_description_management_network_logging); + + private Supplier<String> mWorkProfileDialogNetworkStringSupplier = () -> + mContext == null ? null : mContext.getString( + R.string.monitoring_description_managed_profile_network_logging); + @Inject QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, UserTracker userTracker, @Main Handler mainHandler, @@ -337,9 +398,7 @@ class QSSecurityFooter extends ViewController<View> private String getManagedDeviceMonitoringText(CharSequence organizationName) { if (organizationName == null) { return mDpm.getResources().getString( - QS_MSG_MANAGEMENT_MONITORING, - () -> mContext.getString( - R.string.quick_settings_disclosure_management_monitoring)); + QS_MSG_MANAGEMENT_MONITORING, mManagementMonitoringStringSupplier); } return mDpm.getResources().getString( QS_MSG_NAMED_MANAGEMENT_MONITORING, @@ -354,9 +413,7 @@ class QSSecurityFooter extends ViewController<View> if (vpnName != null && vpnNameWorkProfile != null) { if (organizationName == null) { return mDpm.getResources().getString( - QS_MSG_MANAGEMENT_MULTIPLE_VPNS, - () -> mContext.getString( - R.string.quick_settings_disclosure_management_vpns)); + QS_MSG_MANAGEMENT_MULTIPLE_VPNS, mManagementMultipleVpnStringSupplier); } return mDpm.getResources().getString( QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS, @@ -386,10 +443,7 @@ class QSSecurityFooter extends ViewController<View> private String getMangedDeviceGeneralText(CharSequence organizationName) { if (organizationName == null) { - return mDpm.getResources().getString( - QS_MSG_MANAGEMENT, - () -> mContext.getString( - R.string.quick_settings_disclosure_management)); + return mDpm.getResources().getString(QS_MSG_MANAGEMENT, mManagementMessageSupplier); } if (isFinancedDevice()) { return mContext.getString( @@ -431,9 +485,7 @@ class QSSecurityFooter extends ViewController<View> if (hasCACertsInWorkProfile && isWorkProfileOn) { if (workProfileOrganizationName == null) { return mDpm.getResources().getString( - QS_MSG_WORK_PROFILE_MONITORING, - () -> mContext.getString( - R.string.quick_settings_disclosure_managed_profile_monitoring)); + QS_MSG_WORK_PROFILE_MONITORING, mWorkProfileMonitoringStringSupplier); } return mDpm.getResources().getString( QS_MSG_NAMED_WORK_PROFILE_MONITORING, @@ -478,10 +530,9 @@ class QSSecurityFooter extends ViewController<View> private String getManagedProfileNetworkActivityText() { return mDpm.getResources().getString( - QS_MSG_WORK_PROFILE_NETWORK, - () -> mContext.getString( - R.string.quick_settings_disclosure_managed_profile_network_activity)); + QS_MSG_WORK_PROFILE_NETWORK, mWorkProfileNetworkStringSupplier); } + @Override public void onClick(DialogInterface dialog, int which) { if (which == DialogInterface.BUTTON_NEGATIVE) { @@ -569,6 +620,12 @@ class QSSecurityFooter extends ViewController<View> caCertsWarning.setText(caCertsMessage); // Make "Open trusted credentials"-link clickable caCertsWarning.setMovementMethod(new LinkMovementMethod()); + + TextView caCertsSubtitle = (TextView) dialogView.findViewById(R.id.ca_certs_subtitle); + String caCertsSubtitleMessage = mDpm.getResources().getString( + QS_DIALOG_MONITORING_CA_CERT_SUBTITLE, mMonitoringSubtitleCaCertStringSupplier); + caCertsSubtitle.setText(caCertsSubtitleMessage); + } // network logging section @@ -581,6 +638,13 @@ class QSSecurityFooter extends ViewController<View> TextView networkLoggingWarning = (TextView) dialogView.findViewById(R.id.network_logging_warning); networkLoggingWarning.setText(networkLoggingMessage); + + TextView networkLoggingSubtitle = (TextView) dialogView.findViewById( + R.id.network_logging_subtitle); + String networkLoggingSubtitleMessage = mDpm.getResources().getString( + QS_DIALOG_MONITORING_NETWORK_SUBTITLE, + mMonitoringSubtitleNetworkStringSupplier); + networkLoggingSubtitle.setText(networkLoggingSubtitleMessage); } // vpn section @@ -594,6 +658,11 @@ class QSSecurityFooter extends ViewController<View> vpnWarning.setText(vpnMessage); // Make "Open VPN Settings"-link clickable vpnWarning.setMovementMethod(new LinkMovementMethod()); + + TextView vpnSubtitle = (TextView) dialogView.findViewById(R.id.vpn_subtitle); + String vpnSubtitleMessage = mDpm.getResources().getString( + QS_DIALOG_MONITORING_VPN_SUBTITLE, mMonitoringSubtitleVpnStringSupplier); + vpnSubtitle.setText(vpnSubtitleMessage); } // Note: if a new section is added, should update configSubtitleVisibility to include @@ -657,8 +726,7 @@ class QSSecurityFooter extends ViewController<View> @VisibleForTesting String getSettingsButton() { return mDpm.getResources().getString( - QS_DIALOG_VIEW_POLICIES, - () -> mContext.getString(R.string.monitoring_button_view_policies)); + QS_DIALOG_VIEW_POLICIES, mViewPoliciesButtonStringSupplier); } private String getPositiveButton() { @@ -692,9 +760,7 @@ class QSSecurityFooter extends ViewController<View> organizationName); } } - return mDpm.getResources().getString( - QS_DIALOG_MANAGEMENT, - () -> mContext.getString(R.string.monitoring_description_management)); + return mDpm.getResources().getString(QS_DIALOG_MANAGEMENT, mManagementDialogStringSupplier); } @Nullable @@ -703,15 +769,11 @@ class QSSecurityFooter extends ViewController<View> if (!(hasCACerts || hasCACertsInWorkProfile)) return null; if (isDeviceManaged) { return mDpm.getResources().getString( - QS_DIALOG_MANAGEMENT_CA_CERT, - () -> mContext.getString( - R.string.monitoring_description_management_ca_certificate)); + QS_DIALOG_MANAGEMENT_CA_CERT, mManagementDialogCaCertStringSupplier); } if (hasCACertsInWorkProfile) { return mDpm.getResources().getString( - QS_DIALOG_WORK_PROFILE_CA_CERT, - () -> mContext.getString( - R.string.monitoring_description_managed_profile_ca_certificate)); + QS_DIALOG_WORK_PROFILE_CA_CERT, mWorkProfileDialogCaCertStringSupplier); } return mContext.getString(R.string.monitoring_description_ca_certificate); } @@ -722,14 +784,10 @@ class QSSecurityFooter extends ViewController<View> if (!isNetworkLoggingEnabled) return null; if (isDeviceManaged) { return mDpm.getResources().getString( - QS_DIALOG_MANAGEMENT_NETWORK, - () -> mContext.getString( - R.string.monitoring_description_management_network_logging)); + QS_DIALOG_MANAGEMENT_NETWORK, mManagementDialogNetworkStringSupplier); } else { return mDpm.getResources().getString( - QS_DIALOG_WORK_PROFILE_NETWORK, - () -> mContext.getString( - R.string.monitoring_description_managed_profile_network_logging)); + QS_DIALOG_WORK_PROFILE_NETWORK, mWorkProfileDialogNetworkStringSupplier); } } @@ -799,7 +857,7 @@ class QSSecurityFooter extends ViewController<View> } else { return mDpm.getResources().getString( QS_DIALOG_MANAGEMENT_TITLE, - () -> mContext.getString(R.string.monitoring_title_device_owned)); + mManagementTitleSupplier); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index d7aa8b21a150..7c8e77b5d993 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -30,6 +30,7 @@ import androidx.annotation.Nullable; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -158,7 +159,9 @@ public class HotspotTile extends QSTileImpl<BooleanState> { state.expandedAccessibilityClassName = Switch.class.getName(); state.contentDescription = state.label; - final boolean isTileUnavailable = isDataSaverEnabled; + final boolean isWifiTetheringAllowed = + WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(mHost.getUserContext()); + final boolean isTileUnavailable = isDataSaverEnabled || !isWifiTetheringAllowed; final boolean isTileActive = (state.value || state.isTransient); if (isTileUnavailable) { @@ -167,15 +170,17 @@ public class HotspotTile extends QSTileImpl<BooleanState> { state.state = isTileActive ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; } - state.secondaryLabel = getSecondaryLabel( - isTileActive, isTransient, isDataSaverEnabled, numConnectedDevices); + state.secondaryLabel = getSecondaryLabel(isTileActive, isTransient, isDataSaverEnabled, + numConnectedDevices, isWifiTetheringAllowed); state.stateDescription = state.secondaryLabel; } @Nullable private String getSecondaryLabel(boolean isActive, boolean isTransient, - boolean isDataSaverEnabled, int numConnectedDevices) { - if (isTransient) { + boolean isDataSaverEnabled, int numConnectedDevices, boolean isWifiTetheringAllowed) { + if (!isWifiTetheringAllowed) { + return mContext.getString(R.string.wifitrackerlib_admin_restricted_network); + } else if (isTransient) { return mContext.getString(R.string.quick_settings_hotspot_secondary_label_transient); } else if (isDataSaverEnabled) { return mContext.getString( diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 6d9455e80adc..489f881c36cf 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -78,7 +78,6 @@ import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManagerGlobal; -import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import android.window.WindowContext; @@ -559,14 +558,9 @@ public class ScreenshotController { private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect, Insets screenInsets, ComponentName topComponent, boolean showFlash) { - if (mAccessibilityManager.isEnabled()) { - AccessibilityEvent event = - new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - event.setContentDescription( - mContext.getResources().getString(R.string.screenshot_saving_title)); - mAccessibilityManager.sendAccessibilityEvent(event); - } - + withWindowAttached(() -> + mScreenshotView.announceForAccessibility( + mContext.getResources().getString(R.string.screenshot_saving_title))); if (mScreenshotView.isAttachedToWindow()) { // if we didn't already dismiss for another reason @@ -633,6 +627,7 @@ public class ScreenshotController { } } } + @Override public void requestCompatCameraControl(boolean showControl, boolean transformationApplied, @@ -718,6 +713,7 @@ public class ScreenshotController { Log.e(TAG, "requestScrollCapture failed", e); } } + ListenableFuture<ScrollCaptureController.LongScreenshot> mLongScreenshotFuture; private void runBatchScrollCapture(ScrollCaptureResponse response) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 7f3758e208db..2621f6da7afa 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -16,6 +16,7 @@ package com.android.systemui.screenshot; +import static android.app.admin.DevicePolicyResources.Strings.SystemUi.SCREENSHOT_BLOCKED_BY_ADMIN; import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS; import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE; @@ -54,7 +55,9 @@ import androidx.annotation.NonNull; import com.android.internal.logging.UiEventLogger; import com.android.internal.util.ScreenshotHelper; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import java.util.concurrent.Executor; import java.util.function.Consumer; import javax.inject.Inject; @@ -70,6 +73,7 @@ public class TakeScreenshotService extends Service { private final ScreenshotNotificationsController mNotificationsController; private final Handler mHandler; private final Context mContext; + private final @Background Executor mBgExecutor; private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() { @Override @@ -97,7 +101,8 @@ public class TakeScreenshotService extends Service { @Inject public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager, DevicePolicyManager devicePolicyManager, UiEventLogger uiEventLogger, - ScreenshotNotificationsController notificationsController, Context context) { + ScreenshotNotificationsController notificationsController, Context context, + @Background Executor bgExecutor) { if (DEBUG_SERVICE) { Log.d(TAG, "new " + this); } @@ -108,6 +113,7 @@ public class TakeScreenshotService extends Service { mUiEventLogger = uiEventLogger; mNotificationsController = notificationsController; mContext = context; + mBgExecutor = bgExecutor; } @Override @@ -189,12 +195,18 @@ public class TakeScreenshotService extends Service { requestCallback.reportError(); return true; } - if(mDevicePolicyManager.getScreenCaptureDisabled(null, UserHandle.USER_ALL)) { - Log.w(TAG, "Skipping screenshot because an IT admin has disabled " - + "screenshots on the device"); - Toast.makeText(mContext, R.string.screenshot_blocked_by_admin, - Toast.LENGTH_SHORT).show(); - requestCallback.reportError(); + + if (mDevicePolicyManager.getScreenCaptureDisabled(null, UserHandle.USER_ALL)) { + mBgExecutor.execute(() -> { + Log.w(TAG, "Skipping screenshot because an IT admin has disabled " + + "screenshots on the device"); + String blockedByAdminText = mDevicePolicyManager.getResources().getString( + SCREENSHOT_BLOCKED_BY_ADMIN, + () -> mContext.getString(R.string.screenshot_blocked_by_admin)); + mHandler.post(() -> + Toast.makeText(mContext, blockedByAdminText, Toast.LENGTH_SHORT).show()); + requestCallback.reportError(); + }); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java index 1b5e52d7f8fb..df2fe4e8511f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java @@ -93,6 +93,7 @@ public class ShadeListBuilder implements Dumpable { private final NotificationInteractionTracker mInteractionTracker; private final DumpManager mDumpManager; // used exclusivly by ShadeListBuilder#notifySectionEntriesUpdated + // TODO replace temp with collection pool for readability private final ArrayList<ListEntry> mTempSectionMembers = new ArrayList<>(); private final boolean mAlwaysLogList; @@ -230,13 +231,7 @@ public class ShadeListBuilder implements Dumpable { mPipelineState.requireState(STATE_IDLE); mNotifSections.clear(); - NotifSectioner lastSection = null; for (NotifSectioner sectioner : sectioners) { - if (lastSection != null && lastSection.getBucket() > sectioner.getBucket()) { - throw new IllegalArgumentException("setSectioners with non contiguous sections " - + lastSection.getName() + " - " + lastSection.getBucket() + " & " - + sectioner.getName() + " - " + sectioner.getBucket()); - } final NotifSection section = new NotifSection(sectioner, mNotifSections.size()); final NotifComparator sectionComparator = section.getComparator(); mNotifSections.add(section); @@ -244,10 +239,23 @@ public class ShadeListBuilder implements Dumpable { if (sectionComparator != null) { sectionComparator.setInvalidationListener(this::onNotifComparatorInvalidated); } - lastSection = sectioner; } mNotifSections.add(new NotifSection(DEFAULT_SECTIONER, mNotifSections.size())); + + // validate sections + final ArraySet<Integer> seenBuckets = new ArraySet<>(); + int lastBucket = mNotifSections.size() > 0 + ? mNotifSections.get(0).getBucket() + : 0; + for (NotifSection section : mNotifSections) { + if (lastBucket != section.getBucket() && seenBuckets.contains(section.getBucket())) { + throw new IllegalStateException("setSectioners with non contiguous sections " + + section.getLabel() + " has an already seen bucket"); + } + lastBucket = section.getBucket(); + seenBuckets.add(lastBucket); + } } void setNotifStabilityManager(@NonNull NotifStabilityManager notifStabilityManager) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt index 6db544c77f87..8be710c8842c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt @@ -57,7 +57,6 @@ class NodeSpecBuilder( var currentSection: NotifSection? = null val prevSections = mutableSetOf<NotifSection?>() - var lastSection: NotifSection? = null val showHeaders = sectionHeaderVisibilityProvider.sectionHeadersVisible val sectionOrder = mutableListOf<NotifSection?>() val sectionHeaders = mutableMapOf<NotifSection?, NodeController?>() @@ -65,15 +64,6 @@ class NodeSpecBuilder( for (entry in notifList) { val section = entry.section!! - - lastSection?.let { - if (it.bucket > section.bucket) { - throw IllegalStateException("buildNodeSpec with non contiguous section " + - "buckets ${it.sectioner.name} - ${it.bucket} & " + - "${it.sectioner.name} - ${it.bucket}") - } - } - lastSection = section if (prevSections.contains(section)) { throw java.lang.RuntimeException("Section ${section.label} has been duplicated") } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index ed69e0609096..f975799b4b85 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -60,6 +60,7 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.MathUtils; import android.util.Property; +import android.util.Slog; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -363,9 +364,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Nullable private OnExpansionChangedListener mExpansionChangedListener; @Nullable private Runnable mOnIntrinsicHeightReachedRunnable; - private SystemNotificationAsyncTask mSystemNotificationAsyncTask = - new SystemNotificationAsyncTask(); - private float mTopRoundnessDuringLaunchAnimation; private float mBottomRoundnessDuringLaunchAnimation; @@ -517,45 +515,20 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } /** - * Caches whether or not this row contains a system notification. Note, this is only cached - * once per notification as the packageInfo can't technically change for a notification row. - */ - private void cacheIsSystemNotification() { - //TODO: This probably shouldn't be in ExpandableNotificationRow - if (mEntry != null && mEntry.mIsSystemNotification == null) { - if (mSystemNotificationAsyncTask.getStatus() == AsyncTask.Status.PENDING) { - // Run async task once, only if it hasn't already been executed. Note this is - // executed in serial - no need to parallelize this small task. - mSystemNotificationAsyncTask.execute(); - } - } - } - - /** * Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif * or is in an allowList). */ public boolean getIsNonblockable() { - // If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once - // again, but in-place on the main thread this time. This should rarely ever get called. - if (mEntry != null && mEntry.mIsSystemNotification == null) { - if (DEBUG) { - Log.d(TAG, "Retrieving isSystemNotification on main thread"); - } - mSystemNotificationAsyncTask.cancel(true /* mayInterruptIfRunning */); - mEntry.mIsSystemNotification = isSystemNotification(mContext, mEntry.getSbn()); + if (mEntry == null || mEntry.getChannel() == null) { + Log.w(TAG, "missing entry or channel"); + return true; } - - boolean isNonblockable = mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction(); - - if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) { - if (mEntry.mIsSystemNotification) { - if (mEntry.getChannel() != null && !mEntry.getChannel().isBlockable()) { - isNonblockable = true; - } - } + if (mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction() + && !mEntry.getChannel().isBlockable()) { + return true; } - return isNonblockable; + + return false; } private boolean isConversation() { @@ -1626,8 +1599,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mBubblesManagerOptional = bubblesManagerOptional; mNotificationGutsManager = gutsManager; mMetricsLogger = metricsLogger; - - cacheIsSystemNotification(); } private void initDimens() { @@ -3545,25 +3516,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView }); } - /** - * Background task for executing IPCs to check if the notification is a system notification. The - * output is used for both the blocking helper and the notification info. - */ - private class SystemNotificationAsyncTask extends AsyncTask<Void, Void, Boolean> { - - @Override - protected Boolean doInBackground(Void... voids) { - return isSystemNotification(mContext, mEntry.getSbn()); - } - - @Override - protected void onPostExecute(Boolean result) { - if (mEntry != null) { - mEntry.mIsSystemNotification = result; - } - } - } - private void setTargetPoint(Point p) { mTargetPoint = p; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 347e05cc7f75..fd307df8d304 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -1198,15 +1198,19 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL if (tileIcon != null) { mWalletButton.setImageDrawable(tileIcon); } - updateWalletVisibility(); - updateAffordanceColors(); + post(() -> { + updateWalletVisibility(); + updateAffordanceColors(); + }); } @Override public void onWalletCardRetrievalError(@NonNull GetWalletCardsError error) { mHasCard = false; - updateWalletVisibility(); - updateAffordanceColors(); + post(() -> { + updateWalletVisibility(); + updateAffordanceColors(); + }); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 0b721383e2d1..8792118fb9ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -188,6 +188,10 @@ public class KeyguardBouncer { } if (mContainer.getVisibility() == View.VISIBLE || mShowingSoon) { + // Calls to reset must resume the ViewControllers when in fullscreen mode + if (needsFullscreenBouncer()) { + mKeyguardViewController.onResume(); + } return; } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java index acff8712e92e..8c842f162e24 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java @@ -19,7 +19,6 @@ package com.android.systemui.wallet.controller; import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE; import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE; -import android.annotation.CallbackExecutor; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -65,7 +64,6 @@ public class QuickAccessWalletController { private static final long RECREATION_TIME_WINDOW = TimeUnit.MINUTES.toMillis(10L); private final Context mContext; private final Executor mExecutor; - private final Executor mCallbackExecutor; private final Executor mBgExecutor; private final SecureSettings mSecureSettings; private final SystemClock mClock; @@ -82,14 +80,12 @@ public class QuickAccessWalletController { public QuickAccessWalletController( Context context, @Main Executor executor, - @CallbackExecutor Executor callbackExecutor, @Background Executor bgExecutor, SecureSettings secureSettings, QuickAccessWalletClient quickAccessWalletClient, SystemClock clock) { mContext = context; mExecutor = executor; - mCallbackExecutor = callbackExecutor; mBgExecutor = bgExecutor; mSecureSettings = secureSettings; mQuickAccessWalletClient = quickAccessWalletClient; @@ -180,7 +176,7 @@ public class QuickAccessWalletController { int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size); GetWalletCardsRequest request = new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1); - mQuickAccessWalletClient.getWalletCards(mExecutor, request, cardsRetriever); + mQuickAccessWalletClient.getWalletCards(mBgExecutor, request, cardsRetriever); } /** @@ -212,7 +208,7 @@ public class QuickAccessWalletController { public void startQuickAccessUiIntent(ActivityStarter activityStarter, ActivityLaunchAnimator.Controller animationController, boolean hasCard) { - mQuickAccessWalletClient.getWalletPendingIntent(mCallbackExecutor, + mQuickAccessWalletClient.getWalletPendingIntent(mBgExecutor, walletPendingIntent -> { if (walletPendingIntent != null) { startQuickAccessViaPendingIntent(walletPendingIntent, activityStarter, diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 7c211b22e657..57253af57f0e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -159,6 +159,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { when(mContext.getDisplay()).thenReturn(mDisplay); // Not support hwc layer by default doReturn(null).when(mDisplay).getDisplayDecorationSupport(); + doReturn(mDisplayMode).when(mDisplay).getMode(); when(mMockTypedArray.length()).thenReturn(0); mPrivacyDotTopLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt index 2abc666e87fb..a4d223853b12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt @@ -14,6 +14,8 @@ import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardViewController import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags +import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController +import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.policy.KeyguardStateController @@ -52,6 +54,11 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController + @Mock + private lateinit var notificationShadeWindowController: NotificationShadeWindowController + + @Mock + private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub private lateinit var remoteAnimationTarget: RemoteAnimationTarget @@ -60,8 +67,11 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) keyguardUnlockAnimationController = KeyguardUnlockAnimationController( context, keyguardStateController, { keyguardViewMediator }, keyguardViewController, - featureFlags, { biometricUnlockController }, statusBarStateController + featureFlags, { biometricUnlockController }, statusBarStateController, + notificationShadeWindowController ) + keyguardUnlockAnimationController.setLauncherUnlockController( + launcherUnlockAnimationController) `when`(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java)) @@ -194,4 +204,18 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning) } + + @Test + fun doNotPlayCannedUnlockAnimation_ifLaunchingApp() { + `when`(notificationShadeWindowController.isLaunchingActivity).thenReturn(true) + + keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( + remoteAnimationTarget, + 0 /* startTime */, + true /* requestedShowSurfaceBehindKeyguard */ + ) + + assertFalse(keyguardUnlockAnimationController.canPerformInWindowLauncherAnimations()) + assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java new file mode 100644 index 000000000000..b86713d0890b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.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.systemui.qs.tiles; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.os.Handler; +import android.service.quicksettings.Tile; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.internal.logging.MetricsLogger; +import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSTileHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.statusbar.policy.DataSaverController; +import com.android.systemui.statusbar.policy.HotspotController; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class HotspotTileTest extends SysuiTestCase { + + @Rule + public MockitoRule mRule = MockitoJUnit.rule(); + @Mock + private QSTileHost mHost; + @Mock + private HotspotController mHotspotController; + @Mock + private DataSaverController mDataSaverController; + + private TestableLooper mTestableLooper; + private HotspotTile mTile; + private QSTile.BooleanState mState = new QSTile.BooleanState(); + + @Before + public void setUp() throws Exception { + mTestableLooper = TestableLooper.get(this); + when(mHost.getContext()).thenReturn(mContext); + when(mHost.getUserContext()).thenReturn(mContext); + when(mDataSaverController.isDataSaverEnabled()).thenReturn(false); + + mTile = new HotspotTile( + mHost, + mTestableLooper.getLooper(), + new Handler(mTestableLooper.getLooper()), + new FalsingManagerFake(), + mock(MetricsLogger.class), + mock(StatusBarStateController.class), + mock(ActivityStarter.class), + mock(QSLogger.class), + mHotspotController, + mDataSaverController + ); + + mTile.initialize(); + mTestableLooper.processAllMessages(); + } + + @Test + public void handleUpdateState_wifiTetheringIsAllowed_stateIsNotUnavailable() { + MockitoSession mockitoSession = ExtendedMockito.mockitoSession() + .spyStatic(WifiEnterpriseRestrictionUtils.class) + .startMocking(); + when(WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(mContext)).thenReturn(true); + + mTile.handleUpdateState(mState, null); + + assertThat(mState.state).isNotEqualTo(Tile.STATE_UNAVAILABLE); + assertThat(String.valueOf(mState.secondaryLabel)) + .isNotEqualTo(mContext.getString(R.string.wifitrackerlib_admin_restricted_network)); + mockitoSession.finishMocking(); + } + + @Test + public void handleUpdateState_wifiTetheringIsDisallowed_stateIsUnavailable() { + MockitoSession mockitoSession = ExtendedMockito.mockitoSession() + .spyStatic(WifiEnterpriseRestrictionUtils.class) + .startMocking(); + when(WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(mContext)).thenReturn(false); + + mTile.handleUpdateState(mState, null); + + assertThat(mState.state).isEqualTo(Tile.STATE_UNAVAILABLE); + assertThat(String.valueOf(mState.secondaryLabel)) + .isEqualTo(mContext.getString(R.string.wifitrackerlib_admin_restricted_network)); + mockitoSession.finishMocking(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 0fff5f5b47e5..16b0376ba1f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -294,7 +294,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { verify(mPresenter).updateNotificationViews(any()); verify(mEntryListener).onEntryRemoved( - eq(mEntry), any(), eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON)); + argThat(matchEntryOnKey()), any(), + eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON)); verify(mRow).setRemoved(); assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey())); @@ -319,8 +320,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mEntryManager.removeNotification("not_a_real_key", mRankingMap, UNDEFINED_DISMISS_REASON); - verify(mEntryListener, never()).onEntryRemoved( - eq(mEntry), any(), eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON)); + verify(mEntryListener, never()).onEntryRemoved(argThat(matchEntryOnKey()), any(), + eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON)); } /** Regression test for b/201097913. */ @@ -333,10 +334,10 @@ public class NotificationEntryManagerTest extends SysuiTestCase { // Verify that only the listener for the NEW pipeline is notified. // Old pipeline: verify(mEntryListener, never()).onEntryRemoved( - argThat(matchEntryOnSbn()), any(), anyBoolean(), anyInt()); + argThat(matchEntryOnKey()), any(), anyBoolean(), anyInt()); // New pipeline: verify(mNotifCollectionListener).onEntryRemoved( - argThat(matchEntryOnSbn()), anyInt()); + argThat(matchEntryOnKey()), anyInt()); } @Test @@ -457,7 +458,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { // THEN the notification is retained assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey())); verify(mEntryListener, never()).onEntryRemoved( - eq(mEntry), any(), eq(false), eq(UNDEFINED_DISMISS_REASON)); + argThat(matchEntryOnKey()), any(), eq(false), eq(UNDEFINED_DISMISS_REASON)); } @Test @@ -476,7 +477,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { // THEN the notification is removed assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey())); verify(mEntryListener).onEntryRemoved( - eq(mEntry), any(), eq(false), eq(UNDEFINED_DISMISS_REASON)); + argThat(matchEntryOnKey()), any(), eq(false), eq(UNDEFINED_DISMISS_REASON)); } @Test @@ -541,7 +542,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { // GIVEN interceptor that intercepts that entry when(mRemoveInterceptor.onNotificationRemoveRequested( - eq(mEntry.getKey()), eq(mEntry), anyInt())) + eq(mEntry.getKey()), argThat(matchEntryOnKey()), anyInt())) .thenReturn(true); // WHEN the notification is removed @@ -549,7 +550,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { // THEN the interceptor intercepts & the entry is not removed & no listeners are called assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey())); - verify(mEntryListener, never()).onEntryRemoved(eq(mEntry), + verify(mEntryListener, never()).onEntryRemoved(argThat(matchEntryOnKey()), any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON)); } @@ -560,7 +561,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { // GIVEN interceptor that doesn't intercept when(mRemoveInterceptor.onNotificationRemoveRequested( - eq(mEntry.getKey()), eq(mEntry), anyInt())) + eq(mEntry.getKey()), argThat(matchEntryOnKey()), anyInt())) .thenReturn(false); // WHEN the notification is removed @@ -568,7 +569,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { // THEN the interceptor intercepts & the entry is not removed & no listeners are called assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey())); - verify(mEntryListener, atLeastOnce()).onEntryRemoved(eq(mEntry), + verify(mEntryListener, atLeastOnce()).onEntryRemoved(argThat(matchEntryOnKey()), any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON)); } @@ -663,9 +664,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { PendingIntent.FLAG_IMMUTABLE)).build(); } - // TODO(b/201321631): Update more tests to use this function instead of eq(mEntry). - private ArgumentMatcher<NotificationEntry> matchEntryOnSbn() { - return e -> e.getSbn().equals(mSbn); + private ArgumentMatcher<NotificationEntry> matchEntryOnKey() { + return e -> e.getKey().equals(mEntry.getKey()); } private static class FakeNotificationLifetimeExtender implements NotificationLifetimeExtender { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java index 9ea1813377a0..4e7e79f2cb26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java @@ -1528,6 +1528,34 @@ public class ShadeListBuilderTest extends SysuiTestCase { } @Test + public void testContiguousSections() { + mListBuilder.setSectioners(List.of( + new PackageSectioner("pkg", 1), + new PackageSectioner("pkg", 1), + new PackageSectioner("pkg", 3), + new PackageSectioner("pkg", 2) + )); + } + + @Test(expected = IllegalStateException.class) + public void testNonContiguousSections() { + mListBuilder.setSectioners(List.of( + new PackageSectioner("pkg", 1), + new PackageSectioner("pkg", 1), + new PackageSectioner("pkg", 3), + new PackageSectioner("pkg", 1) + )); + } + + @Test(expected = IllegalStateException.class) + public void testBucketZeroNotAllowed() { + mListBuilder.setSectioners(List.of( + new PackageSectioner("pkg", 0), + new PackageSectioner("pkg", 1) + )); + } + + @Test public void testStabilizeGroupsDelayedSummaryRendersAllNotifsTopLevel() { // GIVEN group children posted without a summary addGroupChild(0, PACKAGE_1, GROUP_1); @@ -2189,7 +2217,11 @@ public class ShadeListBuilderTest extends SysuiTestCase { } PackageSectioner(String pkg) { - super("PackageSection_" + pkg, 0); + this(pkg, 0); + } + + PackageSectioner(String pkg, int bucket) { + super("PackageSection_" + pkg, bucket); mPackages = List.of(pkg); mComparator = null; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index b3c34c1e4ff4..1f9af81d6508 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -322,11 +322,22 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { ExpandableNotificationRow row = mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification()); row.getEntry().getChannel().setImportanceLockedByCriticalDeviceFunction(true); + row.getEntry().getChannel().setBlockable(false); assertTrue(row.getIsNonblockable()); } @Test + public void testGetIsNonblockable_criticalDeviceFunction_butBlockable() throws Exception { + ExpandableNotificationRow row = + mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification()); + row.getEntry().getChannel().setImportanceLockedByCriticalDeviceFunction(true); + row.getEntry().getChannel().setBlockable(true); + + assertFalse(row.getIsNonblockable()); + } + + @Test public void testCanDismissNoClear() throws Exception { ExpandableNotificationRow row = mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index f43c2a183465..9c02216722e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -475,4 +475,19 @@ public class KeyguardBouncerTest extends SysuiTestCase { mBouncer.setExpansion(bouncerHideAmount); verify(callback, never()).onExpansionChanged(bouncerHideAmount); } + + @Test + public void testOnResumeCalledForFullscreenBouncerOnSecondShow() { + // GIVEN a security mode which requires fullscreen bouncer + when(mKeyguardSecurityModel.getSecurityMode(anyInt())) + .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin); + mBouncer.show(true); + + // WHEN a second call to show occurs, the bouncer will already by visible + reset(mKeyguardHostViewController); + mBouncer.show(true); + + // THEN ensure the ViewController is told to resume + verify(mKeyguardHostViewController).onResume(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java index de2efc71b3a9..8e4f184f560e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java @@ -100,7 +100,6 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase { mContext, MoreExecutors.directExecutor(), MoreExecutors.directExecutor(), - MoreExecutors.directExecutor(), mSecureSettings, mQuickAccessWalletClient, mClock); diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java index bc5cb0250d56..99526b7ef0d1 100644 --- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java +++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java @@ -26,7 +26,7 @@ import com.android.internal.backup.ITransportStatusCallback; public class TransportStatusCallback extends ITransportStatusCallback.Stub { private static final String TAG = "TransportStatusCallback"; - private static final int TIMEOUT_MILLIS = 600 * 1000; // 10 minutes. + private static final int TIMEOUT_MILLIS = 300 * 1000; // 5 minutes. private static final int OPERATION_STATUS_DEFAULT = 0; private final int mOperationTimeout; diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 9b2554fdbb3c..8622ef0940d5 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -649,6 +649,12 @@ public class CompanionDeviceManagerService extends SystemService { private void registerDevicePresenceListenerActive(String packageName, String deviceAddress, boolean active) throws RemoteException { + if (DEBUG) { + Log.i(TAG, "registerDevicePresenceListenerActive()" + + " active=" + active + + " deviceAddress=" + deviceAddress); + } + getContext().enforceCallingOrSelfPermission( android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE, "[un]registerDevicePresenceListenerService"); @@ -664,6 +670,12 @@ public class CompanionDeviceManagerService extends SystemService { + " for user " + userId)); } + // If already at specified state, then no-op. + if (active == association.isNotifyOnDeviceNearby()) { + if (DEBUG) Log.d(TAG, "Device presence listener is already at desired state."); + return; + } + // AssociationInfo class is immutable: create a new AssociationInfo object with updated // flag. association = AssociationInfo.builder(association) @@ -674,7 +686,17 @@ public class CompanionDeviceManagerService extends SystemService { // an application sets/unsets the mNotifyOnDeviceNearby flag. mAssociationStore.updateAssociation(association); - // TODO(b/218615198): correctly handle the case when the device is currently present. + // If device is already present, then trigger callback. + if (active && mDevicePresenceMonitor.isDevicePresent(association.getId())) { + if (DEBUG) Log.d(TAG, "Device is already present. Triggering callback."); + onDeviceAppearedInternal(association.getId()); + } + + // If last listener is unregistered, then unbind application. + if (!active && !shouldBindPackage(userId, packageName)) { + if (DEBUG) Log.d(TAG, "Last listener unregistered. Unbinding application."); + mCompanionAppController.unbindCompanionApplication(userId, packageName); + } } @Override diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index dc8c852902e6..ed6110086089 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -334,10 +334,14 @@ public abstract class PackageManagerInternal { /** * Retrieve all receivers that can handle a broadcast of the given intent. + * @param filterCallingUid The results will be filtered in the context of this UID instead + * of the calling UID. + * @param forSend true if the invocation is intended for sending broadcasts. The value + * of this parameter affects how packages are filtered. */ public abstract List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, - int filterCallingUid, int userId); + int filterCallingUid, int userId, boolean forSend); /** * Retrieve all services that can be performed for the given intent. diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 1033aea4b09f..7a52af61a196 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -3066,42 +3066,88 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { Binder.restoreCallingIdentity(ident); } + // Send the broadcast exactly once to all possible disjoint sets of apps. + // If the location master switch is on, broadcast the ServiceState 4 times: + // - Full ServiceState sent to apps with ACCESS_FINE_LOCATION and READ_PHONE_STATE + // - Full ServiceState sent to apps with ACCESS_FINE_LOCATION and + // READ_PRIVILEGED_PHONE_STATE but not READ_PHONE_STATE + // - Sanitized ServiceState sent to apps with READ_PHONE_STATE but not ACCESS_FINE_LOCATION + // - Sanitized ServiceState sent to apps with READ_PRIVILEGED_PHONE_STATE but neither + // READ_PHONE_STATE nor ACCESS_FINE_LOCATION + // If the location master switch is off, broadcast the ServiceState multiple times: + // - Full ServiceState sent to all apps permitted to bypass the location master switch if + // they have either READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE + // - Sanitized ServiceState sent to all other apps with READ_PHONE_STATE + // - Sanitized ServiceState sent to all other apps with READ_PRIVILEGED_PHONE_STATE but not + // READ_PHONE_STATE + if (Binder.withCleanCallingIdentity(() -> + LocationAccessPolicy.isLocationModeEnabled(mContext, mContext.getUserId()))) { + Intent fullIntent = createServiceStateIntent(state, subId, phoneId, false); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + fullIntent, + new String[]{Manifest.permission.READ_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION}); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + fullIntent, + new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION}, + new String[]{Manifest.permission.READ_PHONE_STATE}); + + Intent sanitizedIntent = createServiceStateIntent(state, subId, phoneId, true); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + sanitizedIntent, + new String[]{Manifest.permission.READ_PHONE_STATE}, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + sanitizedIntent, + new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE}, + new String[]{Manifest.permission.READ_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION}); + } else { + String[] locationBypassPackages = Binder.withCleanCallingIdentity(() -> + LocationAccessPolicy.getLocationBypassPackages(mContext)); + for (String locationBypassPackage : locationBypassPackages) { + Intent fullIntent = createServiceStateIntent(state, subId, phoneId, false); + fullIntent.setPackage(locationBypassPackage); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + fullIntent, + new String[]{Manifest.permission.READ_PHONE_STATE}); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + fullIntent, + new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE}, + new String[]{Manifest.permission.READ_PHONE_STATE}); + } + + Intent sanitizedIntent = createServiceStateIntent(state, subId, phoneId, true); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + sanitizedIntent, + new String[]{Manifest.permission.READ_PHONE_STATE}, + new String[]{/* no excluded permissions */}, + locationBypassPackages); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + sanitizedIntent, + new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE}, + new String[]{Manifest.permission.READ_PHONE_STATE}, + locationBypassPackages); + } + } + + private Intent createServiceStateIntent(ServiceState state, int subId, int phoneId, + boolean sanitizeLocation) { Intent intent = new Intent(Intent.ACTION_SERVICE_STATE); intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); Bundle data = new Bundle(); - state.fillInNotifierBundle(data); + if (sanitizeLocation) { + state.createLocationInfoSanitizedCopy(true).fillInNotifierBundle(data); + } else { + state.fillInNotifierBundle(data); + } intent.putExtras(data); - // Pass the subscription along with the intent. intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId); intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId); intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId); - - // Send the broadcast twice -- once for all apps with READ_PHONE_STATE, then again - // for all apps with READ_PRIVILEGED_PHONE_STATE but not READ_PHONE_STATE. - // Do this again twice, the first time for apps with ACCESS_FINE_LOCATION, then again with - // the location-sanitized service state for all apps without ACCESS_FINE_LOCATION. - // This ensures that any app holding either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE - // get this broadcast exactly once, and we are not exposing location without permission. - mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent, - new String[] {Manifest.permission.READ_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION}); - mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent, - new String[] {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION}, - new String[] {Manifest.permission.READ_PHONE_STATE}); - - // Replace bundle with location-sanitized ServiceState - data = new Bundle(); - state.createLocationInfoSanitizedCopy(true).fillInNotifierBundle(data); - intent.putExtras(data); - mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent, - new String[] {Manifest.permission.READ_PHONE_STATE}, - new String[] {Manifest.permission.ACCESS_FINE_LOCATION}); - mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent, - new String[] {Manifest.permission.READ_PRIVILEGED_PHONE_STATE}, - new String[] {Manifest.permission.READ_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION}); + return intent; } private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId, diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 6fa3bc8e0669..216c9dc4375e 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -6475,7 +6475,7 @@ public final class ActiveServices { } } - if (ret == REASON_DENIED) { + if (ret == REASON_DENIED && verifyPackage(callingPackage, callingUid)) { final boolean isAllowedPackage = mAllowListWhileInUsePermissionInFgs.contains(callingPackage); if (isAllowedPackage) { @@ -6593,12 +6593,11 @@ public final class ActiveServices { } final int uidState = mAm.getUidStateLocked(callingUid); - int callerTargetSdkVersion = INVALID_UID; + int callerTargetSdkVersion = -1; try { - ApplicationInfo ai = mAm.mContext.getPackageManager().getApplicationInfoAsUser( - callingPackage, PackageManager.MATCH_KNOWN_PACKAGES, userId); - callerTargetSdkVersion = ai.targetSdkVersion; - } catch (PackageManager.NameNotFoundException e) { + callerTargetSdkVersion = mAm.mContext.getPackageManager() + .getTargetSdkVersion(callingPackage); + } catch (PackageManager.NameNotFoundException ignored) { } final String debugInfo = "[callingPackage: " + callingPackage @@ -6883,4 +6882,19 @@ public final class ActiveServices { /* allowBackgroundActivityStarts */ false) != REASON_DENIED; } + + /** + * Checks if a given packageName belongs to a given uid. + * @param packageName the package of the caller + * @param uid the uid of the caller + * @return true or false + */ + private boolean verifyPackage(String packageName, int uid) { + if (uid == ROOT_UID || uid == SYSTEM_UID) { + //System and Root are always allowed + return true; + } + return mAm.getPackageManagerInternal().isSameApp(packageName, uid, + UserHandle.getUserId(uid)); + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7e624e05405c..f6e8bc826153 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2627,7 +2627,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void batterySendBroadcast(Intent intent) { synchronized (this) { broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, null, - OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), + null, OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } } @@ -4241,7 +4241,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_UID, uid); intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid)); broadcastIntentLocked(null, null, null, intent, - null, null, 0, null, null, null, null, OP_NONE, + null, null, 0, null, null, null, null, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.getUserId(uid)); } @@ -8122,7 +8122,7 @@ public class ActivityManagerService extends IActivityManager.Stub | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId); broadcastIntentLocked(null, null, null, intent, - null, null, 0, null, null, null, null, OP_NONE, + null, null, 0, null, null, null, null, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, currentUserId); intent = new Intent(Intent.ACTION_USER_STARTING); @@ -8134,8 +8134,8 @@ public class ActivityManagerService extends IActivityManager.Stub public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {} - }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, null, OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, null, null, + OP_NONE, null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, UserHandle.USER_ALL); } catch (Throwable e) { Slog.wtf(TAG, "Failed sending first user broadcasts", e); @@ -13165,8 +13165,8 @@ public class ActivityManagerService extends IActivityManager.Stub Intent intent = allSticky.get(i); BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, null, - null, null, -1, -1, false, null, null, null, OP_NONE, null, receivers, - null, 0, null, null, false, true, true, -1, false, null, + null, null, -1, -1, false, null, null, null, null, OP_NONE, null, + receivers, null, 0, null, null, false, true, true, -1, false, null, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */); queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); @@ -13248,8 +13248,8 @@ public class ActivityManagerService extends IActivityManager.Stub UserManager.DISALLOW_DEBUGGING_FEATURES, user)) { continue; } - List<ResolveInfo> newReceivers = mPackageManagerInt - .queryIntentReceivers(intent, resolvedType, pmFlags, callingUid, user); + List<ResolveInfo> newReceivers = mPackageManagerInt.queryIntentReceivers( + intent, resolvedType, pmFlags, callingUid, user, true /* forSend */); if (user != UserHandle.USER_SYSTEM && newReceivers != null) { // If this is not the system user, we need to check for // any receivers that should be filtered out. @@ -13265,8 +13265,9 @@ public class ActivityManagerService extends IActivityManager.Stub if (newReceivers != null) { for (int i = newReceivers.size() - 1; i >= 0; i--) { final ResolveInfo ri = newReceivers.get(i); - final Resolution<ResolveInfo> resolution = mComponentAliasResolver - .resolveReceiver(intent, ri, resolvedType, pmFlags, user, callingUid); + final Resolution<ResolveInfo> resolution = + mComponentAliasResolver.resolveReceiver(intent, ri, resolvedType, + pmFlags, user, callingUid, true /* forSend */); if (resolution == null) { // It was an alias, but the target was not found. newReceivers.remove(i); @@ -13421,12 +13422,14 @@ public class ActivityManagerService extends IActivityManager.Stub String callerPackage, String callerFeatureId, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, String[] excludedPermissions, - int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, + String[] excludedPackages, int appOp, Bundle bOptions, boolean ordered, + boolean sticky, int callingPid, int callingUid, int realCallingUid, int realCallingPid, int userId) { return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, - excludedPermissions, appOp, bOptions, ordered, sticky, callingPid, callingUid, - realCallingUid, realCallingPid, userId, false /* allowBackgroundActivityStarts */, + excludedPermissions, excludedPackages, appOp, bOptions, ordered, sticky, callingPid, + callingUid, realCallingUid, realCallingPid, userId, + false /* allowBackgroundActivityStarts */, null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastAllowList */); } @@ -13435,7 +13438,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Nullable String callerFeatureId, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, - String[] excludedPermissions, int appOp, Bundle bOptions, + String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, int realCallingPid, int userId, boolean allowBackgroundActivityStarts, @@ -14042,10 +14045,10 @@ public class ActivityManagerService extends IActivityManager.Stub final BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, - requiredPermissions, excludedPermissions, appOp, brOptions, registeredReceivers, - resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, - allowBackgroundActivityStarts, backgroundActivityStartsToken, - timeoutExempt); + requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions, + registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered, + sticky, false, userId, allowBackgroundActivityStarts, + backgroundActivityStartsToken, timeoutExempt); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r); final boolean replaced = replacePending && (queue.replaceParallelBroadcastLocked(r) != null); @@ -14140,7 +14143,7 @@ public class ActivityManagerService extends IActivityManager.Stub BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, - requiredPermissions, excludedPermissions, appOp, brOptions, + requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions, receivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, allowBackgroundActivityStarts, backgroundActivityStartsToken, timeoutExempt); @@ -14269,14 +14272,16 @@ public class ActivityManagerService extends IActivityManager.Stub String[] requiredPermissions, int appOp, Bundle bOptions, boolean serialized, boolean sticky, int userId) { return broadcastIntentWithFeature(caller, null, intent, resolvedType, resultTo, resultCode, - resultData, resultExtras, requiredPermissions, null, appOp, bOptions, serialized, - sticky, userId); + resultData, resultExtras, requiredPermissions, null, null, appOp, bOptions, + serialized, sticky, userId); } + @Override public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, - String[] requiredPermissions, String[] excludedPermissions, int appOp, Bundle bOptions, + String[] requiredPermissions, String[] excludedPermissions, + String[] excludedPackages, int appOp, Bundle bOptions, boolean serialized, boolean sticky, int userId) { enforceNotIsolatedCaller("broadcastIntent"); synchronized(this) { @@ -14291,8 +14296,8 @@ public class ActivityManagerService extends IActivityManager.Stub return broadcastIntentLocked(callerApp, callerApp != null ? callerApp.info.packageName : null, callingFeatureId, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, - requiredPermissions, excludedPermissions, appOp, bOptions, serialized, - sticky, callingPid, callingUid, callingUid, callingPid, userId); + requiredPermissions, excludedPermissions, excludedPackages, appOp, bOptions, + serialized, sticky, callingPid, callingUid, callingUid, callingPid, userId); } finally { Binder.restoreCallingIdentity(origId); } @@ -14315,7 +14320,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, null, - OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid, + null, OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid, realCallingPid, userId, allowBackgroundActivityStarts, backgroundActivityStartsToken, broadcastAllowList); } finally { @@ -16834,10 +16839,11 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManagerService.this.broadcastIntentLocked(null /*callerApp*/, null /*callerPackage*/, null /*callingFeatureId*/, intent, null /*resolvedType*/, resultTo, 0 /*resultCode*/, null /*resultData*/, - null /*resultExtras*/, requiredPermissions, null, AppOpsManager.OP_NONE, - bOptions /*options*/, serialized, false /*sticky*/, callingPid, - callingUid, callingUid, callingPid, userId, - false /*allowBackgroundStarts*/, + null /*resultExtras*/, requiredPermissions, + null /*excludedPermissions*/, null /*excludedPackages*/, + AppOpsManager.OP_NONE, bOptions /*options*/, serialized, + false /*sticky*/, callingPid, callingUid, callingUid, callingPid, + userId, false /*allowBackgroundStarts*/, null /*tokenNeededForBackgroundActivityStarts*/, appIdAllowList); } finally { Binder.restoreCallingIdentity(origId); @@ -16973,7 +16979,7 @@ public class ActivityManagerService extends IActivityManager.Stub | Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, - null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, + null, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) { intent = new Intent(Intent.ACTION_LOCALE_CHANGED); @@ -16988,8 +16994,8 @@ public class ActivityManagerService extends IActivityManager.Stub TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, PowerExemptionManager.REASON_LOCALE_CHANGED, ""); broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, - null, OP_NONE, bOptions.toBundle(), false, false, MY_PID, SYSTEM_UID, - Binder.getCallingUid(), Binder.getCallingPid(), + null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID, + SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } @@ -17004,8 +17010,9 @@ public class ActivityManagerService extends IActivityManager.Stub String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES }; broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, - permissions, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); + permissions, null, null, OP_NONE, null, false, false, MY_PID, + SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), + UserHandle.USER_ALL); } } } @@ -17029,8 +17036,8 @@ public class ActivityManagerService extends IActivityManager.Stub } broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, - null, OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), - Binder.getCallingPid(), UserHandle.USER_ALL); + null, null, OP_NONE, null, false, false, -1, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 402491d8fe80..397a4420700e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -804,8 +804,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.flush(); Bundle bundle = mBroadcastOptions == null ? null : mBroadcastOptions.toBundle(); mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null, - requiredPermissions, null, android.app.AppOpsManager.OP_NONE, bundle, true, false, - mUserId); + requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, bundle, true, + false, mUserId); if (!mAsync) { receiver.waitForFinish(); } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index dd7fb84b46bf..d2e40c56c772 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -860,6 +860,21 @@ public final class BroadcastQueue { } } + // Check that the receiver does *not* belong to any of the excluded packages + if (!skip && r.excludedPackages != null && r.excludedPackages.length > 0) { + if (ArrayUtils.contains(r.excludedPackages, filter.packageName)) { + Slog.w(TAG, "Skipping delivery of excluded package " + + r.intent.toString() + + " to " + filter.receiverList.app + + " (pid=" + filter.receiverList.pid + + ", uid=" + filter.receiverList.uid + ")" + + " excludes package " + filter.packageName + + " due to sender " + r.callerPackage + + " (uid " + r.callingUid + ")"); + skip = true; + } + } + // If the broadcast also requires an app op check that as well. if (!skip && r.appOp != AppOpsManager.OP_NONE && mService.getAppOpsManager().noteOpNoThrow(r.appOp, @@ -1721,6 +1736,19 @@ public final class BroadcastQueue { } } + // Check that the receiver does *not* belong to any of the excluded packages + if (!skip && r.excludedPackages != null && r.excludedPackages.length > 0) { + if (ArrayUtils.contains(r.excludedPackages, component.getPackageName())) { + Slog.w(TAG, "Skipping delivery of excluded package " + + r.intent + " to " + + component.flattenToShortString() + + " excludes package " + component.getPackageName() + + " due to sender " + r.callerPackage + + " (uid " + r.callingUid + ")"); + skip = true; + } + } + if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID && r.requiredPermissions != null && r.requiredPermissions.length > 0) { for (int i = 0; i < r.requiredPermissions.length; i++) { diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 5343af25fd39..19ffc1733f3d 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -75,6 +75,7 @@ final class BroadcastRecord extends Binder { final String resolvedType; // the resolved data type final String[] requiredPermissions; // permissions the caller has required final String[] excludedPermissions; // permissions to exclude + final String[] excludedPackages; // packages to exclude final int appOp; // an app op that is associated with this broadcast final BroadcastOptions options; // BroadcastOptions supplied by caller final List receivers; // contains BroadcastFilter and ResolveInfo @@ -162,6 +163,10 @@ final class BroadcastRecord extends Binder { pw.print(prefix); pw.print("excludedPermissions="); pw.print(Arrays.toString(excludedPermissions)); } + if (excludedPackages != null && excludedPackages.length > 0) { + pw.print(prefix); pw.print("excludedPackages="); + pw.print(Arrays.toString(excludedPackages)); + } if (options != null) { pw.print(prefix); pw.print("options="); pw.println(options.toBundle()); } @@ -260,7 +265,8 @@ final class BroadcastRecord extends Binder { Intent _intent, ProcessRecord _callerApp, String _callerPackage, @Nullable String _callerFeatureId, int _callingPid, int _callingUid, boolean _callerInstantApp, String _resolvedType, - String[] _requiredPermissions, String[] _excludedPermissions, int _appOp, + String[] _requiredPermissions, String[] _excludedPermissions, + String[] _excludedPackages, int _appOp, BroadcastOptions _options, List _receivers, IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras, boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId, boolean allowBackgroundActivityStarts, @@ -280,6 +286,7 @@ final class BroadcastRecord extends Binder { resolvedType = _resolvedType; requiredPermissions = _requiredPermissions; excludedPermissions = _excludedPermissions; + excludedPackages = _excludedPackages; appOp = _appOp; options = _options; receivers = _receivers; @@ -321,6 +328,7 @@ final class BroadcastRecord extends Binder { resolvedType = from.resolvedType; requiredPermissions = from.requiredPermissions; excludedPermissions = from.excludedPermissions; + excludedPackages = from.excludedPackages; appOp = from.appOp; options = from.options; receivers = from.receivers; @@ -381,9 +389,10 @@ final class BroadcastRecord extends Binder { // build a new BroadcastRecord around that single-target list BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, - requiredPermissions, excludedPermissions, appOp, options, splitReceivers, resultTo, - resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId, - allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt); + requiredPermissions, excludedPermissions, excludedPackages, appOp, options, + splitReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, + initialSticky, userId, allowBackgroundActivityStarts, + mBackgroundActivityStartsToken, timeoutExempt); split.enqueueTime = this.enqueueTime; split.enqueueRealTime = this.enqueueRealTime; split.enqueueClockTime = this.enqueueClockTime; @@ -459,7 +468,7 @@ final class BroadcastRecord extends Binder { for (int i = 0; i < uidSize; i++) { final BroadcastRecord br = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, - requiredPermissions, excludedPermissions, appOp, options, + requiredPermissions, excludedPermissions, excludedPackages, appOp, options, uid2receiverList.valueAt(i), null /* _resultTo */, resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId, allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt); diff --git a/services/core/java/com/android/server/am/ComponentAliasResolver.java b/services/core/java/com/android/server/am/ComponentAliasResolver.java index 2db3b15e719d..01735a754c83 100644 --- a/services/core/java/com/android/server/am/ComponentAliasResolver.java +++ b/services/core/java/com/android/server/am/ComponentAliasResolver.java @@ -483,7 +483,7 @@ public class ComponentAliasResolver { @Nullable public Resolution<ResolveInfo> resolveReceiver(@NonNull Intent intent, @NonNull ResolveInfo receiver, @Nullable String resolvedType, - int packageFlags, int userId, int callingUid) { + int packageFlags, int userId, int callingUid, boolean forSend) { // Resolve this alias. final Resolution<ComponentName> resolution = resolveComponentAlias(() -> receiver.activityInfo.getComponentName()); @@ -506,8 +506,8 @@ public class ComponentAliasResolver { i.setPackage(null); i.setComponent(resolution.getTarget()); - List<ResolveInfo> resolved = pmi.queryIntentReceivers(i, - resolvedType, packageFlags, callingUid, userId); + List<ResolveInfo> resolved = pmi.queryIntentReceivers( + i, resolvedType, packageFlags, callingUid, userId, forSend); if (resolved == null || resolved.size() == 0) { // Target component not found. Slog.w(TAG, "Alias target " + target.flattenToShortString() + " not found"); diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java index 756209824614..35f91ba1169b 100644 --- a/services/core/java/com/android/server/am/PreBootBroadcaster.java +++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java @@ -124,7 +124,7 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { REASON_PRE_BOOT_COMPLETED, ""); synchronized (mService) { mService.broadcastIntentLocked(null, null, null, mIntent, null, this, 0, null, null, - null, null, AppOpsManager.OP_NONE, bOptions.toBundle(), true, + null, null, null, AppOpsManager.OP_NONE, bOptions.toBundle(), true, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId); } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index c04377389e8e..7ffea26638f5 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -3203,8 +3203,8 @@ class UserController implements Handler.Callback { synchronized (mService) { return mService.broadcastIntentLocked(null, null, null, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, null, - appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid, - realCallingPid, userId); + null, appOp, bOptions, ordered, sticky, callingPid, callingUid, + realCallingUid, realCallingPid, userId); } } diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index d239c02d4529..27ce493f717f 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -501,6 +501,7 @@ public final class AppHibernationService extends SystemService { null /* resultExtras */, requiredPermissions, null /* excludedPermissions */, + null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* serialized */, @@ -519,6 +520,7 @@ public final class AppHibernationService extends SystemService { null /* resultExtras */, requiredPermissions, null /* excludedPermissions */, + null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* serialized */, diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 8c0e2ddd8f3c..0123c646c764 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -6637,6 +6637,11 @@ public class AudioService extends IAudioService.Stub // verify arguments Objects.requireNonNull(device); AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior); + sVolumeLogger.log(new AudioEventLogger.StringEvent("setDeviceVolumeBehavior: dev:" + + AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:" + + device.getAddress() + " behavior:" + + AudioDeviceVolumeManager.volumeBehaviorName(deviceVolumeBehavior) + + " pack:" + pkgName).printLog(TAG)); if (pkgName == null) { pkgName = ""; } @@ -8352,7 +8357,7 @@ public class AudioService extends IAudioService.Stub private void avrcpSupportsAbsoluteVolume(String address, boolean support) { // address is not used for now, but may be used when multiple a2dp devices are supported sVolumeLogger.log(new AudioEventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr=" - + address + " support=" + support)); + + address + " support=" + support).printLog(TAG)); mDeviceBroker.setAvrcpAbsoluteVolumeSupported(support); setAvrcpAbsoluteVolumeSupported(support); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java index de0a36a32b66..bf7a62aadc28 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java @@ -88,6 +88,10 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem mCallback.onClientFinished(this, true /* success */); } + public boolean interruptsPrecedingClients() { + return true; + } + /** * Reset the local lockout state and notify any listeners. * diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java index 6e74d3622c1a..f29b9e823109 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java @@ -65,6 +65,10 @@ public class FaceResetLockoutClient extends HalClientMonitor<IBiometricsFace> { startHalOperation(); } + public boolean interruptsPrecedingClients() { + return true; + } + @Override protected void startHalOperation() { try { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java index f90cba79dac2..c8148df9ea71 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java @@ -82,6 +82,10 @@ class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implem } } + public boolean interruptsPrecedingClients() { + return true; + } + void onLockoutCleared() { resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache, mLockoutResetDispatcher); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java index 559ca0633c42..843fcc8ee2a6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java @@ -50,6 +50,10 @@ public class FingerprintResetLockoutClient extends BaseClientMonitor { callback.onClientFinished(this, true /* success */); } + public boolean interruptsPrecedingClients() { + return true; + } + @Override public int getProtoEnum() { return BiometricsProto.CM_RESET_LOCKOUT; diff --git a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java index 0944c57d121f..c37d4c6bcaef 100644 --- a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java +++ b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java @@ -18,6 +18,7 @@ package com.android.server.clipboard; import android.annotation.Nullable; import android.content.ClipData; +import android.os.PersistableBundle; import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; @@ -137,6 +138,9 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> { final ClipData clip = new ClipData("host clipboard", new String[]{"text/plain"}, new ClipData.Item(str)); + final PersistableBundle bundle = new PersistableBundle(); + bundle.putBoolean("com.android.systemui.SUPPRESS_CLIPBOARD_OVERLAY", true); + clip.getDescription().setExtras(bundle); if (LOG_CLIBOARD_ACCESS) { Slog.i(TAG, "Setting the guest clipboard to '" + str + "'"); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 68a53f13da8a..312da9a7243e 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -209,7 +209,6 @@ public class Vpn { private final NetworkInfo mNetworkInfo; private int mLegacyState; @VisibleForTesting protected String mPackage; - private String mSessionKey; private int mOwnerUID; private boolean mIsPackageTargetingAtLeastQ; @VisibleForTesting @@ -1534,11 +1533,17 @@ public class Vpn { } // Note: Return type guarantees results are deduped and sorted, which callers require. + // This method also adds the SDK sandbox UIDs corresponding to the applications by default, + // since apps are generally not aware of them, yet they should follow the VPN configuration + // of the app they belong to. private SortedSet<Integer> getAppsUids(List<String> packageNames, int userId) { SortedSet<Integer> uids = new TreeSet<>(); for (String app : packageNames) { int uid = getAppUid(app, userId); if (uid != -1) uids.add(uid); + if (Process.isApplicationUid(uid)) { + uids.add(Process.toSdkSandboxUid(uid)); + } } return uids; } @@ -1991,9 +1996,7 @@ public class Vpn { public synchronized int getActiveVpnType() { if (!mNetworkInfo.isConnectedOrConnecting()) return VpnManager.TYPE_VPN_NONE; if (mVpnRunner == null) return VpnManager.TYPE_VPN_SERVICE; - return mVpnRunner instanceof IkeV2VpnRunner - ? VpnManager.TYPE_VPN_PLATFORM - : VpnManager.TYPE_VPN_LEGACY; + return isIkev2VpnRunner() ? VpnManager.TYPE_VPN_PLATFORM : VpnManager.TYPE_VPN_LEGACY; } private void updateAlwaysOnNotification(DetailedState networkState) { @@ -2531,6 +2534,7 @@ public class Vpn { @Nullable private IpSecTunnelInterface mTunnelIface; @Nullable private IkeSession mSession; @Nullable private Network mActiveNetwork; + private final String mSessionKey; IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) { super(TAG); @@ -2876,7 +2880,6 @@ public class Vpn { */ private void disconnectVpnRunner() { mActiveNetwork = null; - mSessionKey = null; mIsRunning = false; resetIkeState(); @@ -3306,7 +3309,7 @@ public class Vpn { } private boolean isCurrentIkev2VpnLocked(@NonNull String packageName) { - return isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner; + return isCurrentPreparedPackage(packageName) && isIkev2VpnRunner(); } /** @@ -3360,6 +3363,16 @@ public class Vpn { return VpnProfile.decode("" /* Key unused */, encoded); } + private boolean isIkev2VpnRunner() { + return (mVpnRunner instanceof IkeV2VpnRunner); + } + + @GuardedBy("this") + @Nullable + private String getSessionKeyLocked() { + return isIkev2VpnRunner() ? ((IkeV2VpnRunner) mVpnRunner).mSessionKey : null; + } + /** * Starts an already provisioned VPN Profile, keyed by package name. * @@ -3387,7 +3400,11 @@ public class Vpn { } startVpnProfilePrivileged(profile, packageName); - return mSessionKey; + if (!isIkev2VpnRunner()) { + throw new IllegalStateException("mVpnRunner shouldn't be null and should also be " + + "an instance of Ikev2VpnRunner"); + } + return getSessionKeyLocked(); } finally { Binder.restoreCallingIdentity(token); } @@ -3490,11 +3507,8 @@ public class Vpn { } private VpnProfileState makeVpnProfileState() { - // TODO: mSessionKey will be moved to Ikev2VpnRunner once aosp/2007077 is merged, so after - // merging aosp/2007077, here should check Ikev2VpnRunner is null or not. Session key will - // be null if Ikev2VpnRunner is null. - return new VpnProfileState(getStateFromLegacyState(mLegacyState), mSessionKey, mAlwaysOn, - mLockdown); + return new VpnProfileState(getStateFromLegacyState(mLegacyState), + isIkev2VpnRunner() ? getSessionKeyLocked() : null, mAlwaysOn, mLockdown); } /** diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 9042326c5760..0eda980a29b6 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -656,7 +656,6 @@ public class NotificationManagerService extends SystemService { private int mWarnRemoteViewsSizeBytes; private int mStripRemoteViewsSizeBytes; - private boolean mForceUserSetOnUpgrade; private MetricsLogger mMetricsLogger; private NotificationChannelLogger mNotificationChannelLogger; @@ -2284,6 +2283,7 @@ public class NotificationManagerService extends SystemService { mNotificationChannelLogger, mAppOps, new SysUiStatsEvent.BuilderFactory()); + mPreferencesHelper.updateFixedImportance(mUm.getUsers()); mRankingHelper = new RankingHelper(getContext(), mRankingHandler, mPreferencesHelper, @@ -2471,9 +2471,6 @@ public class NotificationManagerService extends SystemService { WorkerHandler handler = new WorkerHandler(Looper.myLooper()); - mForceUserSetOnUpgrade = getContext().getResources().getBoolean( - R.bool.config_notificationForceUserSetOnUpgrade); - init(handler, new RankingHandlerWorker(mRankingThread.getLooper()), AppGlobals.getPackageManager(), getContext().getPackageManager(), getLocalService(LightsManager.class), @@ -2502,8 +2499,7 @@ public class NotificationManagerService extends SystemService { LocalServices.getService(ActivityManagerInternal.class), createToastRateLimiter(), new PermissionHelper(LocalServices.getService( PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(), - AppGlobals.getPermissionManager(), - mForceUserSetOnUpgrade), + AppGlobals.getPermissionManager()), LocalServices.getService(UsageStatsManagerInternal.class), getContext().getSystemService(TelecomManager.class), new NotificationChannelLoggerImpl()); @@ -3632,6 +3628,12 @@ public class NotificationManagerService extends SystemService { } @Override + public boolean isImportanceLocked(String pkg, int uid) { + checkCallerIsSystem(); + return mPreferencesHelper.isImportanceLocked(pkg, uid); + } + + @Override public boolean canShowBadge(String pkg, int uid) { checkCallerIsSystem(); return mPreferencesHelper.canShowBadge(pkg, uid); @@ -6146,7 +6148,6 @@ public class NotificationManagerService extends SystemService { pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate); pw.println(" hideSilentStatusBar=" + mPreferencesHelper.shouldHideSilentStatusIcons()); - pw.println(" mForceUserSetOnUpgrade=" + mForceUserSetOnUpgrade); } pw.println(" mArchive=" + mArchive.toString()); mArchive.dumpImpl(pw, filter); diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index b2fee1e8b545..a28547b1cda9 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -55,14 +55,12 @@ public final class PermissionHelper { private final PermissionManagerServiceInternal mPmi; private final IPackageManager mPackageManager; private final IPermissionManager mPermManager; - private final boolean mForceUserSetOnUpgrade; public PermissionHelper(PermissionManagerServiceInternal pmi, IPackageManager packageManager, - IPermissionManager permManager, boolean forceUserSetOnUpgrade) { + IPermissionManager permManager) { mPmi = pmi; mPackageManager = packageManager; mPermManager = permManager; - mForceUserSetOnUpgrade = forceUserSetOnUpgrade; } /** @@ -212,9 +210,8 @@ public final class PermissionHelper { return; } if (!isPermissionFixed(pkgPerm.packageName, pkgPerm.userId)) { - boolean userSet = mForceUserSetOnUpgrade ? true : pkgPerm.userModifiedSettings; setNotificationPermission(pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, - userSet, !userSet); + true /* userSet always true on upgrade */); } } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 4e3fbaa18870..97133a56779d 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -42,8 +42,10 @@ import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; +import android.content.pm.UserInfo; import android.metrics.LogMaker; import android.os.Binder; import android.os.Build; @@ -146,7 +148,6 @@ public class PreferencesHelper implements RankingConfig { static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = false; private static final boolean DEFAULT_SHOW_BADGE = true; - private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false; private static final boolean DEFAULT_APP_LOCKED_IMPORTANCE = false; static final boolean DEFAULT_BUBBLES_ENABLED = true; @@ -193,8 +194,6 @@ public class PreferencesHelper implements RankingConfig { private boolean mAllowInvalidShortcuts = false; - private Map<String, List<String>> mOemLockedApps = new HashMap(); - public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper, PermissionHelper permHelper, NotificationChannelLogger notificationChannelLogger, @@ -411,7 +410,7 @@ public class PreferencesHelper implements RankingConfig { channel.populateFromXml(parser); } channel.setImportanceLockedByCriticalDeviceFunction( - r.defaultAppLockedImportance); + r.defaultAppLockedImportance || r.fixedImportance); if (isShortcutOk(channel) && isDeletionOk(channel)) { r.channels.put(id, channel); @@ -484,14 +483,6 @@ public class PreferencesHelper implements RankingConfig { r.visibility = visibility; r.showBadge = showBadge; r.bubblePreference = bubblePreference; - if (mOemLockedApps.containsKey(r.pkg)) { - List<String> channels = mOemLockedApps.get(r.pkg); - if (channels == null || channels.isEmpty()) { - r.oemLockedImportance = true; - } else { - r.oemLockedChannels = channels; - } - } try { createDefaultChannelIfNeededLocked(r); @@ -818,6 +809,13 @@ public class PreferencesHelper implements RankingConfig { } } + boolean isImportanceLocked(String pkg, int uid) { + synchronized (mPackagePreferences) { + PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); + return r.fixedImportance || r.defaultAppLockedImportance; + } + } + @Override public boolean isGroupBlocked(String packageName, int uid, String groupId) { if (groupId == null) { @@ -1008,7 +1006,7 @@ public class PreferencesHelper implements RankingConfig { clearLockedFieldsLocked(channel); channel.setImportanceLockedByCriticalDeviceFunction( - r.defaultAppLockedImportance); + r.defaultAppLockedImportance || r.fixedImportance); if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) { channel.setLockscreenVisibility( @@ -1090,8 +1088,7 @@ public class PreferencesHelper implements RankingConfig { updatedChannel.unlockFields(updatedChannel.getUserLockedFields()); } - if ((mPermissionHelper.isPermissionFixed(r.pkg, UserHandle.getUserId(r.uid)) - || channel.isImportanceLockedByCriticalDeviceFunction()) + if (channel.isImportanceLockedByCriticalDeviceFunction() && !(channel.isBlockable() || channel.getImportance() == IMPORTANCE_NONE)) { updatedChannel.setImportance(channel.getImportance()); } @@ -1267,6 +1264,28 @@ public class PreferencesHelper implements RankingConfig { mHideSilentStatusBarIcons = hide; } + public void updateFixedImportance(List<UserInfo> users) { + for (UserInfo user : users) { + List<PackageInfo> packages = mPm.getInstalledPackagesAsUser( + PackageManager.PackageInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY), + user.getUserHandle().getIdentifier()); + for (PackageInfo pi : packages) { + boolean fixed = mPermissionHelper.isPermissionFixed( + pi.packageName, user.getUserHandle().getIdentifier()); + if (fixed) { + synchronized (mPackagePreferences) { + PackagePreferences p = getOrCreatePackagePreferencesLocked( + pi.packageName, pi.applicationInfo.uid); + p.fixedImportance = true; + for (NotificationChannel channel : p.channels.values()) { + channel.setImportanceLockedByCriticalDeviceFunction(true); + } + } + } + } + } + } + public void updateDefaultApps(int userId, ArraySet<String> toRemove, ArraySet<Pair<String, Integer>> toAdd) { synchronized (mPackagePreferences) { @@ -1274,8 +1293,10 @@ public class PreferencesHelper implements RankingConfig { if (userId == UserHandle.getUserId(p.uid)) { if (toRemove != null && toRemove.contains(p.pkg)) { p.defaultAppLockedImportance = false; - for (NotificationChannel channel : p.channels.values()) { - channel.setImportanceLockedByCriticalDeviceFunction(false); + if (!p.fixedImportance) { + for (NotificationChannel channel : p.channels.values()) { + channel.setImportanceLockedByCriticalDeviceFunction(false); + } } } } @@ -1934,13 +1955,9 @@ public class PreferencesHelper implements RankingConfig { pw.print(" defaultAppLocked="); pw.print(r.defaultAppLockedImportance); } - if (r.oemLockedImportance != DEFAULT_OEM_LOCKED_IMPORTANCE) { - pw.print(" oemLocked="); - pw.print(r.oemLockedImportance); - } - if (!r.oemLockedChannels.isEmpty()) { - pw.print(" futureLockedChannels="); - pw.print(r.oemLockedChannels); + if (r.fixedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) { + pw.print(" fixedImportance="); + pw.print(r.fixedImportance); } pw.println(); for (NotificationChannel channel : r.channels.values()) { @@ -2682,9 +2699,8 @@ public class PreferencesHelper implements RankingConfig { int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS; // these fields are loaded on boot from a different source of truth and so are not // written to notification policy xml - boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE; - List<String> oemLockedChannels = new ArrayList<>(); boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE; + boolean fixedImportance = DEFAULT_APP_LOCKED_IMPORTANCE; boolean hasSentInvalidMessage = false; boolean hasSentValidMessage = false; diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java index ed71f1eb5313..9d1f0704a3cf 100644 --- a/services/core/java/com/android/server/pm/BroadcastHelper.java +++ b/services/core/java/com/android/server/pm/BroadcastHelper.java @@ -197,7 +197,7 @@ public final class BroadcastHelper { final BroadcastOptions bOptions = getTemporaryAppAllowlistBroadcastOptions( REASON_LOCKED_BOOT_COMPLETED); am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null, - requiredPermissions, null, android.app.AppOpsManager.OP_NONE, + requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(), false, false, userId); // Deliver BOOT_COMPLETED only if user is unlocked @@ -207,7 +207,7 @@ public final class BroadcastHelper { bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); } am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null, - requiredPermissions, null, android.app.AppOpsManager.OP_NONE, + requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(), false, false, userId); } } catch (RemoteException e) { @@ -263,7 +263,7 @@ public final class BroadcastHelper { }; try { am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null, - requiredPermissions, null, android.app.AppOpsManager.OP_NONE, null, false, + requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, UserHandle.USER_ALL); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -301,7 +301,7 @@ public final class BroadcastHelper { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); try { am.broadcastIntentWithFeature(null, null, intent, null, null, - 0, null, null, null, null, android.app.AppOpsManager.OP_NONE, + 0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, userId); } catch (RemoteException e) { } diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index 5a811c89c5b9..652847ad1647 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -315,9 +315,9 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Deprecated public final List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, - int filterCallingUid, int userId) { - return getResolveIntentHelper().queryIntentReceiversInternal( - snapshot(), intent, resolvedType, flags, userId, filterCallingUid); + int filterCallingUid, int userId, boolean forSend) { + return getResolveIntentHelper().queryIntentReceiversInternal(snapshot(), intent, + resolvedType, flags, userId, filterCallingUid, forSend); } @Override diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java index b74670b77a11..2db1c2029d9c 100644 --- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java +++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java @@ -306,16 +306,32 @@ final class ResolveIntentHelper { return new IntentSender(target); } - // In this method, we have to know the actual calling UID, but in some cases Binder's - // call identity is removed, so the UID has to be passed in explicitly. - public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Computer computer, Intent intent, + /** + * Retrieve all receivers that can handle a broadcast of the given intent. + * @param queryingUid the results will be filtered in the context of this UID instead. + */ + @NonNull + public List<ResolveInfo> queryIntentReceiversInternal(Computer computer, Intent intent, + String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId, + int queryingUid) { + return queryIntentReceiversInternal(computer, intent, resolvedType, flags, userId, + queryingUid, false); + } + + /** + * @see PackageManagerInternal#queryIntentReceivers(Intent, String, long, int, int, boolean) + */ + @NonNull + public List<ResolveInfo> queryIntentReceiversInternal(Computer computer, Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId, - int filterCallingUid) { + int filterCallingUid, boolean forSend) { if (!mUserManager.exists(userId)) return Collections.emptyList(); - computer.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/, - false /*checkShell*/, "query intent receivers"); - final String instantAppPkgName = computer.getInstantAppPackageName(filterCallingUid); - flags = computer.updateFlagsForResolve(flags, userId, filterCallingUid, + // The identity used to filter the receiver components + final int queryingUid = forSend ? Process.SYSTEM_UID : filterCallingUid; + computer.enforceCrossUserPermission(queryingUid, userId, + false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); + final String instantAppPkgName = computer.getInstantAppPackageName(queryingUid); + flags = computer.updateFlagsForResolve(flags, userId, queryingUid, false /*includeInstantApps*/, computer.isImplicitImageCaptureIntentAndNotSetByDpc(intent, userId, resolvedType, flags)); @@ -397,7 +413,7 @@ final class ResolveIntentHelper { list, true, originalIntent, resolvedType, filterCallingUid); } - return computer.applyPostResolutionFilter(list, instantAppPkgName, false, filterCallingUid, + return computer.applyPostResolutionFilter(list, instantAppPkgName, false, queryingUid, false, userId, intent); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index 7be83b03243a..d34682df3413 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -42,6 +42,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE; import static android.content.pm.PackageManager.MASK_PERMISSION_FLAGS_ALL; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.INVALID_UID; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED; @@ -97,6 +98,7 @@ import android.os.storage.StorageManager; import android.permission.IOnPermissionsChangeListener; import android.permission.PermissionControllerManager; import android.permission.PermissionManager; +import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -333,7 +335,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt mPackageManagerInt.writeSettings(true); } @Override - public void onPermissionRevoked(int uid, int userId, String reason, boolean overrideKill) { + public void onPermissionRevoked(int uid, int userId, String reason, boolean overrideKill, + @Nullable String permissionName) { mOnPermissionChangeListeners.onPermissionsChanged(uid); // Critical; after this call the application should never have the permission @@ -342,13 +345,41 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt return; } - final int appId = UserHandle.getAppId(uid); - if (reason == null) { - mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED)); - } else { - mHandler.post(() -> killUid(appId, userId, reason)); + mHandler.post(() -> { + if (POST_NOTIFICATIONS.equals(permissionName) + && isAppBackupAndRestoreRunning(uid)) { + return; + } + + final int appId = UserHandle.getAppId(uid); + if (reason == null) { + killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED); + } else { + killUid(appId, userId, reason); + } + }); + } + + private boolean isAppBackupAndRestoreRunning(int uid) { + if (checkUidPermission(uid, Manifest.permission.BACKUP) != PERMISSION_GRANTED) { + return false; + } + + try { + int userId = UserHandle.getUserId(uid); + boolean isInSetup = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, userId) == 0; + boolean isInDeferredSetup = Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) + == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED; + return isInSetup || isInDeferredSetup; + } catch (Settings.SettingNotFoundException e) { + Slog.w(LOG_TAG, "Failed to check if the user is in restore: " + e); + return false; } } + @Override public void onInstallPermissionRevoked() { mPackageManagerInt.writeSettings(true); @@ -1600,7 +1631,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt if (callback != null) { if (isRuntimePermission) { callback.onPermissionRevoked(UserHandle.getUid(userId, pkg.getUid()), userId, - reason, overrideKill); + reason, overrideKill, permName); } else { mDefaultPermissionCallback.onInstallPermissionRevoked(); } @@ -5246,7 +5277,11 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt onPermissionRevoked(uid, userId, reason, false); } public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason, - boolean overrideKill) {} + boolean overrideKill) { + onPermissionRevoked(uid, userId, reason, false, null); + } + public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason, + boolean overrideKill, @Nullable String permissionName) {} public void onInstallPermissionRevoked() {} public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) {} public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync, diff --git a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java index 750d4004cb60..fd6ec065d421 100644 --- a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java +++ b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java @@ -16,44 +16,105 @@ package com.android.server.sensorprivacy; +import static android.hardware.SensorManager.SENSOR_DELAY_NORMAL; + +import android.annotation.ColorInt; import android.app.AppOpsManager; import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.hardware.lights.Light; import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; import android.hardware.lights.LightsRequest; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Looper; +import android.os.SystemClock; import android.permission.PermissionManager; import android.util.ArraySet; +import android.util.Pair; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.FgThread; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.concurrent.Executor; + +class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener, + SensorEventListener { -class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener { + @VisibleForTesting + static final double LIGHT_VALUE_MULTIPLIER = 1 / Math.log(1.1); + private final Handler mHandler; + private final Executor mExecutor; private final Context mContext; + private final AppOpsManager mAppOpsManager; private final LightsManager mLightsManager; + private final SensorManager mSensorManager; private final Set<String> mActivePackages = new ArraySet<>(); private final Set<String> mActivePhonePackages = new ArraySet<>(); - private final int mCameraPrivacyLightColor; - private final List<Light> mCameraLights = new ArrayList<>(); - private final AppOpsManager mAppOpsManager; private LightsManager.LightsSession mLightsSession = null; + @ColorInt + private final int mDayColor; + @ColorInt + private final int mNightColor; + + private final Sensor mLightSensor; + + private boolean mIsAmbientLightListenerRegistered = false; + private final long mMovingAverageIntervalMillis; + /** When average of the time integral over the past {@link #mMovingAverageIntervalMillis} + * milliseconds of the log_1.1(lux(t)) is greater than this value, use the daytime brightness + * else use nighttime brightness. */ + private final long mNightThreshold; + private final ArrayDeque<Pair<Long, Integer>> mAmbientLightValues = new ArrayDeque<>(); + /** Tracks the Riemann sum of {@link #mAmbientLightValues} to avoid O(n) operations when sum is + * needed */ + private long mAlvSum = 0; + private int mLastLightColor = 0; + /** The elapsed real time that the ALS was started watching */ + private long mElapsedTimeStartedReading; + + private final Object mDelayedUpdateToken = new Object(); + + // Can't mock static native methods, workaround for testing + private long mElapsedRealTime = -1; + CameraPrivacyLightController(Context context) { + this(context, FgThread.get().getLooper()); + } + + @VisibleForTesting + CameraPrivacyLightController(Context context, Looper looper) { mContext = context; + mHandler = new Handler(looper); + mExecutor = new HandlerExecutor(mHandler); + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mLightsManager = mContext.getSystemService(LightsManager.class); + mSensorManager = mContext.getSystemService(SensorManager.class); - mCameraPrivacyLightColor = mContext.getColor(R.color.camera_privacy_light); + mDayColor = mContext.getColor(R.color.camera_privacy_light_day); + mNightColor = mContext.getColor(R.color.camera_privacy_light_night); + mMovingAverageIntervalMillis = mContext.getResources() + .getInteger(R.integer.config_cameraPrivacyLightAlsAveragingIntervalMillis); + mNightThreshold = (long) (Math.log(mContext.getResources() + .getInteger(R.integer.config_cameraPrivacyLightAlsNightThreshold)) + * LIGHT_VALUE_MULTIPLIER); List<Light> lights = mLightsManager.getLights(); for (int i = 0; i < lights.size(); i++) { @@ -64,12 +125,60 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis } if (mCameraLights.isEmpty()) { + mLightSensor = null; return; } mAppOpsManager.startWatchingActive( new String[] {AppOpsManager.OPSTR_CAMERA, AppOpsManager.OPSTR_PHONE_CALL_CAMERA}, - FgThread.getExecutor(), this); + mExecutor, this); + + // It may be useful in the future to configure devices to know which lights are near which + // sensors so that we can control individual lights based on their environment. + mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); + } + + private void addElement(long time, int value) { + if (mAmbientLightValues.isEmpty()) { + // Eliminate the size == 1 edge case and assume the light value has been constant for + // the previous interval + mAmbientLightValues.add(new Pair<>(time - getCurrentIntervalMillis() - 1, value)); + } + Pair<Long, Integer> lastElement = mAmbientLightValues.peekLast(); + mAmbientLightValues.add(new Pair<>(time, value)); + + mAlvSum += (time - lastElement.first) * lastElement.second; + removeObsoleteData(time); + } + + private void removeObsoleteData(long time) { + while (mAmbientLightValues.size() > 1) { + Pair<Long, Integer> element0 = mAmbientLightValues.pollFirst(); // NOTICE: POLL + Pair<Long, Integer> element1 = mAmbientLightValues.peekFirst(); // NOTICE: PEEK + if (element1.first > time - getCurrentIntervalMillis()) { + mAmbientLightValues.addFirst(element0); + break; + } + mAlvSum -= (element1.first - element0.first) * element0.second; + } + } + + /** + * Gives the Riemann sum of {@link #mAmbientLightValues} where the part of the interval that + * stretches outside the time window is removed and the time since the last change is added in. + */ + private long getLiveAmbientLightTotal() { + if (mAmbientLightValues.isEmpty()) { + return mAlvSum; + } + long time = getElapsedRealTime(); + removeObsoleteData(time); + + Pair<Long, Integer> firstElement = mAmbientLightValues.peekFirst(); + Pair<Long, Integer> lastElement = mAmbientLightValues.peekLast(); + + return mAlvSum - Math.max(0, time - getCurrentIntervalMillis() - firstElement.first) + * firstElement.second + (time - lastElement.first) * lastElement.second; } @Override @@ -93,10 +202,16 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis } private void updateLightSession() { + if (Looper.myLooper() != mHandler.getLooper()) { + mHandler.post(this::updateLightSession); + return; + } + Set<String> exemptedPackages = PermissionManager.getIndicatorExemptedPackages(mContext); boolean shouldSessionEnd = exemptedPackages.containsAll(mActivePackages) && exemptedPackages.containsAll(mActivePhonePackages); + updateSensorListener(shouldSessionEnd); if (shouldSessionEnd) { if (mLightsSession == null) { @@ -106,20 +221,73 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis mLightsSession.close(); mLightsSession = null; } else { - if (mLightsSession != null) { + int lightColor; + if (mLightSensor != null && getLiveAmbientLightTotal() + < getCurrentIntervalMillis() * mNightThreshold) { + lightColor = mNightColor; + } else { + lightColor = mDayColor; + } + + if (mLastLightColor == lightColor && mLightsSession != null) { return; } + mLastLightColor = lightColor; LightsRequest.Builder requestBuilder = new LightsRequest.Builder(); for (int i = 0; i < mCameraLights.size(); i++) { requestBuilder.addLight(mCameraLights.get(i), new LightState.Builder() - .setColor(mCameraPrivacyLightColor) + .setColor(lightColor) .build()); } - mLightsSession = mLightsManager.openSession(Integer.MAX_VALUE); + if (mLightsSession == null) { + mLightsSession = mLightsManager.openSession(Integer.MAX_VALUE); + } + mLightsSession.requestLights(requestBuilder.build()); } } + + private void updateSensorListener(boolean shouldSessionEnd) { + if (shouldSessionEnd && mIsAmbientLightListenerRegistered) { + mSensorManager.unregisterListener(this); + mIsAmbientLightListenerRegistered = false; + } + if (!shouldSessionEnd && !mIsAmbientLightListenerRegistered && mLightSensor != null) { + mSensorManager.registerListener(this, mLightSensor, SENSOR_DELAY_NORMAL, mHandler); + mIsAmbientLightListenerRegistered = true; + mElapsedTimeStartedReading = getElapsedRealTime(); + } + } + + private long getElapsedRealTime() { + return mElapsedRealTime == -1 ? SystemClock.elapsedRealtime() : mElapsedRealTime; + } + + @VisibleForTesting + void setElapsedRealTime(long time) { + mElapsedRealTime = time; + } + + @Override + public void onSensorChanged(SensorEvent event) { + // Using log space to represent human sensation (Fechner's Law) instead of lux + // because lux values causes bright flashes to skew the average very high. + addElement(event.timestamp, Math.max(0, + (int) (Math.log(event.values[0]) * LIGHT_VALUE_MULTIPLIER))); + updateLightSession(); + mHandler.removeCallbacksAndMessages(mDelayedUpdateToken); + mHandler.postDelayed(CameraPrivacyLightController.this::updateLightSession, + mDelayedUpdateToken, mMovingAverageIntervalMillis); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) {} + + private long getCurrentIntervalMillis() { + return Math.min(mMovingAverageIntervalMillis, + getElapsedRealTime() - mElapsedTimeStartedReading); + } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index abbcd28575cd..189fff865ea0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5095,13 +5095,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // still check DC#okToAnimate again if the transition animation is fine to apply. // TODO(new-app-transition): Rewrite this logic using WM Shell. final boolean recentsAnimating = isAnimating(PARENTS, ANIMATION_TYPE_RECENTS); + final boolean isEnteringPipWithoutVisibleChange = mWaitForEnteringPinnedMode + && mVisible == visible; if (okToAnimate(true /* ignoreFrozen */, canTurnScreenOn()) && (appTransition.isTransitionSet() || (recentsAnimating && !isActivityTypeHome())) - // If the visibility change during enter PIP, we don't want to include it in app - // transition to affect the animation theme, because the Pip organizer will animate - // the entering PIP instead. - && !mWaitForEnteringPinnedMode) { + // If the visibility is not changed during enter PIP, we don't want to include it in + // app transition to affect the animation theme, because the Pip organizer will + // animate the entering PIP instead. + && !isEnteringPipWithoutVisibleChange) { if (visible) { displayContent.mOpeningApps.add(this); mEnteringAnimation = true; @@ -6340,8 +6342,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mSharedStartingData != null ? mSharedStartingData.mAssociatedTask : null; if (associatedTask == null) { removeStartingWindow(); - } else if (associatedTask.getActivity( - r -> r.mVisibleRequested && !r.firstWindowDrawn) == null) { + } else if (associatedTask.getActivity(r -> r.mVisibleRequested && !r.firstWindowDrawn + // Don't block starting window removal if an Activity can't be a starting window + // target. + && r.mSharedStartingData != null) == null) { // The last drawn activity may not be the one that owns the starting window. final ActivityRecord r = associatedTask.topActivityContainsStartingWindow(); if (r != null) { diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 72408b67de41..d60981fcf504 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -518,6 +518,8 @@ public class ActivityStartController { .setRequestCode(-1) .setCallingUid(callingUid) .setCallingPid(callingPid) + .setRealCallingUid(callingUid) + .setRealCallingPid(callingPid) .setUserId(caller != null ? caller.mUserId : mService.getCurrentUserId()) .execute(); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c12f7f33b069..66eca990b14f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -6228,6 +6228,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** * Sets if Display APIs should be sandboxed to the activity window bounds. */ + @VisibleForTesting void setSandboxDisplayApis(boolean sandboxDisplayApis) { mSandboxDisplayApis = sandboxDisplayApis; } diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index ad2767c41e82..d02ad992c7e8 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -22,6 +22,7 @@ import android.content.Context; import android.graphics.Color; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -156,6 +157,7 @@ final class LetterboxConfiguration { * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and * the framework implementation will be used to determine the aspect ratio. */ + @VisibleForTesting void setFixedOrientationLetterboxAspectRatio(float aspectRatio) { mFixedOrientationLetterboxAspectRatio = aspectRatio; } @@ -164,6 +166,7 @@ final class LetterboxConfiguration { * Resets the aspect ratio of letterbox for fixed orientation to {@link * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}. */ + @VisibleForTesting void resetFixedOrientationLetterboxAspectRatio() { mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio); @@ -177,25 +180,6 @@ final class LetterboxConfiguration { } /** - * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0, - * both it and a value of {@link - * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and - * corners of the activity won't be rounded. - */ - void setLetterboxActivityCornersRadius(int cornersRadius) { - mLetterboxActivityCornersRadius = cornersRadius; - } - - /** - * Resets corners raidus for activities presented in the letterbox mode to {@link - * com.android.internal.R.integer.config_letterboxActivityCornersRadius}. - */ - void resetLetterboxActivityCornersRadius() { - mLetterboxActivityCornersRadius = mContext.getResources().getInteger( - com.android.internal.R.integer.config_letterboxActivityCornersRadius); - } - - /** * Whether corners of letterboxed activities are rounded. */ boolean isLetterboxActivityCornersRounded() { @@ -226,34 +210,6 @@ final class LetterboxConfiguration { return Color.valueOf(mContext.getResources().getColor(colorId)); } - - /** - * Sets color of letterbox background which is used when {@link - * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as - * fallback for other backfround types. - */ - void setLetterboxBackgroundColor(Color color) { - mLetterboxBackgroundColorOverride = color; - } - - /** - * Sets color ID of letterbox background which is used when {@link - * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as - * fallback for other backfround types. - */ - void setLetterboxBackgroundColorResourceId(int colorId) { - mLetterboxBackgroundColorResourceIdOverride = colorId; - } - - /** - * Resets color of letterbox background to {@link - * com.android.internal.R.color.config_letterboxBackgroundColor}. - */ - void resetLetterboxBackgroundColor() { - mLetterboxBackgroundColorOverride = null; - mLetterboxBackgroundColorResourceIdOverride = null; - } - /** * Gets {@link LetterboxBackgroundType} specified in {@link * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command. @@ -263,19 +219,6 @@ final class LetterboxConfiguration { return mLetterboxBackgroundType; } - /** Sets letterbox background type. */ - void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) { - mLetterboxBackgroundType = backgroundType; - } - - /** - * Resets cletterbox background type to {@link - * com.android.internal.R.integer.config_letterboxBackgroundType}. - */ - void resetLetterboxBackgroundType() { - mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); - } - /** Returns a string representing the given {@link LetterboxBackgroundType}. */ static String letterboxBackgroundTypeToString( @LetterboxBackgroundType int backgroundType) { @@ -305,27 +248,6 @@ final class LetterboxConfiguration { } /** - * Overrides alpha of a black scrim shown over wallpaper for {@link - * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}. - * - * <p>If given value is < 0 or >= 1, both it and a value of {@link - * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored - * and 0.0 (transparent) is instead. - */ - void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) { - mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha; - } - - /** - * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link - * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}. - */ - void resetLetterboxBackgroundWallpaperDarkScrimAlpha() { - mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat( - com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha); - } - - /** * Gets alpha of a black scrim shown over wallpaper letterbox background. */ float getLetterboxBackgroundWallpaperDarkScrimAlpha() { @@ -333,28 +255,6 @@ final class LetterboxConfiguration { } /** - * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in - * {@link mLetterboxBackgroundType}. - * - * <p> If given value <= 0, both it and a value of {@link - * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored - * and 0 is used instead. - */ - void setLetterboxBackgroundWallpaperBlurRadius(int radius) { - mLetterboxBackgroundWallpaperBlurRadius = radius; - } - - /** - * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link - * mLetterboxBackgroundType} to {@link - * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}. - */ - void resetLetterboxBackgroundWallpaperBlurRadius() { - mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius); - } - - /** * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link * mLetterboxBackgroundType}. */ @@ -381,6 +281,7 @@ final class LetterboxConfiguration { * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and * central position (0.5) is used. */ + @VisibleForTesting void setLetterboxHorizontalPositionMultiplier(float multiplier) { mLetterboxHorizontalPositionMultiplier = multiplier; } @@ -389,6 +290,7 @@ final class LetterboxConfiguration { * Resets horizontal position of a center of the letterboxed app window to {@link * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}. */ + @VisibleForTesting void resetLetterboxHorizontalPositionMultiplier() { mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat( com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier); @@ -406,6 +308,7 @@ final class LetterboxConfiguration { * Overrides whether reachability repositioning is allowed for letterboxed fullscreen apps in * landscape device orientation. */ + @VisibleForTesting void setIsReachabilityEnabled(boolean enabled) { mIsReachabilityEnabled = enabled; } @@ -414,6 +317,7 @@ final class LetterboxConfiguration { * Resets whether reachability repositioning is allowed for letterboxed fullscreen apps in * landscape device orientation to {@link R.bool.config_letterboxIsReachabilityEnabled}. */ + @VisibleForTesting void resetIsReachabilityEnabled() { mIsReachabilityEnabled = mContext.getResources().getBoolean( R.bool.config_letterboxIsReachabilityEnabled); @@ -429,22 +333,6 @@ final class LetterboxConfiguration { return mDefaultPositionForReachability; } - /** - * Overrides default horizonal position of the letterboxed app window when reachability - * is enabled. - */ - void setDefaultPositionForReachability(@LetterboxReachabilityPosition int position) { - mDefaultPositionForReachability = position; - } - - /** - * Resets default horizontal position of the letterboxed app window when reachability is - * enabled to {@link R.integer.config_letterboxDefaultPositionForReachability}. - */ - void resetDefaultPositionForReachability() { - mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext); - } - @LetterboxReachabilityPosition private static int readLetterboxReachabilityPositionFromConfig(Context context) { int position = context.getResources().getInteger( @@ -516,17 +404,8 @@ final class LetterboxConfiguration { /** * Overrides whether education is allowed for letterboxed fullscreen apps. */ + @VisibleForTesting void setIsEducationEnabled(boolean enabled) { mIsEducationEnabled = enabled; } - - /** - * Resets whether education is allowed for letterboxed fullscreen apps to - * {@link R.bool.config_letterboxIsEducationEnabled}. - */ - void resetIsEducationEnabled() { - mIsEducationEnabled = mContext.getResources().getBoolean( - R.bool.config_letterboxIsEducationEnabled); - } - } diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 160fc95f3f7c..7a055d2948ad 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -64,6 +64,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.telephony.CellBroadcastUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.am.ActivityManagerService; @@ -392,6 +393,10 @@ public class LockTaskController { return false; } + if (isWirelessEmergencyAlert(intent)) { + return false; + } + return !(isTaskAuthAllowlisted(taskAuth) || mLockTaskModeTasks.isEmpty()); } @@ -424,6 +429,25 @@ public class LockTaskController { return isPackageAllowlisted(userId, packageName); } + private boolean isWirelessEmergencyAlert(Intent intent) { + if (intent == null) { + return false; + } + + final ComponentName cellBroadcastAlertDialogComponentName = + CellBroadcastUtils.getDefaultCellBroadcastAlertDialogComponent(mContext); + + if (cellBroadcastAlertDialogComponentName == null) { + return false; + } + + if (cellBroadcastAlertDialogComponentName.equals(intent.getComponent())) { + return true; + } + + return false; + } + private boolean isEmergencyCallIntent(Intent intent) { if (intent == null) { return false; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9872f55ad383..7a5480401de8 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5704,25 +5704,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) { - if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); - } - - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final DisplayContent displayContent = mRoot.getDisplayContent(displayId); - if (displayContent != null) { - displayContent.setSandboxDisplayApis(sandboxDisplayApis); - } - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - /** The global settings only apply to default display. */ private boolean applyForcedPropertiesForDefaultDisplay() { boolean changed = false; diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 5a2f28f4a365..34c93482ecfe 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -19,16 +19,6 @@ package com.android.server.wm; import static android.os.Build.IS_USER; import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_CENTER; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_LEFT; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_RIGHT; - -import android.content.res.Resources.NotFoundException; -import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.os.ParcelFileDescriptor; @@ -46,8 +36,6 @@ import com.android.internal.os.ByteTransferPipe; import com.android.internal.protolog.ProtoLogImpl; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; -import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType; -import com.android.server.wm.LetterboxConfiguration.LetterboxReachabilityPosition; import java.io.IOException; import java.io.PrintWriter; @@ -70,12 +58,10 @@ public class WindowManagerShellCommand extends ShellCommand { // Internal service impl -- must perform security checks before touching. private final WindowManagerService mInternal; - private final LetterboxConfiguration mLetterboxConfiguration; public WindowManagerShellCommand(WindowManagerService service) { mInterface = service; mInternal = service; - mLetterboxConfiguration = service.mLetterboxConfiguration; } @Override @@ -127,14 +113,6 @@ public class WindowManagerShellCommand extends ShellCommand { return runGetIgnoreOrientationRequest(pw); case "dump-visible-window-views": return runDumpVisibleWindowViews(pw); - case "set-letterbox-style": - return runSetLetterboxStyle(pw); - case "get-letterbox-style": - return runGetLetterboxStyle(pw); - case "reset-letterbox-style": - return runResetLetterboxStyle(pw); - case "set-sandbox-display-apis": - return runSandboxDisplayApis(pw); case "set-multi-window-config": return runSetMultiWindowConfig(); case "get-multi-window-config": @@ -353,37 +331,6 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } - /** - * Override display size and metrics to reflect the DisplayArea of the calling activity. - */ - private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException { - int displayId = Display.DEFAULT_DISPLAY; - String arg = getNextArgRequired(); - if ("-d".equals(arg)) { - displayId = Integer.parseInt(getNextArgRequired()); - arg = getNextArgRequired(); - } - - final boolean sandboxDisplayApis; - switch (arg) { - case "true": - case "1": - sandboxDisplayApis = true; - break; - case "false": - case "0": - sandboxDisplayApis = false; - break; - default: - getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we " - + "get " + arg); - return -1; - } - - mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis); - return 0; - } - private int runDismissKeyguard(PrintWriter pw) throws RemoteException { mInterface.dismissKeyguard(null /* callback */, null /* message */); return 0; @@ -606,347 +553,6 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } - private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException { - final float aspectRatio; - try { - String arg = getNextArgRequired(); - aspectRatio = Float.parseFloat(arg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: bad aspect ratio format " + e); - return -1; - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: aspect ratio should be provided as an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio); - } - return 0; - } - - private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException { - final int cornersRadius; - try { - String arg = getNextArgRequired(); - cornersRadius = Integer.parseInt(arg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: bad corners radius format " + e); - return -1; - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: corners radius should be provided as an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius); - } - return 0; - } - - private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException { - @LetterboxBackgroundType final int backgroundType; - try { - String arg = getNextArgRequired(); - switch (arg) { - case "solid_color": - backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR; - break; - case "app_color_background": - backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; - break; - case "app_color_background_floating": - backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; - break; - case "wallpaper": - backgroundType = LETTERBOX_BACKGROUND_WALLPAPER; - break; - default: - getErrPrintWriter().println( - "Error: 'solid_color', 'app_color_background' or " - + "'wallpaper' should be provided as an argument"); - return -1; - } - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: 'solid_color', 'app_color_background' or " - + "'wallpaper' should be provided as an argument" + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType); - } - return 0; - } - - private int runSetLetterboxBackgroundColorResource(PrintWriter pw) throws RemoteException { - final int colorId; - try { - String arg = getNextArgRequired(); - colorId = mInternal.mContext.getResources() - .getIdentifier(arg, "color", "com.android.internal"); - } catch (NotFoundException e) { - getErrPrintWriter().println( - "Error: color in '@android:color/resource_name' format should be provided as " - + "an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundColorResourceId(colorId); - } - return 0; - } - - private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException { - final Color color; - try { - String arg = getNextArgRequired(); - color = Color.valueOf(Color.parseColor(arg)); - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: color in #RRGGBB format should be provided as " - + "an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundColor(color); - } - return 0; - } - - private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw) - throws RemoteException { - final int radius; - try { - String arg = getNextArgRequired(); - radius = Integer.parseInt(arg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: blur radius format " + e); - return -1; - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: blur radius should be provided as an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius); - } - return 0; - } - - private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw) - throws RemoteException { - final float alpha; - try { - String arg = getNextArgRequired(); - alpha = Float.parseFloat(arg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: bad alpha format " + e); - return -1; - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: alpha should be provided as an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha); - } - return 0; - } - - private int runSetLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException { - final float multiplier; - try { - String arg = getNextArgRequired(); - multiplier = Float.parseFloat(arg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: bad multiplier format " + e); - return -1; - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: multiplier should be provided as an argument " + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier); - } - return 0; - } - - private int runSetLetterboxIsReachabilityEnabled(PrintWriter pw) throws RemoteException { - String arg = getNextArg(); - final boolean enabled; - switch (arg) { - case "true": - case "1": - enabled = true; - break; - case "false": - case "0": - enabled = false; - break; - default: - getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg); - return -1; - } - - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setIsReachabilityEnabled(enabled); - } - return 0; - } - - private int runSetLetterboxDefaultPositionForReachability(PrintWriter pw) - throws RemoteException { - @LetterboxReachabilityPosition final int position; - try { - String arg = getNextArgRequired(); - switch (arg) { - case "left": - position = LETTERBOX_REACHABILITY_POSITION_LEFT; - break; - case "center": - position = LETTERBOX_REACHABILITY_POSITION_CENTER; - break; - case "right": - position = LETTERBOX_REACHABILITY_POSITION_RIGHT; - break; - default: - getErrPrintWriter().println( - "Error: 'left', 'center' or 'right' are expected as an argument"); - return -1; - } - } catch (IllegalArgumentException e) { - getErrPrintWriter().println( - "Error: 'left', 'center' or 'right' are expected as an argument" + e); - return -1; - } - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setDefaultPositionForReachability(position); - } - return 0; - } - - private int runSetLetterboxIsEducationEnabled(PrintWriter pw) throws RemoteException { - String arg = getNextArg(); - final boolean enabled; - switch (arg) { - case "true": - case "1": - enabled = true; - break; - case "false": - case "0": - enabled = false; - break; - default: - getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg); - return -1; - } - - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setIsEducationEnabled(enabled); - } - return 0; - } - - private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException { - if (peekNextArg() == null) { - getErrPrintWriter().println("Error: No arguments provided."); - } - while (peekNextArg() != null) { - String arg = getNextArg(); - switch (arg) { - case "--aspectRatio": - runSetFixedOrientationLetterboxAspectRatio(pw); - break; - case "--cornerRadius": - runSetLetterboxActivityCornersRadius(pw); - break; - case "--backgroundType": - runSetLetterboxBackgroundType(pw); - break; - case "--backgroundColor": - runSetLetterboxBackgroundColor(pw); - break; - case "--backgroundColorResource": - runSetLetterboxBackgroundColorResource(pw); - break; - case "--wallpaperBlurRadius": - runSetLetterboxBackgroundWallpaperBlurRadius(pw); - break; - case "--wallpaperDarkScrimAlpha": - runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw); - break; - case "--horizontalPositionMultiplier": - runSetLetterboxHorizontalPositionMultiplier(pw); - break; - case "--isReachabilityEnabled": - runSetLetterboxIsReachabilityEnabled(pw); - break; - case "--defaultPositionForReachability": - runSetLetterboxDefaultPositionForReachability(pw); - break; - case "--isEducationEnabled": - runSetLetterboxIsEducationEnabled(pw); - break; - default: - getErrPrintWriter().println( - "Error: Unrecognized letterbox style option: " + arg); - return -1; - } - } - return 0; - } - - private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException { - if (peekNextArg() == null) { - resetLetterboxStyle(); - } - synchronized (mInternal.mGlobalLock) { - while (peekNextArg() != null) { - String arg = getNextArg(); - switch (arg) { - case "aspectRatio": - mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); - break; - case "cornerRadius": - mLetterboxConfiguration.resetLetterboxActivityCornersRadius(); - break; - case "backgroundType": - mLetterboxConfiguration.resetLetterboxBackgroundType(); - break; - case "backgroundColor": - mLetterboxConfiguration.resetLetterboxBackgroundColor(); - break; - case "wallpaperBlurRadius": - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); - break; - case "wallpaperDarkScrimAlpha": - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); - break; - case "horizontalPositionMultiplier": - mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); - break; - case "isReachabilityEnabled": - mLetterboxConfiguration.getIsReachabilityEnabled(); - break; - case "defaultPositionForReachability": - mLetterboxConfiguration.getDefaultPositionForReachability(); - break; - case "isEducationEnabled": - mLetterboxConfiguration.getIsEducationEnabled(); - break; - default: - getErrPrintWriter().println( - "Error: Unrecognized letterbox style option: " + arg); - return -1; - } - } - } - return 0; - } - private int runSetMultiWindowConfig() { if (peekNextArg() == null) { getErrPrintWriter().println("Error: No arguments provided."); @@ -1021,50 +627,6 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } - private void resetLetterboxStyle() { - synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); - mLetterboxConfiguration.resetLetterboxActivityCornersRadius(); - mLetterboxConfiguration.resetLetterboxBackgroundType(); - mLetterboxConfiguration.resetLetterboxBackgroundColor(); - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); - mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); - mLetterboxConfiguration.resetIsReachabilityEnabled(); - mLetterboxConfiguration.resetDefaultPositionForReachability(); - mLetterboxConfiguration.resetIsEducationEnabled(); - } - } - - private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException { - synchronized (mInternal.mGlobalLock) { - pw.println("Corner radius: " - + mLetterboxConfiguration.getLetterboxActivityCornersRadius()); - pw.println("Horizontal position multiplier: " - + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier()); - pw.println("Aspect ratio: " - + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio()); - pw.println("Is reachability enabled: " - + mLetterboxConfiguration.getIsReachabilityEnabled()); - pw.println("Default position for reachability: " - + LetterboxConfiguration.letterboxReachabilityPositionToString( - mLetterboxConfiguration.getDefaultPositionForReachability())); - pw.println("Is education enabled: " - + mLetterboxConfiguration.getIsEducationEnabled()); - - pw.println("Background type: " - + LetterboxConfiguration.letterboxBackgroundTypeToString( - mLetterboxConfiguration.getLetterboxBackgroundType())); - pw.println(" Background color: " + Integer.toHexString( - mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb())); - pw.println(" Wallpaper blur radius: " - + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius()); - pw.println(" Wallpaper dark scrim alpha: " - + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha()); - } - return 0; - } - private int runReset(PrintWriter pw) throws RemoteException { int displayId = getDisplayId(getNextArg()); @@ -1089,12 +651,6 @@ public class WindowManagerShellCommand extends ShellCommand { // set-ignore-orientation-request mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */); - // set-letterbox-style - resetLetterboxStyle(); - - // set-sandbox-display-apis - mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true); - // set-multi-window-config runResetMultiWindowConfig(); @@ -1129,12 +685,7 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]"); pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] "); pw.println(" If app requested orientation should be ignored."); - pw.println(" set-sandbox-display-apis [true|1|false|0]"); - pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect "); - pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or"); - pw.println(" Size Compat Mode."); - printLetterboxHelp(pw); printMultiWindowConfigHelp(pw); pw.println(" reset [-d DISPLAY_ID]"); @@ -1147,63 +698,6 @@ public class WindowManagerShellCommand extends ShellCommand { } } - private void printLetterboxHelp(PrintWriter pw) { - pw.println(" set-letterbox-style"); - pw.println(" Sets letterbox style using the following options:"); - pw.println(" --aspectRatio aspectRatio"); - pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= " - + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO); - pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will"); - pw.println(" be ignored and framework implementation will determine aspect ratio."); - pw.println(" --cornerRadius radius"); - pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,"); - pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be"); - pw.println(" ignored and corners of the activity won't be rounded."); - pw.println(" --backgroundType [reset|solid_color|app_color_background"); - pw.println(" |app_color_background_floating|wallpaper]"); - pw.println(" Type of background used in the letterbox mode."); - pw.println(" --backgroundColor color"); - pw.println(" Color of letterbox which is be used when letterbox background type"); - pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control"); - pw.println(" letterbox background type. See Color#parseColor for allowed color"); - pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive)."); - pw.println(" --backgroundColorResource resource_name"); - pw.println(" Color resource name of letterbox background which is used when"); - pw.println(" background type is 'solid-color'. Use (set)get-letterbox-style to"); - pw.println(" check and control background type. Parameter is a color resource"); - pw.println(" name, for example, @android:color/system_accent2_50."); - pw.println(" --wallpaperBlurRadius radius"); - pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0"); - pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius"); - pw.println(" are ignored and 0 is used."); - pw.println(" --wallpaperDarkScrimAlpha alpha"); - pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'"); - pw.println(" letterbox background. If alpha < 0 or >= 1 both it and"); - pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored"); - pw.println(" and 0.0 (transparent) is used instead."); - pw.println(" --horizontalPositionMultiplier multiplier"); - pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,"); - pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier"); - pw.println(" are ignored and central position (0.5) is used."); - pw.println(" --isReachabilityEnabled [true|1|false|0]"); - pw.println(" Whether reachability repositioning is allowed for letterboxed"); - pw.println(" fullscreen apps in landscape device orientation."); - pw.println(" --defaultPositionForReachability [left|center|right]"); - pw.println(" Default horizontal position of app window when reachability is."); - pw.println(" enabled."); - pw.println(" --isEducationEnabled [true|1|false|0]"); - pw.println(" Whether education is allowed for letterboxed fullscreen apps."); - pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType"); - pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha"); - pw.println(" |horizontalPositionMultiplier|isReachabilityEnabled"); - pw.println(" isEducationEnabled||defaultPositionMultiplierForReachability]"); - pw.println(" Resets overrides to default values for specified properties separated"); - pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'."); - pw.println(" If no arguments provided, all values will be reset."); - pw.println(" get-letterbox-style"); - pw.println(" Prints letterbox style configuration."); - } - private void printMultiWindowConfigHelp(PrintWriter pw) { pw.println(" set-multi-window-config"); pw.println(" Sets options to determine if activity should be shown in multi window:"); 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 8461b39f8899..17b42260948d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -3220,34 +3220,19 @@ public class AlarmManagerServiceTest { 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. + // 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, diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java index fa3e05a6b001..20cfd59973c3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java @@ -24,20 +24,33 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import android.app.AppOpsManager; import android.content.Context; +import android.content.res.Resources; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.hardware.lights.Light; +import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; import android.hardware.lights.LightsRequest; +import android.os.Handler; +import android.os.Looper; import android.permission.PermissionManager; import android.util.ArraySet; import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.internal.R; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; @@ -53,26 +66,43 @@ import java.util.stream.Collectors; public class CameraPrivacyLightControllerTest { + private int mDayColor = 1; + private int mNightColor = 0; + private int mCameraPrivacyLightAlsAveragingIntervalMillis = 5000; + private int mCameraPrivacyLightAlsNightThreshold = (int) getLightSensorValue(15); + private MockitoSession mMockitoSession; @Mock private Context mContext; @Mock + private Resources mResources; + + @Mock private LightsManager mLightsManager; @Mock private AppOpsManager mAppOpsManager; @Mock + private SensorManager mSensorManager; + + @Mock private LightsManager.LightsSession mLightsSession; + @Mock + private Sensor mLightSensor; + private ArgumentCaptor<AppOpsManager.OnOpActiveChangedListener> mAppOpsListenerCaptor = ArgumentCaptor.forClass(AppOpsManager.OnOpActiveChangedListener.class); private ArgumentCaptor<LightsRequest> mLightsRequestCaptor = ArgumentCaptor.forClass(LightsRequest.class); + private ArgumentCaptor<SensorEventListener> mLightSensorListenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + private Set<String> mExemptedPackages = new ArraySet<>(); private List<Light> mLights = new ArrayList<>(); @@ -86,11 +116,22 @@ public class CameraPrivacyLightControllerTest { .spyStatic(PermissionManager.class) .startMocking(); + doReturn(mDayColor).when(mContext).getColor(R.color.camera_privacy_light_day); + doReturn(mNightColor).when(mContext).getColor(R.color.camera_privacy_light_night); + + doReturn(mResources).when(mContext).getResources(); + doReturn(mCameraPrivacyLightAlsAveragingIntervalMillis).when(mResources) + .getInteger(R.integer.config_cameraPrivacyLightAlsAveragingIntervalMillis); + doReturn(mCameraPrivacyLightAlsNightThreshold).when(mResources) + .getInteger(R.integer.config_cameraPrivacyLightAlsNightThreshold); + doReturn(mLightsManager).when(mContext).getSystemService(LightsManager.class); doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class); + doReturn(mSensorManager).when(mContext).getSystemService(SensorManager.class); doReturn(mLights).when(mLightsManager).getLights(); doReturn(mLightsSession).when(mLightsManager).openSession(anyInt()); + doReturn(mLightSensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT); doReturn(mExemptedPackages) .when(() -> PermissionManager.getIndicatorExemptedPackages(any())); @@ -107,7 +148,7 @@ public class CameraPrivacyLightControllerTest { @Test public void testAppsOpsListenerNotRegisteredWithoutCameraLights() { mLights.add(getNextLight(false)); - new CameraPrivacyLightController(mContext); + createCameraPrivacyLightController(); verify(mAppOpsManager, times(0)).startWatchingActive(any(), any(), any()); } @@ -116,7 +157,7 @@ public class CameraPrivacyLightControllerTest { public void testAppsOpsListenerRegisteredWithCameraLight() { mLights.add(getNextLight(true)); - new CameraPrivacyLightController(mContext); + createCameraPrivacyLightController(); verify(mAppOpsManager, times(1)).startWatchingActive(any(), any(), any()); } @@ -128,14 +169,13 @@ public class CameraPrivacyLightControllerTest { mLights.add(getNextLight(r.nextBoolean())); } - new CameraPrivacyLightController(mContext); + createCameraPrivacyLightController(); // Verify no session has been opened at this point. verify(mLightsManager, times(0)).openSession(anyInt()); // Set camera op as active. - verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); - mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true); + openCamera(); // Verify session has been opened exactly once verify(mLightsManager, times(1)).openSession(anyInt()); @@ -161,7 +201,7 @@ public class CameraPrivacyLightControllerTest { public void testWillOnlyOpenOnceWhenTwoPackagesStartOp() { mLights.add(getNextLight(true)); - new CameraPrivacyLightController(mContext); + createCameraPrivacyLightController(); verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); @@ -176,7 +216,7 @@ public class CameraPrivacyLightControllerTest { public void testWillCloseOnFinishOp() { mLights.add(getNextLight(true)); - new CameraPrivacyLightController(mContext); + createCameraPrivacyLightController(); verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); @@ -192,7 +232,7 @@ public class CameraPrivacyLightControllerTest { public void testWillCloseOnFinishOpForAllPackages() { mLights.add(getNextLight(true)); - new CameraPrivacyLightController(mContext); + createCameraPrivacyLightController(); int numUids = 100; List<Integer> uids = new ArrayList<>(numUids); @@ -226,7 +266,7 @@ public class CameraPrivacyLightControllerTest { mLights.add(getNextLight(true)); mExemptedPackages.add("pkg1"); - new CameraPrivacyLightController(mContext); + createCameraPrivacyLightController(); verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); @@ -235,6 +275,147 @@ public class CameraPrivacyLightControllerTest { verify(mLightsManager, times(0)).openSession(anyInt()); } + @Test + public void testNoLightSensor() { + mLights.add(getNextLight(true)); + doReturn(null).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT); + + createCameraPrivacyLightController(); + + openCamera(); + + verify(mLightsSession).requestLights(mLightsRequestCaptor.capture()); + LightsRequest lightsRequest = mLightsRequestCaptor.getValue(); + for (LightState lightState : lightsRequest.getLightStates()) { + assertEquals(mDayColor, lightState.getColor()); + } + } + + @Test + public void testALSListenerNotRegisteredUntilCameraIsOpened() { + mLights.add(getNextLight(true)); + Sensor sensor = mock(Sensor.class); + doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT); + + CameraPrivacyLightController cplc = createCameraPrivacyLightController(); + + verify(mSensorManager, never()).registerListener(any(SensorEventListener.class), + any(Sensor.class), anyInt(), any(Handler.class)); + + openCamera(); + + verify(mSensorManager, times(1)).registerListener(mLightSensorListenerCaptor.capture(), + any(Sensor.class), anyInt(), any(Handler.class)); + + mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10001, "pkg", false); + verify(mSensorManager, times(1)).unregisterListener(mLightSensorListenerCaptor.getValue()); + } + + @Ignore + @Test + public void testDayColor() { + testBrightnessToColor(20, mDayColor); + } + + @Ignore + @Test + public void testNightColor() { + testBrightnessToColor(10, mNightColor); + } + + private void testBrightnessToColor(int brightnessValue, int color) { + mLights.add(getNextLight(true)); + Sensor sensor = mock(Sensor.class); + doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT); + + CameraPrivacyLightController cplc = createCameraPrivacyLightController(); + cplc.setElapsedRealTime(0); + + openCamera(); + + verify(mSensorManager).registerListener(mLightSensorListenerCaptor.capture(), + any(Sensor.class), anyInt(), any(Handler.class)); + SensorEventListener sensorListener = mLightSensorListenerCaptor.getValue(); + float[] sensorEventValues = new float[1]; + SensorEvent sensorEvent = new SensorEvent(sensor, 0, 0, sensorEventValues); + + sensorEventValues[0] = getLightSensorValue(brightnessValue); + sensorListener.onSensorChanged(sensorEvent); + + verify(mLightsSession, atLeastOnce()).requestLights(mLightsRequestCaptor.capture()); + for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) { + assertEquals(color, lightState.getColor()); + } + } + + @Ignore + @Test + public void testDayToNightTransistion() { + mLights.add(getNextLight(true)); + Sensor sensor = mock(Sensor.class); + doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT); + + CameraPrivacyLightController cplc = createCameraPrivacyLightController(); + cplc.setElapsedRealTime(0); + + openCamera(); + // There will be an initial call at brightness 0 + verify(mLightsSession, times(1)).requestLights(any(LightsRequest.class)); + + verify(mSensorManager).registerListener(mLightSensorListenerCaptor.capture(), + any(Sensor.class), anyInt(), any(Handler.class)); + SensorEventListener sensorListener = mLightSensorListenerCaptor.getValue(); + + onSensorEvent(cplc, sensorListener, sensor, 0, 20); + + // 5 sec avg = 20 + onSensorEvent(cplc, sensorListener, sensor, 5000, 30); + + verify(mLightsSession, times(2)).requestLights(mLightsRequestCaptor.capture()); + for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) { + assertEquals(mDayColor, lightState.getColor()); + } + + // 5 sec avg = 22 + + onSensorEvent(cplc, sensorListener, sensor, 6000, 10); + + // 5 sec avg = 18 + + onSensorEvent(cplc, sensorListener, sensor, 8000, 5); + + // Should have always been day + verify(mLightsSession, times(2)).requestLights(mLightsRequestCaptor.capture()); + for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) { + assertEquals(mDayColor, lightState.getColor()); + } + + // 5 sec avg = 12 + + onSensorEvent(cplc, sensorListener, sensor, 10000, 5); + + // Should now be night + verify(mLightsSession, times(3)).requestLights(mLightsRequestCaptor.capture()); + for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) { + assertEquals(mNightColor, lightState.getColor()); + } + } + + private void onSensorEvent(CameraPrivacyLightController cplc, + SensorEventListener sensorListener, Sensor sensor, long timestamp, int value) { + cplc.setElapsedRealTime(timestamp); + sensorListener.onSensorChanged(new SensorEvent(sensor, 0, timestamp, + new float[] {getLightSensorValue(value)})); + } + + // Use the test thread so that the test is deterministic + private CameraPrivacyLightController createCameraPrivacyLightController() { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + return new CameraPrivacyLightController(mContext, Looper.myLooper()); + } + private Light getNextLight(boolean cameraType) { Light light = ExtendedMockito.mock(Light.class); if (cameraType) { @@ -245,4 +426,13 @@ public class CameraPrivacyLightControllerTest { doReturn(mNextLightId++).when(light).getId(); return light; } + + private float getLightSensorValue(int i) { + return (float) Math.exp(i / CameraPrivacyLightController.LIGHT_VALUE_MULTIPLIER); + } + + private void openCamera() { + verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); + mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10001, "pkg", true); + } } diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java index 18e0f29d4166..bce99a09c6d2 100644 --- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java +++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java @@ -416,6 +416,7 @@ public class BroadcastRecordTest { null /* resolvedType */, null /* requiredPermissions */, null /* excludedPermissions */, + null /* excludedPackages */, 0 /* appOp */, null /* options */, new ArrayList<>(receivers), // Make a copy to not affect the original list. diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java index 5b3a1284069e..98f0603ca633 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -339,7 +339,7 @@ public final class AppHibernationServiceTest { ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mIActivityManager, times(2)).broadcastIntentWithFeature(any(), any(), intentArgumentCaptor.capture(), any(), any(), anyInt(), any(), any(), any(), any(), - anyInt(), any(), anyBoolean(), anyBoolean(), eq(USER_ID_1)); + any(), anyInt(), any(), anyBoolean(), anyBoolean(), eq(USER_ID_1)); List<Intent> capturedIntents = intentArgumentCaptor.getAllValues(); assertEquals(capturedIntents.get(0).getAction(), Intent.ACTION_LOCKED_BOOT_COMPLETED); assertEquals(capturedIntents.get(1).getAction(), Intent.ACTION_BOOT_COMPLETED); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java index 3a352cbe1900..9b1d9c4eed22 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java @@ -26,8 +26,6 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.google.common.truth.Truth.assertThat; -import static junit.framework.Assert.fail; - import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -54,7 +52,6 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; import org.junit.Before; import org.junit.Test; @@ -62,14 +59,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; -import java.lang.reflect.Type; -import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; @SmallTest @@ -88,7 +78,7 @@ public class PermissionHelperTest extends UiServiceTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, false); + mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager); PackageInfo testPkgInfo = new PackageInfo(); testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.POST_NOTIFICATIONS }; when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt())) @@ -212,61 +202,8 @@ public class PermissionHelperTest extends UiServiceTestCase { } @Test - public void testSetNotificationPermission_pkgPerm_grantReviewRequired() throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) - .thenReturn(PERMISSION_DENIED); - - PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission( - "pkg", 10, true, false); - mPermissionHelper.setNotificationPermission(pkgPerm); - - verify(mPermManager, never()).revokeRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, 10, "PermissionHelper"); - verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - FLAG_PERMISSION_REVIEW_REQUIRED, FLAG_PERMISSION_REVIEW_REQUIRED, true, 10); - } - - @Test - public void testSetNotificationPermission_pkgPerm_notUserSet_grantedByDefaultPermNotSet() - throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) - .thenReturn(PERMISSION_DENIED); - when(mPermManager.getPermissionFlags(anyString(), - eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt())).thenReturn(FLAG_PERMISSION_GRANTED_BY_DEFAULT); - PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission( - "pkg", 10, true, false); - - mPermissionHelper.setNotificationPermission(pkgPerm); - verify(mPermManager, never()).revokeRuntimePermission( - anyString(), anyString(), anyInt(), anyString()); - verify(mPermManager, never()).updatePermissionFlags( - anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt()); - } - - @Test - public void testSetNotificationPermission_pkgPerm_userSet_grantedByDefaultPermSet() - throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) - .thenReturn(PERMISSION_DENIED); - when(mPermManager.getPermissionFlags(anyString(), - eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt())).thenReturn(FLAG_PERMISSION_GRANTED_BY_DEFAULT); - PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission( - "pkg", 10, true, true); - - mPermissionHelper.setNotificationPermission(pkgPerm); - verify(mPermManager).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); - verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_REVIEW_REQUIRED, - FLAG_PERMISSION_USER_SET, true, 10); - } - - @Test public void testSetNotificationPermission_pkgPerm_grantedByDefaultPermSet_allUserSet() throws Exception { - mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true); when(mPmi.checkPermission(anyString(), anyString(), anyInt())) .thenReturn(PERMISSION_DENIED); when(mPermManager.getPermissionFlags(anyString(), diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index a5cec7e01e9a..8d50ceaf74e9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -85,6 +85,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Color; import android.media.AudioAttributes; @@ -121,6 +122,8 @@ import com.android.os.AtomsProto.PackageNotificationPreferences; import com.android.server.UiServiceTestCase; import com.android.server.notification.PermissionHelper.PackagePermission; +import com.google.common.collect.ImmutableList; + import org.json.JSONArray; import org.json.JSONObject; import org.junit.Before; @@ -3523,8 +3526,37 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testUpdateNotificationChannel_fixedPermission() { + List<UserInfo> users = ImmutableList.of(new UserInfo(UserHandle.USER_SYSTEM, "user0", 0)); when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); + PackageInfo pm = new PackageInfo(); + pm.packageName = PKG_O; + pm.applicationInfo = new ApplicationInfo(); + pm.applicationInfo.uid = UID_O; + List<PackageInfo> packages = ImmutableList.of(pm); + when(mPm.getInstalledPackagesAsUser(any(), anyInt())).thenReturn(packages); + mHelper.updateFixedImportance(users); + + assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O)); + + NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); + + NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE); + update.setAllowBubbles(false); + + mHelper.updateNotificationChannel(PKG_O, UID_O, update, true); + assertEquals(IMPORTANCE_HIGH, + mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance()); + assertEquals(false, + mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble()); + } + + @Test + public void testUpdateNotificationChannel_defaultApp() { + ArraySet<Pair<String, Integer>> toAdd = new ArraySet<>(); + toAdd.add(new Pair(PKG_O, UID_O)); + mHelper.updateDefaultApps(0, null, toAdd); NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); @@ -3595,6 +3627,58 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testUpdateFixedImportance_multiUser() { + NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); + NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); + NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT); + // different uids, same package + mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); + mHelper.createNotificationChannel(PKG_O, UID_O, b, false, false); + mHelper.createNotificationChannel(PKG_O, UserHandle.PER_USER_RANGE + 1, c, true, true); + + UserInfo user = new UserInfo(); + user.id = 0; + List<UserInfo> users = ImmutableList.of(user); + when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); + PackageInfo pm = new PackageInfo(); + pm.packageName = PKG_O; + pm.applicationInfo = new ApplicationInfo(); + pm.applicationInfo.uid = UID_O; + List<PackageInfo> packages = ImmutableList.of(pm); + when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + mHelper.updateFixedImportance(users); + + assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, b.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + assertFalse(mHelper.getNotificationChannel( + PKG_O, UserHandle.PER_USER_RANGE + 1, c.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + } + + @Test + public void testUpdateFixedImportance_channelDoesNotExistYet() { + UserInfo user = new UserInfo(); + user.id = 0; + List<UserInfo> users = ImmutableList.of(user); + when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); + PackageInfo pm = new PackageInfo(); + pm.packageName = PKG_O; + pm.applicationInfo = new ApplicationInfo(); + pm.applicationInfo.uid = UID_O; + List<PackageInfo> packages = ImmutableList.of(pm); + when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + mHelper.updateFixedImportance(users); + + NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); + + assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + } + + @Test public void testUpdateDefaultApps_add_multiUser() { NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); @@ -3759,6 +3843,62 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testUpdateFixedImportance_thenDefaultAppsRemoves() { + UserInfo user = new UserInfo(); + user.id = 0; + List<UserInfo> users = ImmutableList.of(user); + when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); + PackageInfo pm = new PackageInfo(); + pm.packageName = PKG_O; + pm.applicationInfo = new ApplicationInfo(); + pm.applicationInfo.uid = UID_O; + List<PackageInfo> packages = ImmutableList.of(pm); + when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + mHelper.updateFixedImportance(users); + + ArraySet<String> toRemove = new ArraySet<>(); + toRemove.add(PKG_O); + mHelper.updateDefaultApps(0, toRemove, null); + + assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O)); + + NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); + + // Still locked by permission if not role + assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + } + + @Test + public void testUpdateDefaultApps_thenNotFixedPermission() { + ArraySet<Pair<String, Integer>> toAdd = new ArraySet<>(); + toAdd.add(new Pair(PKG_O, UID_O)); + mHelper.updateDefaultApps(0, null, toAdd); + + UserInfo user = new UserInfo(); + user.id = 0; + List<UserInfo> users = ImmutableList.of(user); + when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(false); + PackageInfo pm = new PackageInfo(); + pm.packageName = PKG_O; + pm.applicationInfo = new ApplicationInfo(); + pm.applicationInfo.uid = UID_O; + List<PackageInfo> packages = ImmutableList.of(pm); + when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + mHelper.updateFixedImportance(users); + + assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O)); + + NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); + + // Still locked by role if not permission + assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + } + + @Test public void testChannelXml_backupDefaultApp() throws Exception { NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 36e7a66d52e2..a34896a419ed 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -2853,6 +2853,11 @@ public class ActivityRecordTests extends WindowTestsBase { assertTrue(activity2.isResizeable()); activity1.reparent(taskFragment1, POSITION_TOP); + // Adds an Activity which doesn't have shared starting data, and verify if it blocks + // starting window removal. + final ActivityRecord activity3 = new ActivityBuilder(mAtm).build(); + taskFragment2.addChild(activity3, POSITION_TOP); + verify(activity1.getSyncTransaction()).reparent(eq(startingWindow.mSurfaceControl), eq(task.mSurfaceControl)); assertEquals(activity1.mStartingData, startingWindow.mStartingData); diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index 9fc9489e3c2e..1d14dc31fa26 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -82,6 +82,7 @@ import android.util.Pair; import androidx.test.filters.SmallTest; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.telephony.CellBroadcastUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; @@ -297,6 +298,22 @@ public class LockTaskControllerTest { } @Test + public void testLockTaskViolation_wirelessEmergencyAlerts() { + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED); + mLockTaskController.startLockTaskMode(tr, false, TEST_UID); + + // GIVEN cellbroadcast task necessary for emergency warning alerts + Task cellbroadcastreceiver = getTask( + new Intent().setComponent( + CellBroadcastUtils.getDefaultCellBroadcastAlertDialogComponent(mContext)), + LOCK_TASK_AUTH_PINNABLE); + + // THEN the cellbroadcast task should all be allowed + assertFalse(mLockTaskController.isLockTaskModeViolation(cellbroadcastreceiver)); + } + + @Test public void testStopLockTaskMode() throws Exception { // GIVEN one task record with allowlisted auth that is in lock task mode Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED); diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java index 85d59a216f25..9dfb0cc289ee 100644 --- a/telephony/common/android/telephony/LocationAccessPolicy.java +++ b/telephony/common/android/telephony/LocationAccessPolicy.java @@ -361,7 +361,10 @@ public final class LocationAccessPolicy { return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, pid, uid); } - private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) { + /** + * @return Whether location is enabled for the given user. + */ + public static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) { LocationManager locationManager = context.getSystemService(LocationManager.class); if (locationManager == null) { Log.w(TAG, "Couldn't get location manager, denying location access"); @@ -370,6 +373,14 @@ public final class LocationAccessPolicy { return locationManager.isLocationEnabledForUser(UserHandle.of(userId)); } + /** + * @return An array of packages that are always allowed to access location. + */ + public static @NonNull String[] getLocationBypassPackages(@NonNull Context context) { + return context.getResources().getStringArray( + com.android.internal.R.array.config_serviceStateLocationAllowedPackages); + } + private static boolean checkInteractAcrossUsersFull( @NonNull Context context, int pid, int uid) { return checkManifestPermission(context, pid, uid, diff --git a/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java index 6c6375586225..6181329bcb2a 100644 --- a/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java +++ b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java @@ -16,6 +16,7 @@ package com.android.internal.telephony; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -62,4 +63,17 @@ public class CellBroadcastUtils { return packageName; } + + /** + * Utility method to get cellbroadcast alert dialog component name + */ + public static ComponentName getDefaultCellBroadcastAlertDialogComponent(Context context) { + String cellBroadcastReceiverPackageName = + getDefaultCellBroadcastReceiverPackageName(context); + if (TextUtils.isEmpty(cellBroadcastReceiverPackageName)) { + return null; + } + return ComponentName.createRelative(cellBroadcastReceiverPackageName, + "com.android.cellbroadcastreceiver.CellBroadcastAlertDialog"); + } } |