diff options
54 files changed, 1189 insertions, 385 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index cef065ddac9e..0e9efbcc98b8 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -1536,13 +1536,14 @@ public class DeviceIdleController extends SystemService @VisibleForTesting static final int MSG_REPORT_STATIONARY_STATUS = 7; private static final int MSG_FINISH_IDLE_OP = 8; - private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS = 9; private static final int MSG_SEND_CONSTRAINT_MONITORING = 10; @VisibleForTesting static final int MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11; @VisibleForTesting static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12; private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 13; + private static final int MSG_REPORT_TEMP_APP_WHITELIST_ADDED_TO_NPMS = 14; + private static final int MSG_REPORT_TEMP_APP_WHITELIST_REMOVED_TO_NPMS = 15; final class MyHandler extends Handler { MyHandler(Looper looper) { @@ -1659,10 +1660,17 @@ public class DeviceIdleController extends SystemService } } } break; - case MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS: { + case MSG_REPORT_TEMP_APP_WHITELIST_ADDED_TO_NPMS: { final int appId = msg.arg1; - final boolean added = (msg.arg2 == 1); - mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, added); + final int reasonCode = msg.arg2; + final String reason = (String) msg.obj; + mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, true, + reasonCode, reason); + } break; + case MSG_REPORT_TEMP_APP_WHITELIST_REMOVED_TO_NPMS: { + final int appId = msg.arg1; + mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, false, + REASON_UNKNOWN, /* reason= */ null); } break; case MSG_SEND_CONSTRAINT_MONITORING: { final IDeviceIdleConstraint constraint = (IDeviceIdleConstraint) msg.obj; @@ -2811,14 +2819,15 @@ public class DeviceIdleController extends SystemService // NPMS needs to update its state synchronously in certain situations so we // can't have it use the TempAllowlistChangeListener path right now. // TODO: see if there's a way to simplify/consolidate - mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS, appId, 1) - .sendToTarget(); + mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_ADDED_TO_NPMS, appId, + reasonCode, reason).sendToTarget(); } reportTempWhitelistChangedLocked(uid, true); } } if (informWhitelistChanged) { - mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, true); + mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, true, + reasonCode, reason); } } @@ -2890,8 +2899,8 @@ public class DeviceIdleController extends SystemService final int appId = UserHandle.getAppId(uid); updateTempWhitelistAppIdsLocked(uid, false, 0, 0, REASON_UNKNOWN, reason, INVALID_UID); - mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS, appId, 0) - .sendToTarget(); + mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_REMOVED_TO_NPMS, appId, + /* unused= */ 0).sendToTarget(); reportTempWhitelistChangedLocked(uid, false); try { mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_TEMP_WHITELIST_FINISH, 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 1169391d2cd2..fe0c7f718bb0 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -136,6 +136,8 @@ import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemServiceManager; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.usage.AppStandbyInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; @@ -215,9 +217,18 @@ public class AlarmManagerService extends SystemService { final Object mLock = new Object(); - /** Immutable set of app ids that have requested SCHEDULE_EXACT_ALARM permission.*/ + /** Immutable set of app ids requesting {@link Manifest.permission#SCHEDULE_EXACT_ALARM} */ @VisibleForTesting volatile Set<Integer> mExactAlarmCandidates = Collections.emptySet(); + + /** + * A map from uid to the last op-mode we have seen for + * {@link AppOpsManager#OP_SCHEDULE_EXACT_ALARM} + */ + @VisibleForTesting + @GuardedBy("mLock") + SparseIntArray mLastOpScheduleExactAlarm = new SparseIntArray(); + // List of alarms per uid deferred due to user applied background restrictions on the source app SparseArray<ArrayList<Alarm>> mPendingBackgroundAlarms = new SparseArray<>(); private long mNextWakeup; @@ -522,6 +533,9 @@ public class AlarmManagerService extends SystemService { static final String KEY_MIN_DEVICE_IDLE_FUZZ = "min_device_idle_fuzz"; @VisibleForTesting static final String KEY_MAX_DEVICE_IDLE_FUZZ = "max_device_idle_fuzz"; + @VisibleForTesting + static final String KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = + "kill_on_schedule_exact_alarm_revoked"; private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; @@ -564,6 +578,8 @@ public class AlarmManagerService extends SystemService { private static final long DEFAULT_MIN_DEVICE_IDLE_FUZZ = 2 * 60_000; private static final long DEFAULT_MAX_DEVICE_IDLE_FUZZ = 15 * 60_000; + private static final boolean DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = true; + // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; @@ -644,6 +660,13 @@ public class AlarmManagerService extends SystemService { */ public long MAX_DEVICE_IDLE_FUZZ = DEFAULT_MAX_DEVICE_IDLE_FUZZ; + /** + * Whether or not to kill app when the permission + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} is revoked. + */ + public boolean KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = + DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED; + private long mLastAllowWhileIdleWhitelistDuration = -1; private int mVersion = 0; @@ -816,6 +839,11 @@ public class AlarmManagerService extends SystemService { deviceIdleFuzzBoundariesUpdated = true; } break; + case KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED: + KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = properties.getBoolean( + KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED, + DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED); + break; default: if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { // The quotas need to be updated in order, so we can't just rely @@ -830,17 +858,24 @@ public class AlarmManagerService extends SystemService { } private void updateExactAlarmDenyList(String[] newDenyList) { + final Set<String> newSet = Collections.unmodifiableSet(new ArraySet<>(newDenyList)); + final Set<String> removed = new ArraySet<>(EXACT_ALARM_DENY_LIST); + final Set<String> added = new ArraySet<>(newDenyList); + + added.removeAll(EXACT_ALARM_DENY_LIST); + removed.removeAll(newSet); + if (added.size() > 0) { + mHandler.obtainMessage(AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_ADDED, added) + .sendToTarget(); + } + if (removed.size() > 0) { + mHandler.obtainMessage(AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED, removed) + .sendToTarget(); + } if (newDenyList.length == 0) { EXACT_ALARM_DENY_LIST = Collections.emptySet(); } else { - final Set<String> oldSet = EXACT_ALARM_DENY_LIST; - final Set<String> newlyAdded = new ArraySet<>(newDenyList); - EXACT_ALARM_DENY_LIST = Collections.unmodifiableSet(new ArraySet<>(newlyAdded)); - newlyAdded.removeAll(oldSet); - if (newlyAdded.size() > 0) { - mHandler.obtainMessage(AlarmHandler.EXACT_ALARM_DENY_LIST_CHANGED, newlyAdded) - .sendToTarget(); - } + EXACT_ALARM_DENY_LIST = newSet; } } @@ -1007,6 +1042,20 @@ public class AlarmManagerService extends SystemService { pw.print(KEY_EXACT_ALARM_DENY_LIST, EXACT_ALARM_DENY_LIST); pw.println(); + pw.print(KEY_MIN_DEVICE_IDLE_FUZZ); + pw.print("="); + TimeUtils.formatDuration(MIN_DEVICE_IDLE_FUZZ, pw); + pw.println(); + + pw.print(KEY_MAX_DEVICE_IDLE_FUZZ); + pw.print("="); + TimeUtils.formatDuration(MAX_DEVICE_IDLE_FUZZ, pw); + pw.println(); + + pw.print(KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED, + KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED); + pw.println(); + pw.decreaseIndent(); } @@ -1667,16 +1716,57 @@ public class AlarmManagerService extends SystemService { void refreshExactAlarmCandidates() { final String[] candidates = mLocalPermissionManager.getAppOpPermissionPackages( Manifest.permission.SCHEDULE_EXACT_ALARM); - final Set<Integer> appIds = new ArraySet<>(candidates.length); + final Set<Integer> newAppIds = new ArraySet<>(candidates.length); for (final String candidate : candidates) { final int uid = mPackageManagerInternal.getPackageUid(candidate, PackageManager.MATCH_ANY_USER, USER_SYSTEM); if (uid > 0) { - appIds.add(UserHandle.getAppId(uid)); + newAppIds.add(UserHandle.getAppId(uid)); + } + } + final ArraySet<Integer> removed = new ArraySet<>(mExactAlarmCandidates); + removed.removeAll(newAppIds); + // This code is only called on package_added and boot. The set {removed} is only expected to + // be non-empty when a package was updated and it removed the permission from its manifest. + for (int i = 0; i < removed.size(); i++) { + final int removedAppId = removed.valueAt(i); + synchronized (mLock) { + Slog.i(TAG, "App id " + removedAppId + " lost SCHEDULE_EXACT_ALARM on update"); + + final Predicate<Alarm> whichAlarms = a -> { + if (UserHandle.getAppId(a.uid) != removedAppId || a.windowLength != 0) { + return false; + } + if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) { + return false; + } + return a.alarmClock != null || !isExemptFromExactAlarmPermission(a.uid); + }; + removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); } } // No need to lock. Assignment is always atomic. - mExactAlarmCandidates = Collections.unmodifiableSet(appIds); + mExactAlarmCandidates = Collections.unmodifiableSet(newAppIds); + } + + @Override + public void onUserStarting(TargetUser user) { + super.onUserStarting(user); + final int userId = user.getUserIdentifier(); + mHandler.post(() -> { + for (final int appId : mExactAlarmCandidates) { + final int uid = UserHandle.getUid(userId, appId); + final AndroidPackage androidPackage = mPackageManagerInternal.getPackage(uid); + // It will be null if it is not installed on the starting user. + if (androidPackage != null) { + final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, + uid, androidPackage.getPackageName()); + synchronized (mLock) { + mLastOpScheduleExactAlarm.put(uid, mode); + } + } + } + }); } @Override @@ -1706,17 +1796,44 @@ public class AlarmManagerService extends SystemService { @Override public void opChanged(int op, int uid, String packageName) throws RemoteException { - if (op != AppOpsManager.OP_SCHEDULE_EXACT_ALARM) { + final int userId = UserHandle.getUserId(uid); + if (op != AppOpsManager.OP_SCHEDULE_EXACT_ALARM + || !isExactAlarmChangeEnabled(packageName, userId)) { return; } - if (!hasScheduleExactAlarmInternal(packageName, uid)) { + + final boolean requested = mExactAlarmCandidates.contains( + UserHandle.getAppId(uid)); + final boolean denyListed = + mConstants.EXACT_ALARM_DENY_LIST.contains(packageName); + + final int newMode = mAppOps.checkOpNoThrow( + AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, packageName); + + final int oldMode; + synchronized (mLock) { + final int index = mLastOpScheduleExactAlarm.indexOfKey(uid); + if (index < 0) { + oldMode = AppOpsManager.opToDefaultMode( + AppOpsManager.OP_SCHEDULE_EXACT_ALARM); + mLastOpScheduleExactAlarm.put(uid, newMode); + } else { + oldMode = mLastOpScheduleExactAlarm.valueAt(index); + mLastOpScheduleExactAlarm.setValueAt(index, newMode); + } + } + + final boolean hadPermission = getScheduleExactAlarmState(requested, + denyListed, oldMode); + final boolean hasPermission = getScheduleExactAlarmState(requested, + denyListed, newMode); + + if (hadPermission && !hasPermission) { mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS, uid, 0, packageName).sendToTarget(); - } else { - // TODO(b/187206399) Make sure this won't be sent, if the app - // already had the appop previously. + } else if (!hadPermission && hasPermission) { sendScheduleExactAlarmPermissionStateChangedBroadcast( - packageName, UserHandle.getUserId(uid)); + packageName, userId); } } }); @@ -2256,12 +2373,28 @@ public class AlarmManagerService extends SystemService { } } + private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed, + int appOpMode) { + if (!requested) { + return false; + } + if (appOpMode == AppOpsManager.MODE_DEFAULT) { + return !denyListed; + } + return appOpMode == AppOpsManager.MODE_ALLOWED; + } + boolean hasScheduleExactAlarmInternal(String packageName, int uid) { + // Not using getScheduleExactAlarmState as this can avoid some calls to AppOpsService. + // Not using #mLastOpScheduleExactAlarm as it may contain stale values. + // No locking needed as all internal containers being queried are immutable. + final long start = mStatLogger.getTime(); final boolean hasPermission; - // No locking needed as all internal containers being queried are immutable. if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) { hasPermission = false; + } else if (!isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { + hasPermission = false; } else { final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, packageName); @@ -2368,8 +2501,7 @@ public class AlarmManagerService extends SystemService { } else if (exact || allowWhileIdle) { final boolean needsPermission; boolean lowerQuota; - if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, - callingPackage, UserHandle.of(callingUserId))) { + if (isExactAlarmChangeEnabled(callingPackage, callingUserId)) { needsPermission = exact; lowerQuota = !exact; idleOptions = exact ? mOptsWithFgs.toBundle() : mOptsWithoutFgs.toBundle(); @@ -2524,6 +2656,11 @@ public class AlarmManagerService extends SystemService { } }; + private static boolean isExactAlarmChangeEnabled(String packageName, int userId) { + return CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, + packageName, UserHandle.of(userId)); + } + void dumpImpl(IndentingPrintWriter pw) { synchronized (mLock) { pw.println("Current Alarm Manager state:"); @@ -2673,6 +2810,17 @@ public class AlarmManagerService extends SystemService { pw.println("App ids requesting SCHEDULE_EXACT_ALARM: " + mExactAlarmCandidates); pw.println(); + pw.print("Last OP_SCHEDULE_EXACT_ALARM: ["); + for (int i = 0; i < mLastOpScheduleExactAlarm.size(); i++) { + if (i > 0) { + pw.print(", "); + } + UserHandle.formatUid(pw, mLastOpScheduleExactAlarm.keyAt(i)); + pw.print(":" + AppOpsManager.modeToName(mLastOpScheduleExactAlarm.valueAt(i))); + } + pw.println("]"); + + pw.println(); pw.println("Next alarm clock information: "); pw.increaseIndent(); final TreeSet<Integer> users = new TreeSet<>(); @@ -3362,30 +3510,58 @@ public class AlarmManagerService extends SystemService { } /** - * Called when some packages are added to the {@link Constants#EXACT_ALARM_DENY_LIST}, as this - * may cause some of them to lose their permission. + * Called when the {@link Constants#EXACT_ALARM_DENY_LIST}, changes with the packages that + * either got added or deleted. + * These packages may lose or gain the SCHEDULE_EXACT_ALARM permission. * - * Note that these packages don't need to be installed on the device, but if they do have an - * exact alarm scheduled and they lose the permission, this alarm will be canceled. + * Note that these packages don't need to be installed on the device, but if they are and they + * do undergo a permission change, we will handle them appropriately. * + * This should not be called with the lock held as it calls out to other services. * This is not expected to get called frequently. */ - void handlePackagesAddedToExactAlarmsDenyListLocked(ArraySet<String> packageNames) { - Slog.w(TAG, "Packages " + packageNames + " added to the exact alarm deny list."); - final Predicate<Alarm> whichAlarms = a -> { - if (!packageNames.contains(a.packageName) || a.windowLength != 0) { - return false; - } - if (!CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, - a.packageName, UserHandle.getUserHandleForUid(a.uid))) { - return false; - } - if (a.alarmClock == null && isExemptFromExactAlarmPermission(a.uid)) { - return false; + void handleChangesToExactAlarmDenyList(ArraySet<String> changedPackages, boolean added) { + Slog.w(TAG, "Packages " + changedPackages + (added ? " added to" : " removed from") + + " the exact alarm deny list."); + + final int[] startedUserIds = mActivityManagerInternal.getStartedUserIds(); + + for (int i = 0; i < changedPackages.size(); i++) { + final String changedPackage = changedPackages.valueAt(i); + for (final int userId : startedUserIds) { + final int uid = mPackageManagerInternal.getPackageUid(changedPackage, 0, userId); + if (uid <= 0) { + continue; + } + if (!isExactAlarmChangeEnabled(changedPackage, userId)) { + continue; + } + final int appOpMode; + synchronized (mLock) { + appOpMode = mLastOpScheduleExactAlarm.get(uid, + AppOpsManager.opToDefaultMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM)); + } + final boolean requested = mExactAlarmCandidates.contains(UserHandle.getAppId(uid)); + + // added: true => package was added to the deny list + // added: false => package was removed from the deny list + final boolean hadPermission = getScheduleExactAlarmState(requested, !added, + appOpMode); + final boolean hasPermission = getScheduleExactAlarmState(requested, added, + appOpMode); + + if (hadPermission == hasPermission) { + continue; + } + if (added) { + synchronized (mLock) { + removeExactAlarmsOnPermissionRevokedLocked(uid, changedPackage); + } + } else { + sendScheduleExactAlarmPermissionStateChangedBroadcast(changedPackage, userId); + } } - return !hasScheduleExactAlarmInternal(a.packageName, a.uid); - }; - removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); + } } /** @@ -3396,9 +3572,7 @@ public class AlarmManagerService extends SystemService { */ void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) { Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!"); - if (!CompatChanges.isChangeEnabled( - AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, - packageName, UserHandle.getUserHandleForUid(uid))) { + if (!isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { return; } @@ -3409,6 +3583,11 @@ public class AlarmManagerService extends SystemService { return false; }; removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); + + if (mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) { + PermissionManagerService.killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid), + "schedule_exact_alarm revoked"); + } } private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms, int reason) { @@ -3535,6 +3714,11 @@ public class AlarmManagerService extends SystemService { mRemovalHistory.removeAt(i); } } + for (int i = mLastOpScheduleExactAlarm.size() - 1; i >= 0; i--) { + if (UserHandle.getUserId(mLastOpScheduleExactAlarm.keyAt(i)) == userHandle) { + mLastOpScheduleExactAlarm.removeAt(i); + } + } } void interactiveStateChangedLocked(boolean interactive) { @@ -4091,8 +4275,9 @@ public class AlarmManagerService extends SystemService { public static final int CHARGING_STATUS_CHANGED = 6; public static final int REMOVE_FOR_CANCELED = 7; public static final int REMOVE_EXACT_ALARMS = 8; - public static final int EXACT_ALARM_DENY_LIST_CHANGED = 9; - public static final int REFRESH_EXACT_ALARM_CANDIDATES = 10; + public static final int EXACT_ALARM_DENY_LIST_PACKAGES_ADDED = 9; + public static final int EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED = 10; + public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11; AlarmHandler() { super(Looper.myLooper()); @@ -4179,10 +4364,11 @@ public class AlarmManagerService extends SystemService { removeExactAlarmsOnPermissionRevokedLocked(uid, packageName); } break; - case EXACT_ALARM_DENY_LIST_CHANGED: - synchronized (mLock) { - handlePackagesAddedToExactAlarmsDenyListLocked((ArraySet<String>) msg.obj); - } + case EXACT_ALARM_DENY_LIST_PACKAGES_ADDED: + handleChangesToExactAlarmDenyList((ArraySet<String>) msg.obj, true); + break; + case EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED: + handleChangesToExactAlarmDenyList((ArraySet<String>) msg.obj, false); break; case REFRESH_EXACT_ALARM_CANDIDATES: refreshExactAlarmCandidates(); @@ -4349,6 +4535,7 @@ public class AlarmManagerService extends SystemService { case Intent.ACTION_UID_REMOVED: mLastPriorityAlarmDispatch.delete(uid); mRemovalHistory.delete(uid); + mLastOpScheduleExactAlarm.delete(uid); return; case Intent.ACTION_PACKAGE_REMOVED: if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 620323976067..5fa75dd68ce0 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -77,7 +77,7 @@ package android.accessibilityservice { } public abstract class AccessibilityService extends android.app.Service { - field public static final int ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS = 1000; // 0x3e8 + field public static final int ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS = 333; // 0x14d } public class AccessibilityServiceInfo implements android.os.Parcelable { diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 483defab43c9..e91209c1a273 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -685,7 +685,7 @@ public abstract class AccessibilityService extends Service { * @hide */ @TestApi - public static final int ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS = 1000; + public static final int ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS = 333; /** @hide */ public static final String KEY_ACCESSIBILITY_SCREENSHOT_STATUS = diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 7cb8bc0d80fd..317e51c27d90 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -254,6 +254,9 @@ public abstract class ActivityManagerInternal { /** Returns the current user id. */ public abstract int getCurrentUserId(); + /** Returns the currently started user ids. */ + public abstract int[] getStartedUserIds(); + /** Returns true if the user is running. */ public abstract boolean isUserRunning(@UserIdInt int userId, int flags); diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 62b5ec872320..9ed76c1c13d1 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -932,6 +932,21 @@ public final class LoadedApk { boolean registerAppInfoToArt = false; if (mDefaultClassLoader == null) { + // Setup the dex reporter to notify package manager + // of any relevant dex loads. The idle maintenance job will use the information + // reported to optimize the loaded dex files. + // Note that we only need one global reporter per app. + // Make sure we do this before creating the main app classloader for the first time + // so that we can capture the complete application startup. + // + // We should not do this in a zygote context (where mActivityThread will be null), + // thus we'll guard against it. + // Also, the system server reporter (SystemServerDexLoadReporter) is already registered + // when system server starts, so we don't need to do it here again. + if (mActivityThread != null && !ActivityThread.isSystem()) { + BaseDexClassLoader.setReporter(DexLoadReporter.getInstance()); + } + // Temporarily disable logging of disk reads on the Looper thread // as this is early and necessary. StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads(); @@ -1047,14 +1062,6 @@ public final class LoadedApk { } private void registerAppInfoToArt() { - // Setup the dex reporter to notify package manager - // of any relevant dex loads. The idle maintenance job will use the information - // reported to optimize the loaded dex files. - // Note that we only need one global reporter per app. - // Make sure we do this before invoking app code for the first time so that we - // can capture the complete application startup. - BaseDexClassLoader.setReporter(DexLoadReporter.getInstance()); - // Only set up profile support if the loaded apk has the same uid as the // current process. // Currently, we do not support profiling across different apps. diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index 8e3de616f07b..41272437c407 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -693,20 +693,42 @@ public final class CameraExtensionCharacteristics { throw new IllegalArgumentException("Unsupported extension"); } + android.hardware.camera2.extension.Size sz = + new android.hardware.camera2.extension.Size(); + sz.width = captureOutputSize.getWidth(); + sz.height = captureOutputSize.getHeight(); if (areAdvancedExtensionsSupported()) { IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); extender.init(mCameraId); - android.hardware.camera2.extension.Size sz = - new android.hardware.camera2.extension.Size(); - sz.width = captureOutputSize.getWidth(); - sz.height = captureOutputSize.getHeight(); LatencyRange latencyRange = extender.getEstimatedCaptureLatencyRange(mCameraId, sz, format); if (latencyRange != null) { return new Range(latencyRange.min, latencyRange.max); } - } - } catch (RemoteException e) { + } else { + Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = + initializeExtension(extension); + extenders.second.init(mCameraId, mChars.getNativeMetadata()); + if ((format == ImageFormat.YUV_420_888) && + (extenders.second.getCaptureProcessor() == null) ){ + // Extensions that don't implement any capture processor are limited to + // JPEG only! + return null; + } + if ((format == ImageFormat.JPEG) && + (extenders.second.getCaptureProcessor() != null)) { + // The framework will perform the additional encoding pass on the + // processed YUV_420 buffers. Latency in this case is very device + // specific and cannot be estimated accurately enough. + return null; + } + + LatencyRange latencyRange = extenders.second.getEstimatedCaptureLatencyRange(sz); + if (latencyRange != null) { + return new Range(latencyRange.min, latencyRange.max); + } + } + } catch (RemoteException e) { Log.e(TAG, "Failed to query the extension capture latency! Extension service does" + " not respond!"); } finally { diff --git a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl index c04e75ea6758..3ebf63793b79 100644 --- a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl +++ b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl @@ -19,6 +19,8 @@ import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.extension.CaptureStageImpl; import android.hardware.camera2.extension.ICaptureProcessorImpl; +import android.hardware.camera2.extension.LatencyRange; +import android.hardware.camera2.extension.Size; import android.hardware.camera2.extension.SizeList; /** @hide */ @@ -36,4 +38,5 @@ interface IImageCaptureExtenderImpl @nullable List<CaptureStageImpl> getCaptureStages(); int getMaxCaptureStage(); @nullable List<SizeList> getSupportedResolutions(); + LatencyRange getEstimatedCaptureLatencyRange(in Size outputSize); } diff --git a/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl index f365469eaecc..4c85966c10dc 100644 --- a/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl +++ b/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl @@ -21,5 +21,6 @@ import android.hardware.camera2.extension.ParcelImage; /** @hide */ interface IImageProcessorImpl { - void onNextImageAvailable(in OutputConfigId outputConfigId, in ParcelImage image); + void onNextImageAvailable(in OutputConfigId outputConfigId, in ParcelImage image, + in String physicalCameraId); } diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index abc487db5bdb..5cf50a25de78 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -79,8 +79,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes private final HandlerThread mHandlerThread; private final CameraExtensionSession.StateCallback mCallbacks; private final IAdvancedExtenderImpl mAdvancedExtender; - // maps camera outputs to extension output ids - private final HashMap<Surface, Integer> mSurfaceIdMap = new HashMap<>(); + // maps registered camera surfaces to extension output configs + private final HashMap<Surface, CameraOutputConfig> mCameraConfigMap = new HashMap<>(); // maps camera extension output ids to camera registered image readers private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>(); private final RequestProcessor mRequestProcessor = new RequestProcessor(); @@ -226,7 +226,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes reader.getSurface()); break; case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER: - // TBD + // Support for multi-resolution outputs to be added in future releases default: throw new IllegalArgumentException("Unsupported output config type: " + output.type); @@ -251,7 +251,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes } } outputList.add(outConfig); - mSurfaceIdMap.put(outConfig.getSurface(), output.outputId.id); + mCameraConfigMap.put(outConfig.getSurface(), output); } SessionConfiguration sessionConfiguration = new SessionConfiguration( @@ -629,7 +629,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes if (request.getTag() instanceof Integer) { Integer requestId = (Integer) request.getTag(); mCallback.onCaptureBufferLost(requestId, frameNumber, - mSurfaceIdMap.get(target)); + mCameraConfigMap.get(target).outputId.id); } else { Log.e(TAG, "Invalid capture request tag!"); } @@ -736,12 +736,14 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes private static final class ImageReaderHandler implements ImageReader.OnImageAvailableListener { private final OutputConfigId mOutputConfigId; private final IImageProcessorImpl mIImageProcessor; + private final String mPhysicalCameraId; private ImageReaderHandler(int outputConfigId, - IImageProcessorImpl iImageProcessor) { + IImageProcessorImpl iImageProcessor, String physicalCameraId) { mOutputConfigId = new OutputConfigId(); mOutputConfigId.id = outputConfigId; mIImageProcessor = iImageProcessor; + mPhysicalCameraId = physicalCameraId; } @Override @@ -787,7 +789,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes parcelImage.crop = img.getCropRect(); try { - mIImageProcessor.onNextImageAvailable(mOutputConfigId, parcelImage); + mIImageProcessor.onNextImageAvailable(mOutputConfigId, parcelImage, + mPhysicalCameraId); } catch (RemoteException e) { Log.e(TAG, "Failed to propagate image buffer on output surface id: " + mOutputConfigId + " extension service does not respond!"); @@ -804,8 +807,17 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes IImageProcessorImpl imageProcessor) { synchronized (mInterfaceLock) { if (mReaderMap.containsKey(outputConfigId.id)) { - mReaderMap.get(outputConfigId.id).setOnImageAvailableListener( - new ImageReaderHandler(outputConfigId.id, imageProcessor), mHandler); + ImageReader reader = mReaderMap.get(outputConfigId.id); + String physicalCameraId = null; + if (mCameraConfigMap.containsKey(reader.getSurface())) { + physicalCameraId = + mCameraConfigMap.get(reader.getSurface()).physicalCameraId; + reader.setOnImageAvailableListener(new ImageReaderHandler(outputConfigId.id, + imageProcessor, physicalCameraId), mHandler); + } else { + Log.e(TAG, "Camera output configuration for ImageReader with " + + " config Id " + outputConfigId.id + " not found!"); + } } else { Log.e(TAG, "ImageReader with output config id: " + outputConfigId.id + " not found!"); @@ -828,7 +840,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes ArrayList<CaptureRequest> captureRequests = new ArrayList<>(); for (Request request : requests) { captureRequests.add(initializeCaptureRequest(mCameraDevice, request, - mSurfaceIdMap)); + mCameraConfigMap)); } mCaptureSession.captureBurstRequests(captureRequests, new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback); @@ -848,7 +860,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes synchronized (mInterfaceLock) { try { CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice, - request, mSurfaceIdMap); + request, mCameraConfigMap); CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback); mCaptureSession.setSingleRepeatingRequest(repeatingRequest, new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback); @@ -891,12 +903,13 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes } private static CaptureRequest initializeCaptureRequest(CameraDevice cameraDevice, - Request request, HashMap<Surface, Integer> surfaceIdMap) throws CameraAccessException { + Request request, HashMap<Surface, CameraOutputConfig> surfaceIdMap) + throws CameraAccessException { CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(request.templateId); for (OutputConfigId configId : request.targetOutputConfigIds) { boolean found = false; - for (Map.Entry<Surface, Integer> entry : surfaceIdMap.entrySet()) { - if (entry.getValue() == configId.id) { + for (Map.Entry<Surface, CameraOutputConfig> entry : surfaceIdMap.entrySet()) { + if (entry.getValue().outputId.id == configId.id) { builder.addTarget(entry.getKey()); found = true; break; diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index 5339f41ecdd6..4bcc4942d8a0 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -18,7 +18,6 @@ package android.hardware.camera2.impl; import android.content.Context; import android.graphics.ImageFormat; -import android.graphics.PixelFormat; import android.graphics.SurfaceTexture; import android.hardware.HardwareBuffer; import android.hardware.camera2.CameraAccessException; @@ -42,7 +41,6 @@ import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.ExtensionSessionConfiguration; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.SessionConfiguration; -import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.SurfaceUtils; import android.media.Image; import android.media.ImageReader; @@ -68,7 +66,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; -import java.util.concurrent.RejectedExecutionException; public final class CameraExtensionSessionImpl extends CameraExtensionSession { private static final int PREVIEW_QUEUE_SIZE = 3; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 86380a294a3d..d42e0c367763 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1410,8 +1410,6 @@ public final class ViewRootImpl implements ViewParent, final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets; mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent, attrs.getTitle().toString()); - addASurfaceTransactionCallback(); - mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl); updateColorModeIfNeeded(attrs.getColorMode()); updateForceDarkMode(); if (mAttachInfo.mThreadedRenderer != null) { @@ -1420,6 +1418,8 @@ public final class ViewRootImpl implements ViewParent, if (mHardwareRendererObserver != null) { mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); } + addASurfaceTransactionCallback(); + mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl); } } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d7010298adba..6533fc51accf 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5658,6 +5658,8 @@ <!-- Allows query of any normal app on the device, regardless of manifest declarations. <p>Protection level: normal --> <permission android:name="android.permission.QUERY_ALL_PACKAGES" + android:label="@string/permlab_queryAllPackages" + android:description="@string/permdesc_queryAllPackages" android:protectionLevel="normal" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 766ad167e5b3..bdeff89352ea 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3895,6 +3895,11 @@ <!-- Description of an application permission that lets it ask user to ignore battery optimizations for that app--> <string name="permdesc_requestIgnoreBatteryOptimizations">Allows an app to ask for permission to ignore battery optimizations for that app.</string> + <!-- Title of an application permission that lets query all other packages. [CHAR LIMIT=NONE] --> + <string name="permlab_queryAllPackages">query all packages</string> + <!-- Description of an application permission that lets it query all other packages. [CHAR LIMIT=NONE] --> + <string name="permdesc_queryAllPackages">Allows an app to see all installed packages.</string> + <!-- Shown in the tutorial for tap twice for zoom control. --> <string name="tutorial_double_tap_to_zoom_message_short">Tap twice for zoom control</string> diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 0d8715bfaef2..fe80b5845bf5 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -331,18 +331,18 @@ public class RippleDrawable extends LayerDrawable { private void setRippleActive(boolean active) { if (mRippleActive != active) { mRippleActive = active; - } - if (mState.mRippleStyle == STYLE_SOLID) { - if (active) { - tryRippleEnter(); - } else { - tryRippleExit(); - } - } else { - if (active) { - startPatternedAnimation(); + if (mState.mRippleStyle == STYLE_SOLID) { + if (active) { + tryRippleEnter(); + } else { + tryRippleExit(); + } } else { - exitPatternedAnimation(); + if (active) { + startPatternedAnimation(); + } else { + exitPatternedAnimation(); + } } } } diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp index ee7b26058952..d86d9ee56f4c 100644 --- a/libs/hwui/jni/Typeface.cpp +++ b/libs/hwui/jni/Typeface.cpp @@ -204,10 +204,9 @@ static sk_sp<SkData> makeSkDataCached(const std::string& path, bool hasVerity) { return entry; } -static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSkia( - minikin::BufferReader* reader) { - const void* buffer = reader->data(); - size_t pos = reader->pos(); +static std::shared_ptr<minikin::MinikinFont> loadMinikinFontSkia(minikin::BufferReader); + +static minikin::Font::TypefaceLoader* readMinikinFontSkia(minikin::BufferReader* reader) { // Advance reader's position. reader->skipString(); // fontPath reader->skip<int>(); // fontIndex @@ -217,60 +216,63 @@ static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSki reader->skip<uint32_t>(); // expectedFontRevision reader->skipString(); // expectedPostScriptName } - return [buffer, pos]() -> std::shared_ptr<minikin::MinikinFont> { - minikin::BufferReader fontReader(buffer, pos); - std::string_view fontPath = fontReader.readString(); - std::string path(fontPath.data(), fontPath.size()); - ATRACE_FORMAT("Loading font %s", path.c_str()); - int fontIndex = fontReader.read<int>(); - const minikin::FontVariation* axesPtr; - uint32_t axesCount; - std::tie(axesPtr, axesCount) = fontReader.readArray<minikin::FontVariation>(); - bool hasVerity = static_cast<bool>(fontReader.read<int8_t>()); - uint32_t expectedFontRevision; - std::string_view expectedPostScriptName; - if (hasVerity) { - expectedFontRevision = fontReader.read<uint32_t>(); - expectedPostScriptName = fontReader.readString(); - } - sk_sp<SkData> data = makeSkDataCached(path, hasVerity); - if (data.get() == nullptr) { - // This may happen if: - // 1. When the process failed to open the file (e.g. invalid path or permission). - // 2. When the process failed to map the file (e.g. hitting max_map_count limit). - ALOGE("Failed to make SkData from file name: %s", path.c_str()); + return &loadMinikinFontSkia; +} + +static std::shared_ptr<minikin::MinikinFont> loadMinikinFontSkia(minikin::BufferReader reader) { + std::string_view fontPath = reader.readString(); + std::string path(fontPath.data(), fontPath.size()); + ATRACE_FORMAT("Loading font %s", path.c_str()); + int fontIndex = reader.read<int>(); + const minikin::FontVariation* axesPtr; + uint32_t axesCount; + std::tie(axesPtr, axesCount) = reader.readArray<minikin::FontVariation>(); + bool hasVerity = static_cast<bool>(reader.read<int8_t>()); + uint32_t expectedFontRevision; + std::string_view expectedPostScriptName; + if (hasVerity) { + expectedFontRevision = reader.read<uint32_t>(); + expectedPostScriptName = reader.readString(); + } + sk_sp<SkData> data = makeSkDataCached(path, hasVerity); + if (data.get() == nullptr) { + // This may happen if: + // 1. When the process failed to open the file (e.g. invalid path or permission). + // 2. When the process failed to map the file (e.g. hitting max_map_count limit). + ALOGE("Failed to make SkData from file name: %s", path.c_str()); + return nullptr; + } + const void* fontPtr = data->data(); + size_t fontSize = data->size(); + if (hasVerity) { + // Verify font metadata if verity is enabled. + minikin::FontFileParser parser(fontPtr, fontSize, fontIndex); + std::optional<uint32_t> revision = parser.getFontRevision(); + if (!revision.has_value() || revision.value() != expectedFontRevision) { + LOG_ALWAYS_FATAL("Wrong font revision: %s", path.c_str()); return nullptr; } - const void* fontPtr = data->data(); - size_t fontSize = data->size(); - if (hasVerity) { - // Verify font metadata if verity is enabled. - minikin::FontFileParser parser(fontPtr, fontSize, fontIndex); - std::optional<uint32_t> revision = parser.getFontRevision(); - if (!revision.has_value() || revision.value() != expectedFontRevision) { - LOG_ALWAYS_FATAL("Wrong font revision: %s", path.c_str()); - return nullptr; - } - std::optional<std::string> psName = parser.getPostScriptName(); - if (!psName.has_value() || psName.value() != expectedPostScriptName) { - LOG_ALWAYS_FATAL("Wrong PostScript name: %s", path.c_str()); - return nullptr; - } - } - std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount); - std::shared_ptr<minikin::MinikinFont> minikinFont = - fonts::createMinikinFontSkia(std::move(data), fontPath, fontPtr, fontSize, - fontIndex, axes); - if (minikinFont == nullptr) { - ALOGE("Failed to create MinikinFontSkia: %s", path.c_str()); + std::optional<std::string> psName = parser.getPostScriptName(); + if (!psName.has_value() || psName.value() != expectedPostScriptName) { + LOG_ALWAYS_FATAL("Wrong PostScript name: %s", path.c_str()); return nullptr; } - return minikinFont; - }; + } + std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount); + std::shared_ptr<minikin::MinikinFont> minikinFont = fonts::createMinikinFontSkia( + std::move(data), fontPath, fontPtr, fontSize, fontIndex, axes); + if (minikinFont == nullptr) { + ALOGE("Failed to create MinikinFontSkia: %s", path.c_str()); + return nullptr; + } + return minikinFont; } static void writeMinikinFontSkia(minikin::BufferWriter* writer, const minikin::MinikinFont* typeface) { + // When you change the format of font metadata, please update code to parse + // typefaceMetadataReader() in + // frameworks/base/libs/hwui/jni/fonts/Font.cpp too. const std::string& path = typeface->GetFontPath(); writer->writeString(path); writer->write<int>(typeface->GetFontIndex()); diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index bd3b7c93466c..09be630dc741 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -192,7 +192,7 @@ static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong // Critical Native static jlong Font_getMinikinFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); - return reinterpret_cast<jlong>(font->font->typeface().get()); + return reinterpret_cast<jlong>(font->font.get()); } // Critical Native @@ -224,12 +224,21 @@ static jlong Font_getReleaseNativeFontFunc(CRITICAL_JNI_PARAMS) { // Fast Native static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); - const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); - const std::string& path = minikinFont->GetFontPath(); - if (path.empty()) { - return nullptr; + minikin::BufferReader reader = font->font->typefaceMetadataReader(); + if (reader.data() != nullptr) { + std::string path = std::string(reader.readString()); + if (path.empty()) { + return nullptr; + } + return env->NewStringUTF(path.c_str()); + } else { + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + const std::string& path = minikinFont->GetFontPath(); + if (path.empty()) { + return nullptr; + } + return env->NewStringUTF(path.c_str()); } - return env->NewStringUTF(path.c_str()); } // Fast Native @@ -257,22 +266,43 @@ static jint Font_getPackedStyle(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { // Critical Native static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); - const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); - return minikinFont->GetFontIndex(); + minikin::BufferReader reader = font->font->typefaceMetadataReader(); + if (reader.data() != nullptr) { + reader.skipString(); // fontPath + return reader.read<int>(); + } else { + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + return minikinFont->GetFontIndex(); + } } // Critical Native static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); - const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); - return minikinFont->GetAxes().size(); + minikin::BufferReader reader = font->font->typefaceMetadataReader(); + if (reader.data() != nullptr) { + reader.skipString(); // fontPath + reader.skip<int>(); // fontIndex + return reader.readArray<minikin::FontVariation>().second; + } else { + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + return minikinFont->GetAxes().size(); + } } // Critical Native static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint index) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); - const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); - minikin::FontVariation var = minikinFont->GetAxes().at(index); + minikin::BufferReader reader = font->font->typefaceMetadataReader(); + minikin::FontVariation var; + if (reader.data() != nullptr) { + reader.skipString(); // fontPath + reader.skip<int>(); // fontIndex + var = reader.readArray<minikin::FontVariation>().first[index]; + } else { + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + var = minikinFont->GetAxes().at(index); + } uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value); return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary); } diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index e5e8fe6ed5dc..687830d5c7b3 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -20,7 +20,7 @@ android:id="@+id/udfps_view" android:layout_width="match_parent" android:layout_height="match_parent" - systemui:sensorTouchAreaCoefficient="0.5" + systemui:sensorTouchAreaCoefficient="0.75" android:contentDescription="@string/accessibility_fingerprint_label"> <ViewStub diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index d1cb6ec7a072..c4f78e7782a2 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -376,6 +376,17 @@ public class AuthContainerView extends LinearLayout addView(mFrameLayout); + // init view before showing + if (mBiometricView != null) { + mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation); + mBiometricView.setPanelController(mPanelController); + mBiometricView.setPromptInfo(mConfig.mPromptInfo); + mBiometricView.setCallback(mBiometricCallback); + mBiometricView.setBackgroundView(mBackgroundView); + mBiometricView.setUserId(mConfig.mUserId); + mBiometricView.setEffectiveUserId(mEffectiveUserId); + } + // TODO: De-dupe the logic with AuthCredentialPasswordView setOnKeyListener((v, keyCode, event) -> { if (keyCode != KeyEvent.KEYCODE_BACK) { @@ -404,13 +415,6 @@ public class AuthContainerView extends LinearLayout } private void addBiometricView() { - mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation); - mBiometricView.setPanelController(mPanelController); - mBiometricView.setPromptInfo(mConfig.mPromptInfo); - mBiometricView.setCallback(mBiometricCallback); - mBiometricView.setBackgroundView(mBackgroundView); - mBiometricView.setUserId(mConfig.mUserId); - mBiometricView.setEffectiveUserId(mEffectiveUserId); mBiometricScrollView.addView(mBiometricView); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 980bb12d390c..00888dfe48d3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -355,7 +355,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @Override public void onBouncerVisibilityChanged() { - mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing(); + mIsBouncerVisible = mKeyguardViewManager.isBouncerShowing(); if (!mIsBouncerVisible) { mInputBouncerHiddenAmount = 1f; } else if (mKeyguardViewManager.isBouncerShowing()) { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index c45eb3558948..ff95604088ed 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -307,7 +307,7 @@ public class DozeTriggers implements DozeMachine.Part { // Since the gesture won't be received by the UDFPS view, manually inject an // event. mAuthController.onAodInterrupt((int) screenX, (int) screenY, - rawValues[2] /* major */, rawValues[3] /* minor */); + rawValues[3] /* major */, rawValues[4] /* minor */); } else { mDozeHost.extendPulse(pulseReason); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 032f9c4e7581..0d9749e05262 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -104,7 +104,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private static final int MAX_NUM_LOGGED_GESTURES = 10; // Temporary log until b/176302696 is resolved - static final boolean DEBUG_MISSING_GESTURE = true; + static final boolean DEBUG_MISSING_GESTURE = false; static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture"; private static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION = @@ -147,6 +147,16 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mPackageName = "_UNKNOWN"; } } + + @Override + public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { + mIsInPipMode = true; + } + + @Override + public void onActivityUnpinned() { + mIsInPipMode = false; + } }; private DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = @@ -220,6 +230,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private boolean mIsNavBarShownTransiently; private boolean mIsBackGestureAllowed; private boolean mGestureBlockingActivityRunning; + private boolean mIsInPipMode; private InputMonitor mInputMonitor; private InputChannelCompat.InputEventReceiver mInputEventReceiver; @@ -636,7 +647,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker // If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back // gesture - if (mPipExcludedBounds.contains(x, y) || mNavBarOverlayExcludedBounds.contains(x, y)) { + final boolean isInsidePip = mIsInPipMode && mPipExcludedBounds.contains(x, y); + if (isInsidePip || mNavBarOverlayExcludedBounds.contains(x, y)) { return false; } @@ -898,6 +910,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker pw.println(" mInRejectedExclusion" + mInRejectedExclusion); pw.println(" mExcludeRegion=" + mExcludeRegion); pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion); + pw.println(" mIsInPipMode=" + mIsInPipMode); pw.println(" mPipExcludedBounds=" + mPipExcludedBounds); pw.println(" mNavBarOverlayExcludedBounds=" + mNavBarOverlayExcludedBounds); pw.println(" mEdgeWidthLeft=" + mEdgeWidthLeft); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index 1a828e45dc38..cbdcad5cf385 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -16,9 +16,7 @@ package com.android.systemui.qs; -import android.content.Intent; import android.os.Bundle; -import android.provider.AlarmClock; import android.view.View; import android.view.View.OnClickListener; @@ -113,10 +111,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader private View.OnClickListener mOnClickListener = new OnClickListener() { @Override public void onClick(View v) { - if (v == mClockView) { - mActivityStarter.postStartActivityDismissingKeyguard(new Intent( - AlarmClock.ACTION_SHOW_ALARMS), 0); - } else if (v == mPrivacyChip) { + if (v == mPrivacyChip) { // If the privacy chip is visible, it means there were some indicators mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK); mPrivacyDialogController.showDialog(getContext()); @@ -167,7 +162,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader @Override protected void onViewAttached() { - mClockView.setOnClickListener(mOnClickListener); mPrivacyChip.setOnClickListener(mOnClickListener); mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); @@ -188,7 +182,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader @Override protected void onViewDetached() { - mClockView.setOnClickListener(null); mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener); mPrivacyChip.setOnClickListener(null); mStatusBarIconController.removeIconGroup(mIconManager); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index f4c15fbf90b8..cb0c411b2753 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -560,8 +560,6 @@ public class OverviewProxyService extends CurrentUserTracker implements mSmartspaceTransitionController.createExternalInterface().asBinder()); try { - Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.onInitialize: curUser=" - + mCurrentBoundedUserId); mOverviewProxy.onInitialize(params); } catch (RemoteException e) { mCurrentBoundedUserId = -1; @@ -646,7 +644,6 @@ public class OverviewProxyService extends CurrentUserTracker implements // Listen for nav bar mode changes mNavBarMode = navModeController.addListener(this); - Log.d(TAG_OPS + " b/182478748", "OverviewProxyService: mode=" + mNavBarMode); // Listen for launcher package changes IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); @@ -807,7 +804,6 @@ public class OverviewProxyService extends CurrentUserTracker implements mOverviewServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, UserHandle.of(getCurrentUserId())); - Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.connect: bound=" + mBound); } catch (SecurityException e) { Log.e(TAG_OPS, "Unable to bind because of security error", e); } @@ -860,9 +856,6 @@ public class OverviewProxyService extends CurrentUserTracker implements private void disconnectFromLauncherService() { if (mBound) { - Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.disconnect: curUser=" - + mCurrentBoundedUserId); - // Always unbind the service (ie. if called through onNullBinding or onBindingDied) mContext.unbindService(mOverviewServiceConnection); mBound = false; @@ -993,14 +986,11 @@ public class OverviewProxyService extends CurrentUserTracker implements final int currentUser = ActivityManagerWrapper.getInstance().getCurrentUserId(); mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent, MATCH_SYSTEM_ONLY, currentUser) != null; - Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.updateEnabledState: curUser=" - + currentUser + " enabled=" + mIsEnabled); } @Override public void onNavigationModeChanged(int mode) { mNavBarMode = mode; - Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.onNavModeChanged: mode=" + mode); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 1331829201b8..7a1e5cf1770b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -642,7 +642,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump return; } - if (mState == ScrimState.UNLOCKED) { + if (mState == ScrimState.UNLOCKED || mState == ScrimState.BUBBLE_EXPANDED) { // Darken scrim as you pull down the shade when unlocked, unless the shade is expanding // because we're doing the screen off animation. if (!mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()) { @@ -657,12 +657,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } mInFrontAlpha = 0; } - } else if (mState == ScrimState.BUBBLE_EXPANDED) { - // Darken scrim as you pull down the shade when unlocked - float behindFraction = getInterpolatedFraction(); - behindFraction = (float) Math.pow(behindFraction, 0.8f); - mBehindAlpha = behindFraction * mDefaultScrimAlpha; - mInFrontAlpha = 0; } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED || mState == ScrimState.PULSING) { Pair<Integer, Float> result = calculateBackStateForState(mState); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 4a9d66c16003..c0b45c6d5c96 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -236,13 +236,14 @@ public class DozeTriggersTest extends SysuiTestCase { public void testOnSensor_Fingerprint() { final int screenX = 100; final int screenY = 100; + final float misc = -1; final float minor = 2f; final float major = 3f; final int reason = DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; - float[] rawValues = new float[]{screenX, screenY, minor, major}; + float[] rawValues = new float[]{screenX, screenY, misc, major, minor}; mTriggers.onSensor(reason, screenX, screenY, rawValues); verify(mHost).extendPulse(reason); - verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY), eq(minor), eq(major)); + verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY), eq(major), eq(minor)); } private void waitForSensorManager() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt index 3c1b36e41d89..7ac6f8212a61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt @@ -127,6 +127,11 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { } @Test + fun testClockNotClickable() { + assertThat(clock.isClickable).isFalse() + } + + @Test fun testIgnoredSlotsOnAttached_noIndicators() { setPrivacyController(micCamera = false, location = false) diff --git a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java index d44a4173fc12..7b9ca2ddc345 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java @@ -119,20 +119,32 @@ public class CameraExtensionsProxyService extends Service { private static final String CAMERA_EXTENSION_VERSION_NAME = "androidx.camera.extensions.impl.ExtensionVersionImpl"; private static final String LATEST_VERSION = "1.2.0"; - private static final String LEGACY_VERSION_PREFIX = "1.1"; + private static final String NON_INIT_VERSION_PREFIX = "1.0"; private static final String ADVANCED_VERSION_PREFIX = "1.2"; private static final String[] SUPPORTED_VERSION_PREFIXES = {ADVANCED_VERSION_PREFIX, - LEGACY_VERSION_PREFIX, "1.0."}; + "1.1", NON_INIT_VERSION_PREFIX}; private static final boolean EXTENSIONS_PRESENT = checkForExtensions(); private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ? (new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null; - private static final boolean LEGACY_VERSION_SUPPORTED = - EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(LEGACY_VERSION_PREFIX); - private static final boolean ADVANCED_VERSION_SUPPORTED = - EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX); + private static final boolean ADVANCED_API_SUPPORTED = checkForAdvancedAPI(); + private static final boolean INIT_API_SUPPORTED = EXTENSIONS_PRESENT && + (!EXTENSIONS_VERSION.startsWith(NON_INIT_VERSION_PREFIX)); private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>(); + private static boolean checkForAdvancedAPI() { + if (EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX)) { + try { + return (new ExtensionVersionImpl()).isAdvancedExtenderImplemented(); + } catch (NoSuchMethodError e) { + // This could happen in case device specific extension implementations are using an + // older extension API but incorrectly set the extension version. + } + } + + return false; + } + private static boolean checkForExtensions() { try { Class.forName(CAMERA_EXTENSION_VERSION_NAME); @@ -265,7 +277,7 @@ public class CameraExtensionsProxyService extends Service { public long registerClient(Context ctx) { synchronized (mLock) { - if (LEGACY_VERSION_SUPPORTED) { + if (INIT_API_SUPPORTED) { if (mActiveClients.isEmpty()) { InitializerFuture status = new InitializerFuture(); InitializerImpl.init(EXTENSIONS_VERSION, ctx, new InitializeHandler(status), @@ -299,7 +311,7 @@ public class CameraExtensionsProxyService extends Service { public void unregisterClient(long clientId) { synchronized (mLock) { if (mActiveClients.remove(clientId) && mActiveClients.isEmpty() && - LEGACY_VERSION_SUPPORTED) { + INIT_API_SUPPORTED) { InitializerFuture status = new InitializerFuture(); InitializerImpl.deinit(new ReleaseHandler(status), new HandlerExecutor(mHandler)); @@ -528,7 +540,7 @@ public class CameraExtensionsProxyService extends Service { @Override public boolean advancedExtensionsSupported() { - return ADVANCED_VERSION_SUPPORTED; + return ADVANCED_API_SUPPORTED; } @Override @@ -819,10 +831,11 @@ public class CameraExtensionsProxyService extends Service { } @Override - public void onNextImageAvailable(OutputConfigId outputConfigId, ParcelImage img) { + public void onNextImageAvailable(OutputConfigId outputConfigId, ParcelImage img, + String physicalCameraId) { if (mImageProcessor != null) { mImageProcessor.onNextImageAvailable(outputConfigId.id, img.timestamp, - new ImageReferenceImpl(img)); + new ImageReferenceImpl(img), physicalCameraId); } } } @@ -1072,9 +1085,7 @@ public class CameraExtensionsProxyService extends Service { @Override public void init(String cameraId, CameraMetadataNative chars) { - if (LEGACY_VERSION_SUPPORTED) { - mPreviewExtender.init(cameraId, new CameraCharacteristics(chars)); - } + mPreviewExtender.init(cameraId, new CameraCharacteristics(chars)); } @Override @@ -1136,7 +1147,7 @@ public class CameraExtensionsProxyService extends Service { @Override public List<SizeList> getSupportedResolutions() { - if (LEGACY_VERSION_SUPPORTED) { + if (INIT_API_SUPPORTED) { List<Pair<Integer, android.util.Size[]>> sizes = mPreviewExtender.getSupportedResolutions(); if ((sizes != null) && !sizes.isEmpty()) { @@ -1182,9 +1193,7 @@ public class CameraExtensionsProxyService extends Service { @Override public void init(String cameraId, CameraMetadataNative chars) { - if (LEGACY_VERSION_SUPPORTED) { - mImageExtender.init(cameraId, new CameraCharacteristics(chars)); - } + mImageExtender.init(cameraId, new CameraCharacteristics(chars)); } @Override @@ -1228,7 +1237,7 @@ public class CameraExtensionsProxyService extends Service { @Override public List<SizeList> getSupportedResolutions() { - if (LEGACY_VERSION_SUPPORTED) { + if (INIT_API_SUPPORTED) { List<Pair<Integer, android.util.Size[]>> sizes = mImageExtender.getSupportedResolutions(); if ((sizes != null) && !sizes.isEmpty()) { @@ -1238,6 +1247,23 @@ public class CameraExtensionsProxyService extends Service { return null; } + + @Override + public LatencyRange getEstimatedCaptureLatencyRange( + android.hardware.camera2.extension.Size outputSize) { + if (EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX)) { + Size sz = new Size(outputSize.width, outputSize.height); + Range<Long> latencyRange = mImageExtender.getEstimatedCaptureLatencyRange(sz); + if (latencyRange != null) { + LatencyRange ret = new LatencyRange(); + ret.min = latencyRange.getLower(); + ret.max = latencyRange.getUpper(); + return ret; + } + } + + return null; + } } private class CaptureProcessorImplStub extends ICaptureProcessorImpl.Stub { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c841fa3b057c..231d4b1da58a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -14309,7 +14309,7 @@ public class ActivityManagerService extends IActivityManager.Stub private boolean checkExcessivePowerUsageLPr(final long uptimeSince, boolean doCpuKills, final long cputimeUsed, final String processName, final String description, final int cpuLimit, final ProcessRecord app) { - if (DEBUG_POWER) { + if (DEBUG_POWER && (uptimeSince > 0)) { StringBuilder sb = new StringBuilder(128); sb.append("CPU for "); sb.append(description); @@ -15349,6 +15349,11 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public int[] getStartedUserIds() { + return mUserController.getStartedUserArray(); + } + + @Override public void setPendingIntentAllowBgActivityStarts(IIntentSender target, IBinder allowlistToken, int flags) { if (!(target instanceof PendingIntentRecord)) { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 3bfd62b60c89..8ebc987a59f4 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -4289,8 +4289,8 @@ public final class ProcessList { if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_SERVICE) { long lastCpuTime = r.mProfile.mLastCpuTime.get(); - if (lastCpuTime != 0) { - long uptimeSince = curUptime - service.mLastPowerCheckUptime; + long uptimeSince = curUptime - service.mLastPowerCheckUptime; + if (lastCpuTime != 0 && uptimeSince > 0) { long timeUsed = r.mProfile.mCurCpuTime.get() - lastCpuTime; long cpuTimeToken = proto.start(ProcessOomProto.Detail.SERVICE_RUN_TIME); proto.write(ProcessOomProto.Detail.CpuRunTime.OVER_MS, uptimeSince); @@ -4427,7 +4427,7 @@ public final class ProcessList { if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_SERVICE) { long lastCpuTime = r.mProfile.mLastCpuTime.get(); - if (lastCpuTime != 0) { + if (lastCpuTime != 0 && uptimeSince > 0) { long timeUsed = r.mProfile.mCurCpuTime.get() - lastCpuTime; pw.print(prefix); pw.print(" "); diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 6f73985fbcd4..5cd330a5753c 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -27,6 +27,8 @@ import static android.Manifest.permission.USE_FINGERPRINT; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; +import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED; import static android.hardware.biometrics.BiometricManager.Authenticators; import android.annotation.NonNull; @@ -76,7 +78,6 @@ import java.util.List; */ public class AuthService extends SystemService { private static final String TAG = "AuthService"; - private static final boolean DEBUG = false; private static final String SETTING_HIDL_DISABLED = "com.android.server.biometrics.AuthService.hidlDisabled"; private static final int DEFAULT_HIDL_DISABLED = 0; @@ -208,7 +209,6 @@ public class AuthService extends SystemService { public void authenticate(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) throws RemoteException { - // Only allow internal clients to authenticate with a different userId. final int callingUserId = UserHandle.getCallingUserId(); final int callingUid = Binder.getCallingUid(); @@ -222,17 +222,18 @@ public class AuthService extends SystemService { } if (!checkAppOps(callingUid, opPackageName, "authenticate()")) { - Slog.e(TAG, "Denied by app ops: " + opPackageName); + authenticateFastFail("Denied by app ops: " + opPackageName, receiver); return; } - if (!Utils.isForeground(callingUid, callingPid)) { - Slog.e(TAG, "Caller is not foreground: " + opPackageName); + if (token == null || receiver == null || opPackageName == null || promptInfo == null) { + authenticateFastFail( + "Unable to authenticate, one or more null arguments", receiver); return; } - if (token == null || receiver == null || opPackageName == null || promptInfo == null) { - Slog.e(TAG, "Unable to authenticate, one or more null arguments"); + if (!Utils.isForeground(callingUid, callingPid)) { + authenticateFastFail("Caller is not foreground: " + opPackageName, receiver); return; } @@ -257,6 +258,17 @@ public class AuthService extends SystemService { } } + private void authenticateFastFail(String message, IBiometricServiceReceiver receiver) { + // notify caller in cases where authentication is aborted before calling into + // IBiometricService without raising an exception + Slog.e(TAG, message); + try { + receiver.onError(TYPE_NONE, BIOMETRIC_ERROR_CANCELED, 0 /*vendorCode */); + } catch (RemoteException e) { + Slog.e(TAG, "authenticateFastFail failed to notify caller", e); + } + } + @Override public void cancelAuthentication(IBinder token, String opPackageName) throws RemoteException { diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index ef02a472969b..8f364890aa06 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -679,7 +679,8 @@ public final class AuthSession implements IBinder.DeathRecipient { FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED, latency, mDebugEnabled, - -1 /* sensorId */); + -1 /* sensorId */, + -1f /* ambientLightLux */); } else { final long latency = System.currentTimeMillis() - mStartTimeMs; diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index 99f4e2cb280c..f51b1c2a4f0c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -69,6 +69,31 @@ public abstract class BaseClientMonitor extends LoggableMonitor } } + /** Holder for wrapping multiple handlers into a single Callback. */ + protected static class CompositeCallback implements Callback { + @NonNull + private final Callback[] mCallbacks; + + public CompositeCallback(@NonNull Callback... callbacks) { + mCallbacks = callbacks; + } + + @Override + public final void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { + for (int i = 0; i < mCallbacks.length; i++) { + mCallbacks[i].onClientStarted(clientMonitor); + } + } + + @Override + public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor, + boolean success) { + for (int i = mCallbacks.length - 1; i >= 0; i--) { + mCallbacks[i].onClientFinished(clientMonitor, success); + } + } + } + private final int mSequentialId; @NonNull private final Context mContext; private final int mTargetUserId; @@ -125,7 +150,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction, int statsClient) { - super(statsModality, statsAction, statsClient); + super(context, statsModality, statsAction, statsClient); mSequentialId = sCount++; mContext = context; mToken = token; @@ -153,10 +178,19 @@ public abstract class BaseClientMonitor extends LoggableMonitor * @param callback invoked when the operation is complete (succeeds, fails, etc) */ public void start(@NonNull Callback callback) { - mCallback = callback; + mCallback = wrapCallbackForStart(callback); mCallback.onClientStarted(this); } + /** + * Called during start to provide subclasses a hook for decorating the callback. + * + * Returns the original callback unless overridden. + */ + @NonNull + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return callback; + } public boolean isAlreadyDone() { return mAlreadyDone; diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java index 4da644d23dec..c8867ea2df1c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java @@ -16,7 +16,13 @@ package com.android.server.biometrics.sensors; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.face.FaceManager; @@ -37,26 +43,47 @@ public abstract class LoggableMonitor { final int mStatsModality; private final int mStatsAction; private final int mStatsClient; + @NonNull private final SensorManager mSensorManager; private long mFirstAcquireTimeMs; + private boolean mLightSensorEnabled = false; private boolean mShouldLogMetrics = true; - /** - * Only valid for AuthenticationClient. - * @return true if the client is authenticating for a crypto operation. - */ - protected boolean isCryptoOperation() { - return false; - } + // report only the most recent value + // consider com.android.server.display.utils.AmbientFilter or similar if need arises + private volatile float mLastAmbientLux = 0; + + private final SensorEventListener mLightSensorListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + mLastAmbientLux = event.values[0]; + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Not used. + } + }; /** + * @param context system_server context * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants. * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants. * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants. */ - public LoggableMonitor(int statsModality, int statsAction, int statsClient) { + public LoggableMonitor(@NonNull Context context, int statsModality, int statsAction, + int statsClient) { mStatsModality = statsModality; mStatsAction = statsAction; mStatsClient = statsClient; + mSensorManager = context.getSystemService(SensorManager.class); + } + + /** + * Only valid for AuthenticationClient. + * @return true if the client is authenticating for a crypto operation. + */ + protected boolean isCryptoOperation() { + return false; } protected void setShouldLog(boolean shouldLog) { @@ -131,7 +158,6 @@ public abstract class LoggableMonitor { } protected final void logOnError(Context context, int error, int vendorCode, int targetUserId) { - if (!mShouldLogMetrics) { return; } @@ -199,7 +225,8 @@ public abstract class LoggableMonitor { + ", Client: " + mStatsClient + ", RequireConfirmation: " + requireConfirmation + ", State: " + authState - + ", Latency: " + latency); + + ", Latency: " + latency + + ", Lux: " + mLastAmbientLux); } else { Slog.v(TAG, "Authentication latency: " + latency); } @@ -217,7 +244,8 @@ public abstract class LoggableMonitor { authState, sanitizeLatency(latency), Utils.isDebugEnabled(context, targetUserId), - -1 /* sensorId */); + -1 /* sensorId */, + mLastAmbientLux /* ambientLightLux */); } protected final void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) { @@ -230,6 +258,7 @@ public abstract class LoggableMonitor { + ", User: " + targetUserId + ", Client: " + mStatsClient + ", Latency: " + latency + + ", Lux: " + mLastAmbientLux + ", Success: " + enrollSuccessful); } else { Slog.v(TAG, "Enroll latency: " + latency); @@ -244,7 +273,8 @@ public abstract class LoggableMonitor { targetUserId, sanitizeLatency(latency), enrollSuccessful, - -1 /* sensorId */); + -1, /* sensorId */ + mLastAmbientLux /* ambientLightLux */); } private long sanitizeLatency(long latency) { @@ -255,4 +285,46 @@ public abstract class LoggableMonitor { return latency; } + /** Get a callback to start/stop ALS capture when client runs. */ + @NonNull + protected BaseClientMonitor.Callback createALSCallback() { + return new BaseClientMonitor.Callback() { + @Override + public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { + setLightSensorLoggingEnabled(getAmbientLightSensor(mSensorManager)); + } + + @Override + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, + boolean success) { + setLightSensorLoggingEnabled(null); + } + }; + } + + /** The sensor to use for ALS logging. */ + @Nullable + protected Sensor getAmbientLightSensor(@NonNull SensorManager sensorManager) { + return mShouldLogMetrics ? sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) : null; + } + + private void setLightSensorLoggingEnabled(@Nullable Sensor lightSensor) { + if (DEBUG) { + Slog.v(TAG, "capturing ambient light using: " + + (lightSensor != null ? lightSensor : "[disabled]")); + } + + if (lightSensor != null) { + if (!mLightSensorEnabled) { + mLightSensorEnabled = true; + mLastAmbientLux = 0; + mSensorManager.registerListener(mLightSensorListener, lightSensor, + SensorManager.SENSOR_DELAY_NORMAL); + } + } else { + mLightSensorEnabled = false; + mLastAmbientLux = 0; + mSensorManager.unregisterListener(mLightSensorListener); + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 2ab1285ba8a7..2adf5f98cee5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -99,6 +99,12 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements "face_custom_success_error", 0) == 1; } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override protected void startHalOperation() { try { @@ -229,7 +235,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements } } - @Override public void onLockoutTimed(long durationMillis) { + @Override + public void onLockoutTimed(long durationMillis) { mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED); // Lockout metrics are logged as an error code. final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT; @@ -242,7 +249,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements } } - @Override public void onLockoutPermanent() { + @Override + public void onLockoutPermanent() { mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT); // Lockout metrics are logged as an error code. final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index f97414780247..ff68aa87dbbb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -95,6 +95,12 @@ public class FaceEnrollClient extends EnrollClient<ISession> { ReEnrollNotificationUtils.cancelNotification(getContext()); } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override public void destroy() { try { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java index c4bdb320ddef..01dd18fb5cf0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java @@ -88,6 +88,12 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { "face_custom_success_error", 0) == 1; } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override protected void startHalOperation() { try { @@ -186,7 +192,6 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { @Override public void onAcquired(int acquireInfo, int vendorCode) { - mLastAcquire = acquireInfo; if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java index fc1200a4b42a..d3bd18b6f704 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java @@ -68,6 +68,12 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist); } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override protected boolean hasReachedEnrollmentLimit() { final int limit = getContext().getResources().getInteger( diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index a5326b352264..3c9d802cb4b1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -70,6 +70,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp mUdfpsOverlayController = udfpsOverlayController; } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index edd30bc6b333..11849661693b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -79,6 +79,12 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { } } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) { super.onEnrollResult(identifier, remaining); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index 24ed85c42e4e..45e35e34b62b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -69,6 +69,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi mUdfpsOverlayController = udfpsOverlayController; } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index 4f3e930c4e88..a28a3f6bd5c3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -72,6 +72,12 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint } } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override protected boolean hasReachedEnrollmentLimit() { final int limit = getContext().getResources().getInteger( diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java b/services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java index 5ec85e661b98..6931baa948a1 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java @@ -32,16 +32,18 @@ public class ContextHubShellCommand extends ShellCommand { // Internal service impl -- must perform security checks before touching. private final ContextHubService mInternal; + private final Context mContext; public ContextHubShellCommand(Context context, ContextHubService service) { mInternal = service; - - context.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_CONTEXT_HUB, "ContextHubShellCommand"); + mContext = context; } @Override public int onCommand(String cmd) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_CONTEXT_HUB, "ContextHubShellCommand"); + if ("deny".equals(cmd)) { return runDisableAuth(); } diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java index 2cc2ebf616c5..2defe73addc6 100644 --- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java +++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java @@ -17,6 +17,7 @@ package com.android.server.media.metrics; import android.content.Context; +import android.content.pm.PackageManager; import android.media.metrics.IMediaMetricsManager; import android.media.metrics.NetworkEvent; import android.media.metrics.PlaybackErrorEvent; @@ -24,19 +25,29 @@ import android.media.metrics.PlaybackMetrics; import android.media.metrics.PlaybackStateEvent; import android.media.metrics.TrackChangeEvent; import android.os.Binder; +import android.provider.DeviceConfig; import android.util.Base64; +import android.util.Slog; import android.util.StatsEvent; import android.util.StatsLog; import com.android.server.SystemService; import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; /** * System service manages media metrics. */ public final class MediaMetricsManagerService extends SystemService { + private static final String TAG = "MediaMetricsManagerService"; + // TODO: update these constants when finalized + private static final String MEDIA_METRICS_MODE = "media_metrics_mode"; + private static final int MEDIA_METRICS_MODE_OFF = 0; + private static final int MEDIA_METRICS_MODE_ON = 1; private final SecureRandom mSecureRandom; + private List<String> mBlockList = new ArrayList<>(); /** * Initializes the playback metrics manager service. @@ -56,6 +67,9 @@ public final class MediaMetricsManagerService extends SystemService { private final class BinderService extends IMediaMetricsManager.Stub { @Override public void reportPlaybackMetrics(String sessionId, PlaybackMetrics metrics, int userId) { + if (!shouldWriteStats()) { + return; + } StatsEvent statsEvent = StatsEvent.newBuilder() .setAtomId(320) .writeInt(Binder.getCallingUid()) @@ -85,6 +99,9 @@ public final class MediaMetricsManagerService extends SystemService { @Override public void reportPlaybackStateEvent( String sessionId, PlaybackStateEvent event, int userId) { + if (!shouldWriteStats()) { + return; + } StatsEvent statsEvent = StatsEvent.newBuilder() .setAtomId(322) .writeString(sessionId) @@ -116,6 +133,9 @@ public final class MediaMetricsManagerService extends SystemService { @Override public void reportPlaybackErrorEvent( String sessionId, PlaybackErrorEvent event, int userId) { + if (!shouldWriteStats()) { + return; + } StatsEvent statsEvent = StatsEvent.newBuilder() .setAtomId(323) .writeString(sessionId) @@ -130,6 +150,9 @@ public final class MediaMetricsManagerService extends SystemService { public void reportNetworkEvent( String sessionId, NetworkEvent event, int userId) { + if (!shouldWriteStats()) { + return; + } StatsEvent statsEvent = StatsEvent.newBuilder() .setAtomId(321) .writeString(sessionId) @@ -143,6 +166,9 @@ public final class MediaMetricsManagerService extends SystemService { @Override public void reportTrackChangeEvent( String sessionId, TrackChangeEvent event, int userId) { + if (!shouldWriteStats()) { + return; + } StatsEvent statsEvent = StatsEvent.newBuilder() .setAtomId(324) .writeString(sessionId) @@ -165,5 +191,33 @@ public final class MediaMetricsManagerService extends SystemService { .build(); StatsLog.write(statsEvent); } + + private boolean shouldWriteStats() { + int uid = Binder.getCallingUid(); + + final long identity = Binder.clearCallingIdentity(); + int mode = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_MEDIA, MEDIA_METRICS_MODE, MEDIA_METRICS_MODE_OFF); + Binder.restoreCallingIdentity(identity); + + if (mode != MEDIA_METRICS_MODE_ON) { + return false; + } + if (mBlockList.isEmpty()) { + return true; + } + + PackageManager pm = getContext().getPackageManager(); + String[] packages = pm.getPackagesForUid(uid); + if (packages == null) { + // The valid application UID range is from android.os.Process.FIRST_APPLICATION_UID + // to android.os.Process.LAST_APPLICATION_UID. + // UIDs outside this range will not have a package and will therefore be false. + Slog.d(TAG, "null package from uid " + uid); + return false; + } + // TODO: check calling package with allowlist/blocklist. + return true; + } } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 4b4146683a09..654b17fb9754 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -26,6 +26,7 @@ import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; +import static android.os.PowerExemptionManager.reasonCodeToString; import static android.os.Process.INVALID_UID; import android.app.ActivityManager; @@ -192,13 +193,12 @@ public class NetworkPolicyLogger { } } - void tempPowerSaveWlChanged(int appId, boolean added) { + void tempPowerSaveWlChanged(int appId, boolean added, int reasonCode, String reason) { synchronized (mLock) { if (LOGV || appId == UserHandle.getAppId(mDebugUid)) { - Slog.v(TAG, - getTempPowerSaveWlChangedLog(appId, added)); + Slog.v(TAG, getTempPowerSaveWlChangedLog(appId, added, reasonCode, reason)); } - mEventsBuffer.tempPowerSaveWlChanged(appId, added); + mEventsBuffer.tempPowerSaveWlChanged(appId, added, reasonCode, reason); } } @@ -326,8 +326,10 @@ public class NetworkPolicyLogger { return "Parole state: " + paroleOn; } - private static String getTempPowerSaveWlChangedLog(int appId, boolean added) { - return "temp-power-save whitelist for " + appId + " changed to: " + added; + private static String getTempPowerSaveWlChangedLog(int appId, boolean added, + int reasonCode, String reason) { + return "temp-power-save whitelist for " + appId + " changed to: " + added + + "; reason=" + reasonCodeToString(reasonCode) + " <" + reason + ">"; } private static String getUidFirewallRuleChangedLog(int chain, int uid, int rule) { @@ -497,14 +499,17 @@ public class NetworkPolicyLogger { data.timeStamp = System.currentTimeMillis(); } - public void tempPowerSaveWlChanged(int appId, boolean added) { + public void tempPowerSaveWlChanged(int appId, boolean added, + int reasonCode, String reason) { final Data data = getNextSlot(); if (data == null) return; data.reset(); data.type = EVENT_TEMP_POWER_SAVE_WL_CHANGED; data.ifield1 = appId; + data.ifield2 = reasonCode; data.bfield1 = added; + data.sfield1 = reason; data.timeStamp = System.currentTimeMillis(); } @@ -571,7 +576,8 @@ public class NetworkPolicyLogger { case EVENT_PAROLE_STATE_CHANGED: return getParoleStateChanged(data.bfield1); case EVENT_TEMP_POWER_SAVE_WL_CHANGED: - return getTempPowerSaveWlChangedLog(data.ifield1, data.bfield1); + return getTempPowerSaveWlChangedLog(data.ifield1, data.bfield1, + data.ifield2, data.sfield1); case EVENT_UID_FIREWALL_RULE_CHANGED: return getUidFirewallRuleChangedLog(data.ifield1, data.ifield2, data.ifield3); case EVENT_FIREWALL_CHAIN_ENABLED: diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 2e4d41c7d364..03a63b9058a8 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -17,9 +17,11 @@ package com.android.server.net; import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.Network; import android.net.NetworkTemplate; import android.net.netstats.provider.NetworkStatsProvider; +import android.os.PowerExemptionManager.ReasonCode; import android.telephony.SubscriptionPlan; import java.util.Set; @@ -41,9 +43,13 @@ public abstract class NetworkPolicyManagerInternal { * that network rules for that appId can be updated. * * @param appId The appId which has been updated in the allowlist. - * @param added Denotes whether the {@param appId} has been added or removed from the allowlist. + * @param added Denotes whether the {@code appId} has been added or removed from the allowlist. + * @param reasonCode one of {@link ReasonCode} indicating the reason for the change. + * Only valid when {@code added} is {@code true}. + * @param reason an optional human-readable reason explaining why the app is temp allow-listed. */ - public abstract void onTempPowerSaveWhitelistChange(int appId, boolean added); + public abstract void onTempPowerSaveWhitelistChange(int appId, boolean added, + @ReasonCode int reasonCode, @Nullable String reason); /** * Return the active {@link SubscriptionPlan} for the given network. diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index cfd575080d97..21f68aecd25b 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -211,6 +211,7 @@ import android.os.Message; import android.os.MessageQueue.IdleHandler; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; +import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; @@ -5743,12 +5744,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override - public void onTempPowerSaveWhitelistChange(int appId, boolean added) { + public void onTempPowerSaveWhitelistChange(int appId, boolean added, + @ReasonCode int reasonCode, @Nullable String reason) { synchronized (mUidRulesFirstLock) { if (!mSystemReady) { return; } - mLogger.tempPowerSaveWlChanged(appId, added); + mLogger.tempPowerSaveWlChanged(appId, added, reasonCode, reason); if (added) { mPowerSaveTempWhitelistAppIds.put(appId, true); } else { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c1e29ebc4703..6d51849ea513 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2910,6 +2910,7 @@ public class WindowManagerService extends IWindowManager.Stub + " for the display " + displayId + " that does not exist."); return; } + remoteAnimationAdapter.setCallingPidUid(Binder.getCallingPid(), Binder.getCallingUid()); displayContent.mAppTransition.overridePendingAppTransitionRemote( remoteAnimationAdapter); } 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 d5e1cd6e0415..21de7916e23d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -58,7 +58,8 @@ import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION; import static com.android.server.alarm.AlarmManagerService.ACTIVE_INDEX; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED; -import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_ALARM_DENY_LIST_CHANGED; +import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_ADDED; +import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED; @@ -103,10 +104,8 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verifyNoMoreInteractions; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -155,6 +154,8 @@ import com.android.server.AppStateTrackerImpl; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.usage.AppStandbyInternal; @@ -175,6 +176,7 @@ import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Random; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; @@ -370,8 +372,9 @@ public class AlarmManagerServiceTest { .mockStatic(LocalServices.class) .spyStatic(Looper.class) .mockStatic(MetricsHelper.class) - .mockStatic(Settings.Global.class) + .mockStatic(PermissionManagerService.class) .mockStatic(ServiceManager.class) + .mockStatic(Settings.Global.class) .mockStatic(SystemProperties.class) .spyStatic(UserHandle.class) .strictness(Strictness.WARN) @@ -394,7 +397,7 @@ public class AlarmManagerServiceTest { doCallRealMethod().when((MockedVoidMethod) () -> LocalServices.addService(eq(AlarmManagerInternal.class), any())); doCallRealMethod().when(() -> LocalServices.getService(AlarmManagerInternal.class)); - doReturn(false).when(() -> UserHandle.isCore(TEST_CALLING_UID)); + doReturn(false).when(() -> UserHandle.isCore(anyInt())); when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), eq(TEST_CALLING_USER), anyLong())).thenReturn(STANDBY_BUCKET_ACTIVE); doReturn(Looper.getMainLooper()).when(Looper::myLooper); @@ -983,8 +986,7 @@ public class AlarmManagerServiceTest { verify(mService.mHandler, atLeastOnce()).sendMessageAtTime(messageCaptor.capture(), anyLong()); final Message lastMessage = messageCaptor.getValue(); - assertEquals("Unexpected message send to handler", lastMessage.what, - what); + assertEquals("Unexpected message send to handler", what, lastMessage.what); mService.mHandler.handleMessage(lastMessage); } @@ -1876,6 +1878,8 @@ public class AlarmManagerServiceTest { @Test public void hasScheduleExactAlarmBinderCallNotDenyListed() throws RemoteException { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); @@ -1891,6 +1895,8 @@ public class AlarmManagerServiceTest { @Test public void hasScheduleExactAlarmBinderCallDenyListed() throws RemoteException { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockExactAlarmPermissionGrant(true, true, MODE_ERRORED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); @@ -1905,7 +1911,25 @@ public class AlarmManagerServiceTest { } @Test + public void hasScheduleExactAlarmBinderCallChangeDisabled() throws RemoteException { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); + + mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + + mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + } + + private void mockChangeEnabled(long changeId, boolean enabled) { + doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(), + any(UserHandle.class))); + } + + @Test public void hasScheduleExactAlarmBinderCallNotDeclared() throws RemoteException { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockExactAlarmPermissionGrant(false, false, MODE_DEFAULT); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); @@ -1918,9 +1942,7 @@ public class AlarmManagerServiceTest { @Test public void noPermissionCheckWhenChangeDisabled() throws RemoteException { - doReturn(false).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); // alarm clock mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0, @@ -1946,9 +1968,7 @@ public class AlarmManagerServiceTest { @Test public void exactBinderCallWhenChangeDisabled() throws Exception { - doReturn(false).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, @@ -1963,9 +1983,7 @@ public class AlarmManagerServiceTest { @Test public void exactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception { - doReturn(false).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, @@ -1985,9 +2003,7 @@ public class AlarmManagerServiceTest { @Test public void inexactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception { - doReturn(false).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0, @@ -2006,9 +2022,7 @@ public class AlarmManagerServiceTest { @Test public void alarmClockBinderCallWhenChangeDisabled() throws Exception { - doReturn(false).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); final PendingIntent alarmPi = getNewMockPendingIntent(); final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); @@ -2023,9 +2037,7 @@ public class AlarmManagerServiceTest { @Test public void alarmClockBinderCall() throws RemoteException { - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); @@ -2060,7 +2072,6 @@ public class AlarmManagerServiceTest { } else { setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, ""); } - when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE)).thenReturn(mode); } @@ -2068,9 +2079,7 @@ public class AlarmManagerServiceTest { @Test public void alarmClockBinderCallWithoutPermission() throws RemoteException { setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true); - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); @@ -2089,9 +2098,7 @@ public class AlarmManagerServiceTest { @Test public void exactBinderCallWithPermission() throws RemoteException { - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2115,9 +2122,7 @@ public class AlarmManagerServiceTest { @Test public void exactBinderCallWithAllowlist() throws RemoteException { - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // If permission is denied, only then allowlist will be checked. mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); @@ -2137,9 +2142,7 @@ public class AlarmManagerServiceTest { @Test public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException { - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2162,9 +2165,7 @@ public class AlarmManagerServiceTest { @Test public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException { - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // If permission is denied, only then allowlist will be checked. mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); @@ -2191,9 +2192,7 @@ public class AlarmManagerServiceTest { @Test public void exactBinderCallsWithoutPermissionWithoutAllowlist() throws RemoteException { setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true); - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false); @@ -2220,9 +2219,7 @@ public class AlarmManagerServiceTest { public void inexactAllowWhileIdleBinderCall() throws RemoteException { // Both permission and power exemption status don't matter for these alarms. // We only want to test that the flags and idleOptions are correct. - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0, @@ -2244,9 +2241,7 @@ public class AlarmManagerServiceTest { @Test public void binderCallWithUserAllowlist() throws RemoteException { - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); @@ -2266,10 +2261,7 @@ public class AlarmManagerServiceTest { @Test public void minWindowChangeEnabled() { - doReturn(true).when( - () -> CompatChanges.isChangeEnabled( - eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true); final int minWindow = 73; setDeviceConfigLong(KEY_MIN_WINDOW, minWindow); @@ -2315,10 +2307,7 @@ public class AlarmManagerServiceTest { @Test public void minWindowChangeDisabled() { - doReturn(false).when( - () -> CompatChanges.isChangeEnabled( - eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, false); final long minWindow = 73; setDeviceConfigLong(KEY_MIN_WINDOW, minWindow); @@ -2335,10 +2324,7 @@ public class AlarmManagerServiceTest { @Test public void minWindowPriorityAlarm() { - doReturn(true).when( - () -> CompatChanges.isChangeEnabled( - eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true); final long minWindow = 73; setDeviceConfigLong(KEY_MIN_WINDOW, minWindow); @@ -2356,76 +2342,135 @@ public class AlarmManagerServiceTest { } @Test - public void denyListPackagesAdded() { + public void denyListChanged() { mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"}); + when(mActivityManagerInternal.getStartedUserIds()).thenReturn(EmptyArray.INT); + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "p2,p4,p5"); - assertAndHandleMessageSync(EXACT_ALARM_DENY_LIST_CHANGED); + + final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(mService.mHandler, times(2)).sendMessageAtTime(messageCaptor.capture(), + anyLong()); + + final List<Message> messages = messageCaptor.getAllValues(); + for (final Message msg : messages) { + assertTrue("Unwanted message sent to handler: " + msg.what, + msg.what == EXACT_ALARM_DENY_LIST_PACKAGES_ADDED + || msg.what == EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED); + mService.mHandler.handleMessage(msg); + } ArraySet<String> added = new ArraySet<>(new String[]{"p4", "p5"}); - verify(mService).handlePackagesAddedToExactAlarmsDenyListLocked(eq(added)); + verify(mService).handleChangesToExactAlarmDenyList(eq(added), eq(true)); + + ArraySet<String> removed = new ArraySet<>(new String[]{"p1", "p3"}); + verify(mService).handleChangesToExactAlarmDenyList(eq(removed), eq(false)); } @Test - public void denyListPackagesRemoved() { - clearInvocations(mService.mHandler); - mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"}); - setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "p2"); - verifyNoMoreInteractions(mService.mHandler); + public void permissionGrantedDueToDenyList() { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + + final String[] packages = {"example.package.1", "example.package.2"}; + + final int appId1 = 232; + final int appId2 = 431; + + final int userId1 = 42; + final int userId2 = 53; + + registerAppIds(packages, new Integer[]{appId1, appId2}); + + when(mActivityManagerInternal.getStartedUserIds()).thenReturn(new int[]{userId1, userId2}); + + when(mPermissionManagerInternal.getAppOpPermissionPackages( + SCHEDULE_EXACT_ALARM)).thenReturn(packages); + mService.refreshExactAlarmCandidates(); + + final long allowListDuration = 53442; + when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn( + allowListDuration); + + mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId1), MODE_ALLOWED); + mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId1), MODE_DEFAULT); + mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId2), MODE_IGNORED); + mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId2), MODE_ERRORED); + + mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), false); + + // No permission revoked. + verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(anyInt(), anyString()); + + // Permission got granted only for (appId1, userId2). + final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + final ArgumentCaptor<UserHandle> userCaptor = ArgumentCaptor.forClass(UserHandle.class); + + verify(mMockContext).sendBroadcastAsUser(intentCaptor.capture(), userCaptor.capture(), + isNull(), bundleCaptor.capture()); + + assertEquals(userId2, userCaptor.getValue().getIdentifier()); + + // Validate the intent. + assertEquals(AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED, + intentCaptor.getValue().getAction()); + assertEquals(packages[0], intentCaptor.getValue().getPackage()); + + // Validate the options. + final BroadcastOptions bOptions = new BroadcastOptions(bundleCaptor.getValue()); + assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + bOptions.getTemporaryAppAllowlistType()); + assertEquals(allowListDuration, bOptions.getTemporaryAppAllowlistDuration()); + assertEquals(PowerExemptionManager.REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED, + bOptions.getTemporaryAppAllowlistReasonCode()); } @Test - public void removeExactAlarmsOnPackageAddedToDenyList() { - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + public void permissionRevokedDueToDenyList() { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - // basic exact alarm - setTestAlarm(ELAPSED_REALTIME, 1, 0, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, - null); - // exact and allow-while-idle alarm - setTestAlarm(ELAPSED_REALTIME, 2, 0, getNewMockPendingIntent(), 0, FLAG_ALLOW_WHILE_IDLE, - TEST_CALLING_UID, null); - // alarm clock - setWakeFromIdle(RTC_WAKEUP, 3, getNewMockPendingIntent()); + final String[] packages = {"example.package.1", "example.package.2"}; - final PendingIntent inexact = getNewMockPendingIntent(); - setTestAlarm(ELAPSED_REALTIME, 4, 10, inexact, 0, 0, TEST_CALLING_UID, null); + final int appId1 = 232; + final int appId2 = 431; - final PendingIntent inexactAwi = getNewMockPendingIntent(); - setTestAlarm(ELAPSED_REALTIME, 5, 10, inexactAwi, 0, FLAG_ALLOW_WHILE_IDLE, - TEST_CALLING_UID, null); + final int userId1 = 42; + final int userId2 = 53; - final String differentPackage = "different.package"; - final PendingIntent exactButDifferentPackage = getNewMockPendingIntent( - TEST_CALLING_UID, differentPackage); - setTestAlarm(ELAPSED_REALTIME, 6, 0, exactButDifferentPackage, 0, 0, - TEST_CALLING_UID, differentPackage, null); - assertEquals(6, mService.mAlarmStore.size()); + registerAppIds(packages, new Integer[]{appId1, appId2}); - when(mAppOpsManager.checkOpNoThrow(eq(OP_SCHEDULE_EXACT_ALARM), anyInt(), - anyString())).thenReturn(MODE_DEFAULT); - setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, TEST_CALLING_PACKAGE); - assertAndHandleMessageSync(EXACT_ALARM_DENY_LIST_CHANGED); - verify(mService).handlePackagesAddedToExactAlarmsDenyListLocked( - argThat(set -> (set.size() == 1 && set.contains(TEST_CALLING_PACKAGE)))); + when(mActivityManagerInternal.getStartedUserIds()).thenReturn(new int[]{userId1, userId2}); - final ArrayList<Alarm> remaining = mService.mAlarmStore.asList(); - assertEquals(3, remaining.size()); - assertTrue("Basic inexact alarm removed", - remaining.removeIf(a -> a.matches(inexact, null))); - assertTrue("Inexact allow-while-idle alarm removed", - remaining.removeIf(a -> a.matches(inexactAwi, null))); - assertTrue("Alarm from different package removed", - remaining.removeIf(a -> a.matches(exactButDifferentPackage, null))); + when(mPermissionManagerInternal.getAppOpPermissionPackages( + SCHEDULE_EXACT_ALARM)).thenReturn(packages); + mService.refreshExactAlarmCandidates(); - // Mock should return false by default. - verify(mDeviceIdleInternal, atLeastOnce()).isAppOnWhitelist( - UserHandle.getAppId(TEST_CALLING_UID)); + mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId1), MODE_ALLOWED); + mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId1), MODE_DEFAULT); + mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId2), MODE_IGNORED); + mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId2), MODE_ERRORED); + + mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), true); + + // Permission got revoked only for (appId1, userId2) + verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( + eq(UserHandle.getUid(userId1, appId1)), eq(packages[0])); + verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( + eq(UserHandle.getUid(userId1, appId2)), eq(packages[1])); + verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked( + eq(UserHandle.getUid(userId2, appId2)), eq(packages[1])); + + verify(mService).removeExactAlarmsOnPermissionRevokedLocked( + eq(UserHandle.getUid(userId2, appId1)), eq(packages[0])); } @Test - public void opScheduleExactAlarmRevoked() throws Exception { + public void opChangedPermissionRevoked() throws Exception { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + + mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED); mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); assertAndHandleMessageSync(REMOVE_EXACT_ALARMS); verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, @@ -2433,10 +2478,39 @@ public class AlarmManagerServiceTest { } @Test - public void opScheduleExactAlarmGranted() throws Exception { + public void opChangedChangeDisabled() throws Exception { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); + + mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED); + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + + mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); + + verify(mService.mHandler, never()).sendMessageAtTime( + argThat(m -> m.what == REMOVE_EXACT_ALARMS), anyLong()); + } + + @Test + public void opChangedNoPermissionChangeDueToDenyList() throws Exception { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); + + mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED); + mockExactAlarmPermissionGrant(true, true, MODE_DEFAULT); + + mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); + + verify(mService.mHandler, never()).sendMessageAtTime( + argThat(m -> m.what == REMOVE_EXACT_ALARMS), anyLong()); + } + + @Test + public void opChangedPermissionGranted() throws Exception { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + final long durationMs = 20000L; when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs); + mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED); mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); @@ -2462,9 +2536,7 @@ public class AlarmManagerServiceTest { @Test public void removeExactAlarmsOnPermissionRevoked() { - doReturn(true).when( - () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), - anyString(), any(UserHandle.class))); + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // basic exact alarm setTestAlarm(ELAPSED_REALTIME, 0, 0, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, @@ -2501,6 +2573,9 @@ public class AlarmManagerServiceTest { // Mock should return false by default. verify(mDeviceIdleInternal, atLeastOnce()).isAppOnWhitelist( UserHandle.getAppId(TEST_CALLING_UID)); + + verify(() -> PermissionManagerService.killUid(eq(TEST_CALLING_UID), eq(TEST_CALLING_USER), + anyString())); } @Test @@ -2567,6 +2642,86 @@ public class AlarmManagerServiceTest { } @Test + public void onLastOpScheduleExactAlarmOnUserStart() { + final int userId = 54; + SystemService.TargetUser mockTargetUser = mock(SystemService.TargetUser.class); + when(mockTargetUser.getUserIdentifier()).thenReturn(userId); + + final Integer[] appIds = new Integer[]{43, 254, 7731}; + final int unknownAppId = 2347; + final String[] packageNames = new String[]{"p43", "p254", "p7731"}; + final int[] appOpModes = new int[]{MODE_ALLOWED, MODE_IGNORED, MODE_ERRORED}; + mService.mExactAlarmCandidates = new ArraySet<>(appIds); + mService.mExactAlarmCandidates.add(unknownAppId); + + for (int i = 0; i < appIds.length; i++) { + final int uid = UserHandle.getUid(userId, appIds[i]); + final AndroidPackage pkg = mock(AndroidPackage.class); + when(pkg.getPackageName()).thenReturn(packageNames[i]); + + when(mPackageManagerInternal.getPackage(uid)).thenReturn(pkg); + when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, uid, + packageNames[i])).thenReturn(appOpModes[i]); + } + + final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + doReturn(true).when(mService.mHandler).post(runnableCaptor.capture()); + + mService.onUserStarting(mockTargetUser); + runnableCaptor.getValue().run(); + + assertEquals(appIds.length, mService.mLastOpScheduleExactAlarm.size()); + for (int i = 0; i < appIds.length; i++) { + final int uid = UserHandle.getUid(userId, appIds[i]); + assertEquals(appOpModes[i], mService.mLastOpScheduleExactAlarm.get(uid, -1)); + } + assertTrue(mService.mLastOpScheduleExactAlarm.indexOfKey( + UserHandle.getUid(userId, unknownAppId)) < 0); + } + + @Test + public void refreshExactAlarmCandidatesRemovesExactAlarmsIfNeeded() { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + + mService.mExactAlarmCandidates = new ArraySet<>(new Integer[]{1, 2, 5}); + final String[] updatedRequesters = new String[]{"p11", "p2", "p9"}; + final Integer[] appIds = new Integer[]{11, 2, 9}; + registerAppIds(updatedRequesters, appIds); + + when(mPermissionManagerInternal.getAppOpPermissionPackages( + SCHEDULE_EXACT_ALARM)).thenReturn(updatedRequesters); + + final PendingIntent exactAppId1 = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 0, 0, exactAppId1, 0, 0, + UserHandle.getUid(TEST_CALLING_USER, 1), null); + + final PendingIntent exactAppId2 = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 0, 0, exactAppId2, 0, 0, + UserHandle.getUid(TEST_CALLING_USER, 2), null); + + final PendingIntent exactAppId5 = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 0, 0, exactAppId5, 0, 0, + UserHandle.getUid(TEST_CALLING_USER, 5), null); + + final PendingIntent inexactAppId5 = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 0, 23, inexactAppId5, 0, 0, + UserHandle.getUid(TEST_CALLING_USER, 5), null); + + assertEquals(4, mService.mAlarmStore.size()); + + mService.refreshExactAlarmCandidates(); + // App ids 1 and 5 lost the permission, so there alarms should be removed. + + final ArrayList<Alarm> remaining = mService.mAlarmStore.asList(); + assertEquals(2, remaining.size()); + + assertTrue("Inexact alarm removed", + remaining.removeIf(a -> a.matches(inexactAppId5, null))); + assertTrue("Alarm from app id 2 removed", + remaining.removeIf(a -> a.matches(exactAppId2, null))); + } + + @Test public void refreshExactAlarmCandidatesOnPackageAdded() { final String[] exactAlarmRequesters = new String[]{"p11", "p2", "p9"}; final Integer[] appIds = new Integer[]{11, 2, 9}; diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java index 6f0c8e1004c4..aec70f4f3df0 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; +import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS; import static junit.framework.Assert.assertEquals; @@ -234,6 +236,27 @@ public class AuthServiceTest { eq(mReceiver), eq(TEST_OP_PACKAGE_NAME), eq(promptInfo)); + verify(mReceiver).onError(eq(TYPE_NONE), eq(BIOMETRIC_ERROR_CANCELED), anyInt()); + } + + @Test + public void testAuthenticate_missingRequiredParam() throws Exception { + mAuthService = new AuthService(mContext, mInjector); + mAuthService.onStart(); + + final PromptInfo promptInfo = new PromptInfo(); + final long sessionId = 0; + final int userId = 0; + + mAuthService.mImpl.authenticate( + null /* token */, + sessionId, + userId, + mReceiver, + TEST_OP_PACKAGE_NAME, + promptInfo); + waitForIdle(); + verify(mReceiver).onError(eq(TYPE_NONE), eq(BIOMETRIC_ERROR_CANCELED), anyInt()); } @Test @@ -259,7 +282,6 @@ public class AuthServiceTest { eq(authenticators)); } - @Test public void testHasEnrolledBiometrics_callsBiometricServiceHasEnrolledBiometrics() throws Exception { diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp index ea5a43104bba..e07fbbf7a1c1 100644 --- a/tests/UpdatableSystemFontTest/Android.bp +++ b/tests/UpdatableSystemFontTest/Android.bp @@ -27,6 +27,7 @@ android_test { libs: ["android.test.runner"], static_libs: [ "androidx.test.ext.junit", + "androidx.test.uiautomator_uiautomator", "compatibility-device-util-axt", "platform-test-annotations", "truth-prebuilt", diff --git a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/AndroidManifest.xml b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/AndroidManifest.xml index 5d8f5fc2da93..8c3d87698e17 100644 --- a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/AndroidManifest.xml +++ b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/AndroidManifest.xml @@ -19,5 +19,6 @@ package="com.android.emojirenderingtestapp"> <application> <activity android:name=".EmojiRenderingTestActivity"/> + <activity android:name=".GetAvailableFontsTestActivity"/> </application> </manifest> diff --git a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/GetAvailableFontsTestActivity.java b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/GetAvailableFontsTestActivity.java new file mode 100644 index 000000000000..b9a91490f517 --- /dev/null +++ b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/GetAvailableFontsTestActivity.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.emojirenderingtestapp; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + +import android.app.Activity; +import android.graphics.fonts.Font; +import android.graphics.fonts.SystemFonts; +import android.os.Bundle; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class GetAvailableFontsTestActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + String emojiFontPath = "<Not found>"; + for (Font font : SystemFonts.getAvailableFonts()) { + // Calls font attribute getters to make sure that they don't open font files. + font.getAxes(); + font.getFile(); + font.getLocaleList(); + font.getStyle(); + font.getTtcIndex(); + if ("NotoColorEmoji.ttf".equals(font.getFile().getName())) { + emojiFontPath = font.getFile().getAbsolutePath(); + } + } + LinearLayout container = new LinearLayout(this); + container.setOrientation(LinearLayout.VERTICAL); + TextView textView = new TextView(this); + textView.setText(emojiFontPath); + container.addView(textView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); + setContentView(container); + } +} diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java index 80b0dfeb8804..6bd07d0a84fd 100644 --- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java +++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java @@ -40,6 +40,9 @@ import android.util.Pair; import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; import com.android.compatibility.common.util.StreamUtil; import com.android.compatibility.common.util.SystemUtil; @@ -101,6 +104,9 @@ public class UpdatableSystemFontTest { EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity"; private static final long ACTIVITY_TIMEOUT_MILLIS = SECONDS.toMillis(10); + private static final String GET_AVAILABLE_FONTS_TEST_ACTIVITY = + EMOJI_RENDERING_TEST_APP_ID + "/.GetAvailableFontsTestActivity"; + private static final Pattern PATTERN_FONT_FILES = Pattern.compile("\\.(ttf|otf|ttc|otc)$"); private static final Pattern PATTERN_TMP_FILES = Pattern.compile("^/data/local/tmp/"); private static final Pattern PATTERN_DATA_FONT_FILES = Pattern.compile("^/data/fonts/files/"); @@ -109,6 +115,7 @@ public class UpdatableSystemFontTest { private String mKeyId; private FontManager mFontManager; + private UiDevice mUiDevice; @Before public void setUp() throws Exception { @@ -120,6 +127,7 @@ public class UpdatableSystemFontTest { mKeyId = insertCert(CERT_PATH); mFontManager = context.getSystemService(FontManager.class); expectCommandToSucceed("cmd font clear"); + mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); } @After @@ -287,6 +295,19 @@ public class UpdatableSystemFontTest { assertThat(countMatch(openFiles, patternEmojiVPlus1)).isEqualTo(0); } + @Test + public void getAvailableFonts() throws Exception { + String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME); + startActivity(EMOJI_RENDERING_TEST_APP_ID, GET_AVAILABLE_FONTS_TEST_ACTIVITY); + // GET_AVAILABLE_FONTS_TEST_ACTIVITY shows the NotoColorEmoji path it got. + mUiDevice.wait( + Until.findObject(By.pkg(EMOJI_RENDERING_TEST_APP_ID).text(fontPath)), + ACTIVITY_TIMEOUT_MILLIS); + // The font file should not be opened just by querying the path using + // SystemFont.getAvailableFonts(). + assertThat(isFileOpenedBy(fontPath, EMOJI_RENDERING_TEST_APP_ID)).isFalse(); + } + private static String insertCert(String certPath) throws Exception { Pair<String, String> result; try (InputStream is = new FileInputStream(certPath)) { |