diff options
| author | 2018-09-07 02:19:37 +0000 | |
|---|---|---|
| committer | 2018-09-07 02:19:37 +0000 | |
| commit | dfdc67d9cf899fab05b8bd6dc11b56479033e3de (patch) | |
| tree | d8f1555e970e3699ab1f57efef998bd129a36a4d | |
| parent | 826a36c19b5ca86bf976f66c45c703fdc8db92ee (diff) | |
| parent | 6226c2ff3ca5ae39a01e794f18cc067069d77928 (diff) | |
Merge "Injecting system calls into alarm manager"
| -rw-r--r-- | services/core/java/com/android/server/AlarmManagerService.java | 406 | ||||
| -rw-r--r-- | services/java/com/android/server/SystemServer.java | 3 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/Android.mk | 13 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/AndroidManifest.xml | 5 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/AndroidTest.xml | 1 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java | 374 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/BackgroundRestrictedAlarmsTest.java (renamed from services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java) | 3 |
7 files changed, 630 insertions, 175 deletions
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 26ef42f2b1e7..499c03d05bf2 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -52,6 +52,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.ParcelableException; import android.os.PowerManager; @@ -111,7 +112,7 @@ import java.util.TreeSet; import java.util.function.Predicate; /** - * Alarm manager implementaion. + * Alarm manager implementation. * * Unit test: atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -138,7 +139,6 @@ class AlarmManagerService extends SystemService { static final boolean DEBUG_STANDBY = localLOGV || false; static final boolean RECORD_ALARMS_IN_HISTORY = true; static final boolean RECORD_DEVICE_IDLE_ALARMS = false; - static final int ALARM_EVENT = 1; static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; // Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays @@ -169,7 +169,6 @@ class AlarmManagerService extends SystemService { // List of alarms per uid deferred due to user applied background restrictions on the source app SparseArray<ArrayList<Alarm>> mPendingBackgroundAlarms = new SparseArray<>(); - long mNativeData; private long mNextWakeup; private long mNextNonWakeup; private long mNextWakeUpSetAt; @@ -181,15 +180,14 @@ class AlarmManagerService extends SystemService { private long mLastTickReceived; private long mLastTickAdded; private long mLastTickRemoved; + private final Injector mInjector; int mBroadcastRefCount = 0; PowerManager.WakeLock mWakeLock; boolean mLastWakeLockUnimportantForLogging; ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>(); ArrayList<InFlight> mInFlight = new ArrayList<>(); - final AlarmHandler mHandler = new AlarmHandler(); + AlarmHandler mHandler; ClockReceiver mClockReceiver; - InteractiveStateReceiver mInteractiveStateReceiver; - private UninstallReceiver mUninstallReceiver; final DeliveryTracker mDeliveryTracker = new DeliveryTracker(); PendingIntent mTimeTickSender; PendingIntent mDateChangeSender; @@ -277,7 +275,8 @@ class AlarmManagerService extends SystemService { * global Settings. Any access to this class or its fields should be done while * holding the AlarmManagerService.mLock lock. */ - private final class Constants extends ContentObserver { + @VisibleForTesting + final class Constants extends ContentObserver { // Key names stored in the settings value. private static final String KEY_MIN_FUTURITY = "min_futurity"; private static final String KEY_MIN_INTERVAL = "min_interval"; @@ -456,7 +455,7 @@ class AlarmManagerService extends SystemService { } } - final Constants mConstants; + Constants mConstants; // Alarm delivery ordering bookkeeping static final int PRIO_TICK = 0; @@ -510,7 +509,7 @@ class AlarmManagerService extends SystemService { flags = seed.flags; alarms.add(seed); if (seed.operation == mTimeTickSender) { - mLastTickAdded = System.currentTimeMillis(); + mLastTickAdded = mInjector.getCurrentTimeMillis(); } } @@ -535,7 +534,7 @@ class AlarmManagerService extends SystemService { } alarms.add(index, alarm); if (alarm.operation == mTimeTickSender) { - mLastTickAdded = System.currentTimeMillis(); + mLastTickAdded = mInjector.getCurrentTimeMillis(); } if (DEBUG_BATCH) { Slog.v(TAG, "Adding " + alarm + " to " + this); @@ -573,7 +572,7 @@ class AlarmManagerService extends SystemService { mNextAlarmClockMayChange = true; } if (alarm.operation == mTimeTickSender) { - mLastTickRemoved = System.currentTimeMillis(); + mLastTickRemoved = mInjector.getCurrentTimeMillis(); } } else { if (alarm.whenElapsed > newStart) { @@ -734,17 +733,20 @@ class AlarmManagerService extends SystemService { Alarm mNextWakeFromIdle = null; ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>(); - public AlarmManagerService(Context context) { + @VisibleForTesting + AlarmManagerService(Context context, Injector injector) { super(context); - mConstants = new Constants(mHandler); + mInjector = injector; + } - publishLocalService(AlarmManagerInternal.class, new LocalService()); + AlarmManagerService(Context context) { + this(context, new Injector(context)); } - static long convertToElapsed(long when, int type) { + private long convertToElapsed(long when, int type) { final boolean isRtc = (type == RTC || type == RTC_WAKEUP); if (isRtc) { - when -= System.currentTimeMillis() - SystemClock.elapsedRealtime(); + when -= mInjector.getCurrentTimeMillis() - mInjector.getElapsedRealtime(); } return when; } @@ -854,7 +856,7 @@ class AlarmManagerService extends SystemService { ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone(); mAlarmBatches.clear(); Alarm oldPendingIdleUntil = mPendingIdleUntil; - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtime(); final int oldBatches = oldSet.size(); for (int batchNum = 0; batchNum < oldBatches; batchNum++) { Batch batch = oldSet.get(batchNum); @@ -984,7 +986,7 @@ class AlarmManagerService extends SystemService { alarmsToDeliver = alarmsForUid; mPendingBackgroundAlarms.remove(uid); } - deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime()); + deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime()); } /** @@ -1001,7 +1003,7 @@ class AlarmManagerService extends SystemService { mPendingBackgroundAlarms, alarmsToDeliver, this::isBackgroundRestricted); if (alarmsToDeliver.size() > 0) { - deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime()); + deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime()); } } @@ -1088,7 +1090,7 @@ class AlarmManagerService extends SystemService { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = 0; ent.pkg = "FINISH IDLE"; - ent.elapsedRealtime = SystemClock.elapsedRealtime(); + ent.elapsedRealtime = mInjector.getElapsedRealtime(); mAllowWhileIdleDispatches.add(ent); } @@ -1096,7 +1098,7 @@ class AlarmManagerService extends SystemService { if (mPendingWhileIdleAlarms.size() > 0) { ArrayList<Alarm> alarms = mPendingWhileIdleAlarms; mPendingWhileIdleAlarms = new ArrayList<>(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtime(); for (int i=alarms.size() - 1; i >= 0; i--) { Alarm a = alarms.get(i); reAddAlarmLocked(a, nowElapsed, false); @@ -1282,121 +1284,114 @@ class AlarmManagerService extends SystemService { @Override public void onStart() { - mNativeData = init(); - mNextWakeup = mNextNonWakeup = 0; + mInjector.init(); + + synchronized (mLock) { + mHandler = new AlarmHandler(Looper.myLooper()); + mConstants = new Constants(mHandler); - // We have to set current TimeZone info to kernel - // because kernel doesn't keep this after reboot - setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY)); + mNextWakeup = mNextNonWakeup = 0; - // Also sure that we're booting with a halfway sensible current time - if (mNativeData != 0) { + // We have to set current TimeZone info to kernel + // because kernel doesn't keep this after reboot + setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY)); + + // Also sure that we're booting with a halfway sensible current time final long systemBuildTime = Environment.getRootDirectory().lastModified(); - if (System.currentTimeMillis() < systemBuildTime) { - Slog.i(TAG, "Current time only " + System.currentTimeMillis() + if (mInjector.getCurrentTimeMillis() < systemBuildTime) { + Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis() + ", advancing to build time " + systemBuildTime); - setKernelTime(mNativeData, systemBuildTime); + mInjector.setKernelTime(systemBuildTime); } - } - // Determine SysUI's uid - final PackageManager packMan = getContext().getPackageManager(); - try { - PermissionInfo sysUiPerm = packMan.getPermissionInfo(SYSTEM_UI_SELF_PERMISSION, 0); - ApplicationInfo sysUi = packMan.getApplicationInfo(sysUiPerm.packageName, 0); - if ((sysUi.privateFlags&ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { - mSystemUiUid = sysUi.uid; + // Determine SysUI's uid + mSystemUiUid = mInjector.getSystemUiUid(); + if (mSystemUiUid <= 0) { + Slog.wtf(TAG, "SysUI package not found!"); + } + mWakeLock = mInjector.getAlarmWakeLock(); + + mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0, + new Intent(Intent.ACTION_TIME_TICK).addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0, + UserHandle.ALL); + Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); + mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent, + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL); + + mClockReceiver = mInjector.getClockReceiver(this); + new InteractiveStateReceiver(); + new UninstallReceiver(); + + if (mInjector.isAlarmDriverPresent()) { + AlarmThread waitThread = new AlarmThread(); + waitThread.start(); } else { - Slog.e(TAG, "SysUI permission " + SYSTEM_UI_SELF_PERMISSION - + " defined by non-privileged app " + sysUi.packageName - + " - ignoring"); - } - } catch (NameNotFoundException e) { - } - - if (mSystemUiUid <= 0) { - Slog.wtf(TAG, "SysUI package not found!"); - } - - PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*"); - - mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0, - new Intent(Intent.ACTION_TIME_TICK).addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND - | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0, - UserHandle.ALL); - Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING - | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); - mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent, - Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL); - - // now that we have initied the driver schedule the alarm - mClockReceiver = new ClockReceiver(); - mClockReceiver.scheduleTimeTickEvent(); - mClockReceiver.scheduleDateChangedEvent(); - mInteractiveStateReceiver = new InteractiveStateReceiver(); - mUninstallReceiver = new UninstallReceiver(); - - if (mNativeData != 0) { - AlarmThread waitThread = new AlarmThread(); - waitThread.start(); - } else { - Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler."); - } + Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler."); + } - try { - ActivityManager.getService().registerUidObserver(new UidObserver(), - ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE - | ActivityManager.UID_OBSERVER_ACTIVE, - ActivityManager.PROCESS_STATE_UNKNOWN, null); - } catch (RemoteException e) { - // ignored; both services live in system_server + try { + ActivityManager.getService().registerUidObserver(new UidObserver(), + ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE + | ActivityManager.UID_OBSERVER_ACTIVE, + ActivityManager.PROCESS_STATE_UNKNOWN, null); + } catch (RemoteException e) { + // ignored; both services live in system_server + } } + publishLocalService(AlarmManagerInternal.class, new LocalService()); publishBinderService(Context.ALARM_SERVICE, mService); } @Override public void onBootPhase(int phase) { if (phase == PHASE_SYSTEM_SERVICES_READY) { - mConstants.start(getContext().getContentResolver()); - mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); - mLocalDeviceIdleController - = LocalServices.getService(DeviceIdleController.LocalService.class); - mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); - mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker()); + synchronized (mLock) { + mConstants.start(getContext().getContentResolver()); + mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); + mLocalDeviceIdleController = + LocalServices.getService(DeviceIdleController.LocalService.class); + mUsageStatsManagerInternal = + LocalServices.getService(UsageStatsManagerInternal.class); + mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker()); + + mAppStateTracker = LocalServices.getService(AppStateTracker.class); + mAppStateTracker.addListener(mForceAppStandbyListener); - mAppStateTracker = LocalServices.getService(AppStateTracker.class); - mAppStateTracker.addListener(mForceAppStandbyListener); + mClockReceiver.scheduleTimeTickEvent(); + mClockReceiver.scheduleDateChangedEvent(); + } } } @Override protected void finalize() throws Throwable { try { - close(mNativeData); + mInjector.close(); } finally { super.finalize(); } } boolean setTimeImpl(long millis) { - if (mNativeData == 0) { + if (!mInjector.isAlarmDriverPresent()) { Slog.w(TAG, "Not setting time since no alarm driver is available."); return false; } synchronized (mLock) { - final long currentTimeMillis = System.currentTimeMillis(); - setKernelTime(mNativeData, millis); + final long currentTimeMillis = mInjector.getCurrentTimeMillis(); + mInjector.setKernelTime(millis); final TimeZone timeZone = TimeZone.getDefault(); final int currentTzOffset = timeZone.getOffset(currentTimeMillis); final int newTzOffset = timeZone.getOffset(millis); if (currentTzOffset != newTzOffset) { Slog.i(TAG, "Timezone offset has changed, updating kernel timezone"); - setKernelTimezone(mNativeData, -(newTzOffset / 60000)); + mInjector.setKernelTimezone(-(newTzOffset / 60000)); } // The native implementation of setKernelTime can return -1 even when the kernel // time was set correctly, so assume setting kernel time was successful and always @@ -1426,8 +1421,8 @@ class AlarmManagerService extends SystemService { // Update the kernel timezone information // Kernel tracks time offsets as 'minutes west of GMT' - int gmtOffset = zone.getOffset(System.currentTimeMillis()); - setKernelTimezone(mNativeData, -(gmtOffset / 60000)); + int gmtOffset = zone.getOffset(mInjector.getCurrentTimeMillis()); + mInjector.setKernelTimezone(-(gmtOffset / 60000)); } TimeZone.setDefault(null); @@ -1498,7 +1493,7 @@ class AlarmManagerService extends SystemService { triggerAtTime = 0; } - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtime(); final long nominalTrigger = convertToElapsed(triggerAtTime, type); // Try to prevent spamming by making sure we aren't firing alarms in the immediate future final long minTrigger = nowElapsed + mConstants.MIN_FUTURITY; @@ -1551,7 +1546,8 @@ class AlarmManagerService extends SystemService { * Return the minimum time that should elapse before an app in the specified bucket * can receive alarms again */ - private long getMinDelayForBucketLocked(int bucket) { + @VisibleForTesting + long getMinDelayForBucketLocked(int bucket) { // UsageStats bucket values are treated as floors of their behavioral range. // In other words, a bucket value between WORKING and ACTIVE is treated as // WORKING, not as ACTIVE. The ACTIVE and NEVER bucket apply only at specific @@ -1591,7 +1587,7 @@ class AlarmManagerService extends SystemService { final String sourcePackage = alarm.sourcePackage; final int sourceUserId = UserHandle.getUserId(alarm.creatorUid); final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket( - sourcePackage, sourceUserId, SystemClock.elapsedRealtime()); + sourcePackage, sourceUserId, mInjector.getElapsedRealtime()); final Pair<String, Integer> packageUser = Pair.create(sourcePackage, sourceUserId); final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L); @@ -1619,7 +1615,7 @@ class AlarmManagerService extends SystemService { a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed; } // Add fuzz to make the alarm go off some time before the actual desired time. - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtime(); final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed); if (fuzz > 0) { if (mRandom == null) { @@ -1655,7 +1651,7 @@ class AlarmManagerService extends SystemService { ent.pkg = a.operation.getCreatorPackage(); ent.tag = a.operation.getTag(""); ent.op = "SET"; - ent.elapsedRealtime = SystemClock.elapsedRealtime(); + ent.elapsedRealtime = mInjector.getElapsedRealtime(); ent.argRealtime = a.whenElapsed; mAllowWhileIdleDispatches.add(ent); } @@ -1675,7 +1671,7 @@ class AlarmManagerService extends SystemService { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = 0; ent.pkg = "START IDLE"; - ent.elapsedRealtime = SystemClock.elapsedRealtime(); + ent.elapsedRealtime = mInjector.getElapsedRealtime(); mAllowWhileIdleDispatches.add(ent); } } @@ -1890,8 +1886,8 @@ class AlarmManagerService extends SystemService { pw.println(" App Standby Parole: " + mAppStandbyParole); pw.println(); - final long nowRTC = System.currentTimeMillis(); - final long nowELAPSED = SystemClock.elapsedRealtime(); + final long nowRTC = mInjector.getCurrentTimeMillis(); + final long nowELAPSED = mInjector.getElapsedRealtime(); final long nowUPTIME = SystemClock.uptimeMillis(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @@ -1958,10 +1954,10 @@ class AlarmManagerService extends SystemService { pw.println(); pw.print(" Next kernel non-wakeup alarm: "); - TimeUtils.formatDuration(getNextAlarm(mNativeData, ELAPSED_REALTIME), pw); + TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME), pw); pw.println(); pw.print(" Next kernel wakeup alarm: "); - TimeUtils.formatDuration(getNextAlarm(mNativeData, ELAPSED_REALTIME_WAKEUP), pw); + TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME_WAKEUP), pw); pw.println(); pw.print(" Last wakeup: "); TimeUtils.formatDuration(mLastWakeup, nowELAPSED, pw); @@ -2257,8 +2253,8 @@ class AlarmManagerService extends SystemService { final ProtoOutputStream proto = new ProtoOutputStream(fd); synchronized (mLock) { - final long nowRTC = System.currentTimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowRTC = mInjector.getCurrentTimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtime(); proto.write(AlarmManagerServiceDumpProto.CURRENT_TIME, nowRTC); proto.write(AlarmManagerServiceDumpProto.ELAPSED_REALTIME, nowElapsed); proto.write(AlarmManagerServiceDumpProto.LAST_TIME_CHANGE_CLOCK_TIME, @@ -2494,8 +2490,8 @@ class AlarmManagerService extends SystemService { private void logBatchesLocked(SimpleDateFormat sdf) { ByteArrayOutputStream bs = new ByteArrayOutputStream(2048); PrintWriter pw = new PrintWriter(bs); - final long nowRTC = System.currentTimeMillis(); - final long nowELAPSED = SystemClock.elapsedRealtime(); + final long nowRTC = mInjector.getCurrentTimeMillis(); + final long nowELAPSED = mInjector.getElapsedRealtime(); final int NZ = mAlarmBatches.size(); for (int iz = 0; iz < NZ; iz++) { Batch bz = mAlarmBatches.get(iz); @@ -2693,7 +2689,7 @@ class AlarmManagerService extends SystemService { TimeUtils.formatDuration(mNextNonWakeUpSetAt - nowElapsed, errorMsg); errorMsg.append(", mLastWakeup="); TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg); - errorMsg.append(", timerfd_gettime=" + getNextAlarm(mNativeData, ELAPSED_REALTIME)); + errorMsg.append(", timerfd_gettime=" + mInjector.getNextAlarm(ELAPSED_REALTIME)); errorMsg.append("];"); } if (mNextWakeup < (nowElapsed - 10_000) && mLastWakeup < mNextWakeup) { @@ -2705,7 +2701,7 @@ class AlarmManagerService extends SystemService { errorMsg.append(", mLastWakeup="); TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg); errorMsg.append(", timerfd_gettime=" - + getNextAlarm(mNativeData, ELAPSED_REALTIME_WAKEUP)); + + mInjector.getNextAlarm(ELAPSED_REALTIME_WAKEUP)); errorMsg.append("];"); } if (stuck) { @@ -2716,7 +2712,7 @@ class AlarmManagerService extends SystemService { void rescheduleKernelAlarmsLocked() { // Schedule the next upcoming wakeup alarm. If there is a deliverable batch // prior to that which contains no wakeups, we schedule that as well. - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtime(); validateLastAlarmExpiredLocked(nowElapsed); long nextNonWakeup = 0; if (mAlarmBatches.size() > 0) { @@ -2743,7 +2739,7 @@ class AlarmManagerService extends SystemService { } } - private void removeLocked(PendingIntent operation, IAlarmListener directReceiver) { + void removeLocked(PendingIntent operation, IAlarmListener directReceiver) { if (operation == null && directReceiver == null) { if (localLOGV) { Slog.w(TAG, "requested remove() of null operation", @@ -2983,7 +2979,7 @@ class AlarmManagerService extends SystemService { void interactiveStateChangedLocked(boolean interactive) { if (mInteractive != interactive) { mInteractive = interactive; - final long nowELAPSED = SystemClock.elapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtime(); if (interactive) { if (mPendingNonWakeupAlarms.size() > 0) { final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime; @@ -3023,31 +3019,13 @@ class AlarmManagerService extends SystemService { } private void setLocked(int type, long when) { - if (mNativeData != 0) { - // The kernel never triggers alarms with negative wakeup times - // so we ensure they are positive. - long alarmSeconds, alarmNanoseconds; - if (when < 0) { - alarmSeconds = 0; - alarmNanoseconds = 0; - } else { - alarmSeconds = when / 1000; - alarmNanoseconds = (when % 1000) * 1000 * 1000; - } - - final int result = set(mNativeData, type, alarmSeconds, alarmNanoseconds); - if (result != 0) { - final long nowElapsed = SystemClock.elapsedRealtime(); - Slog.wtf(TAG, "Unable to set kernel alarm, now=" + nowElapsed - + " type=" + type + " when=" + when - + " @ (" + alarmSeconds + "," + alarmNanoseconds - + "), ret = " + result + " = " + Os.strerror(result)); - } + if (mInjector.isAlarmDriverPresent()) { + mInjector.setAlarm(type, when); } else { Message msg = Message.obtain(); - msg.what = ALARM_EVENT; + msg.what = AlarmHandler.ALARM_EVENT; - mHandler.removeMessages(ALARM_EVENT); + mHandler.removeMessages(msg.what); mHandler.sendMessageAtTime(msg, when); } } @@ -3106,13 +3084,13 @@ class AlarmManagerService extends SystemService { exemptOnBatterySaver); } - private native long init(); - private native void close(long nativeData); - private native int set(long nativeData, int type, long seconds, long nanoseconds); - private native int waitForAlarm(long nativeData); - private native int setKernelTime(long nativeData, long millis); - private native int setKernelTimezone(long nativeData, int minuteswest); - private native long getNextAlarm(long nativeData, int type); + private static native long init(); + private static native void close(long nativeData); + private static native int set(long nativeData, int type, long seconds, long nanoseconds); + private static native int waitForAlarm(long nativeData); + private static native int setKernelTime(long nativeData, long millis); + private static native int setKernelTimezone(long nativeData, int minuteswest); + private static native long getNextAlarm(long nativeData, int type); private long getWhileIdleMinIntervalLocked(int uid) { final boolean dozing = mPendingIdleUntil != null; @@ -3516,6 +3494,103 @@ class AlarmManagerService extends SystemService { || (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0; } + @VisibleForTesting + static class Injector { + private long mNativeData; + private Context mContext; + + Injector(Context context) { + mContext = context; + } + + void init() { + mNativeData = AlarmManagerService.init(); + } + + int waitForAlarm() { + return AlarmManagerService.waitForAlarm(mNativeData); + } + + boolean isAlarmDriverPresent() { + return mNativeData != 0; + } + + void setAlarm(int type, long millis) { + // The kernel never triggers alarms with negative wakeup times + // so we ensure they are positive. + final long alarmSeconds, alarmNanoseconds; + if (millis < 0) { + alarmSeconds = 0; + alarmNanoseconds = 0; + } else { + alarmSeconds = millis / 1000; + alarmNanoseconds = (millis % 1000) * 1000 * 1000; + } + + final int result = AlarmManagerService.set(mNativeData, type, alarmSeconds, + alarmNanoseconds); + if (result != 0) { + final long nowElapsed = SystemClock.elapsedRealtime(); + Slog.wtf(TAG, "Unable to set kernel alarm, now=" + nowElapsed + + " type=" + type + " @ (" + alarmSeconds + "," + alarmNanoseconds + + "), ret = " + result + " = " + Os.strerror(result)); + } + } + + long getNextAlarm(int type) { + return AlarmManagerService.getNextAlarm(mNativeData, type); + } + + void setKernelTimezone(int minutesWest) { + AlarmManagerService.setKernelTimezone(mNativeData, minutesWest); + } + + void setKernelTime(long millis) { + if (mNativeData != 0) { + AlarmManagerService.setKernelTime(mNativeData, millis); + } + } + + void close() { + AlarmManagerService.close(mNativeData); + } + + long getElapsedRealtime() { + return SystemClock.elapsedRealtime(); + } + + long getCurrentTimeMillis() { + return System.currentTimeMillis(); + } + + PowerManager.WakeLock getAlarmWakeLock() { + final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + return pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*"); + } + + int getSystemUiUid() { + int sysUiUid = -1; + final PackageManager pm = mContext.getPackageManager(); + try { + PermissionInfo sysUiPerm = pm.getPermissionInfo(SYSTEM_UI_SELF_PERMISSION, 0); + ApplicationInfo sysUi = pm.getApplicationInfo(sysUiPerm.packageName, 0); + if ((sysUi.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { + sysUiUid = sysUi.uid; + } else { + Slog.e(TAG, "SysUI permission " + SYSTEM_UI_SELF_PERMISSION + + " defined by non-privileged app " + sysUi.packageName + + " - ignoring"); + } + } catch (NameNotFoundException e) { + } + return sysUiUid; + } + + ClockReceiver getClockReceiver(AlarmManagerService service) { + return service.new ClockReceiver(); + } + } + private class AlarmThread extends Thread { private int mFalseWakeups; @@ -3524,7 +3599,7 @@ class AlarmManagerService extends SystemService { { super("AlarmManager"); mFalseWakeups = 0; - mWtfThreshold = 10; + mWtfThreshold = 100; } public void run() @@ -3533,14 +3608,16 @@ class AlarmManagerService extends SystemService { while (true) { - int result = waitForAlarm(mNativeData); - - final long nowRTC = System.currentTimeMillis(); - final long nowELAPSED = SystemClock.elapsedRealtime(); + int result = mInjector.waitForAlarm(); + final long nowRTC = mInjector.getCurrentTimeMillis(); + final long nowELAPSED = mInjector.getElapsedRealtime(); synchronized (mLock) { mLastWakeup = nowELAPSED; } - + if (result == 0) { + Slog.wtf(TAG, "waitForAlarm returned 0, nowRTC = " + nowRTC + + ", nowElapsed = " + nowELAPSED); + } triggerList.clear(); if ((result & TIME_CHANGED_MASK) != 0) { @@ -3720,7 +3797,8 @@ class AlarmManagerService extends SystemService { public static final int APP_STANDBY_PAROLE_CHANGED = 6; public static final int REMOVE_FOR_STOPPED = 7; - public AlarmHandler() { + AlarmHandler(Looper looper) { + super(looper); } public void postRemoveForStopped(int uid) { @@ -3732,8 +3810,8 @@ class AlarmManagerService extends SystemService { case ALARM_EVENT: { ArrayList<Alarm> triggerList = new ArrayList<Alarm>(); synchronized (mLock) { - final long nowRTC = System.currentTimeMillis(); - final long nowELAPSED = SystemClock.elapsedRealtime(); + final long nowRTC = mInjector.getCurrentTimeMillis(); + final long nowELAPSED = mInjector.getElapsedRealtime(); triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC); updateNextAlarmClockLocked(); } @@ -3817,7 +3895,7 @@ class AlarmManagerService extends SystemService { Slog.v(TAG, "Received TIME_TICK alarm; rescheduling"); } synchronized (mLock) { - mLastTickReceived = System.currentTimeMillis(); + mLastTickReceived = mInjector.getCurrentTimeMillis(); } scheduleTimeTickEvent(); } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) { @@ -3826,14 +3904,14 @@ class AlarmManagerService extends SystemService { // based off of the current Zone gmt offset + userspace tracked // daylight savings information. TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY)); - int gmtOffset = zone.getOffset(System.currentTimeMillis()); - setKernelTimezone(mNativeData, -(gmtOffset / 60000)); + int gmtOffset = zone.getOffset(mInjector.getCurrentTimeMillis()); + mInjector.setKernelTimezone(-(gmtOffset / 60000)); scheduleDateChangedEvent(); } } public void scheduleTimeTickEvent() { - final long currentTime = System.currentTimeMillis(); + final long currentTime = mInjector.getCurrentTimeMillis(); final long nextTime = 60000 * ((currentTime / 60000) + 1); // Schedule this event for the amount of time that it would take to get to @@ -3841,7 +3919,7 @@ class AlarmManagerService extends SystemService { final long tickEventDelay = nextTime - currentTime; final WorkSource workSource = null; // Let system take blame for time tick events. - setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0, + setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0, 0, mTimeTickSender, null, null, AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid(), "android"); @@ -3853,7 +3931,7 @@ class AlarmManagerService extends SystemService { public void scheduleDateChangedEvent() { Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(System.currentTimeMillis()); + calendar.setTimeInMillis(mInjector.getCurrentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); @@ -4122,7 +4200,7 @@ class AlarmManagerService extends SystemService { } private void updateStatsLocked(InFlight inflight) { - final long nowELAPSED = SystemClock.elapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtime(); BroadcastStats bs = inflight.mBroadcastStats; bs.nesting--; if (bs.nesting <= 0) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b9f8fdbc7674..0b6a33f8c013 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -883,7 +883,8 @@ public final class SystemServer { } traceBeginAndSlog("StartAlarmManagerService"); - mSystemServiceManager.startService(AlarmManagerService.class); + mSystemServiceManager.startService(new AlarmManagerService(context)); + traceEnd(); traceBeginAndSlog("InitWatchdog"); diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk index 34de9dfb7ad6..8c0283318419 100644 --- a/services/tests/mockingservicestests/Android.mk +++ b/services/tests/mockingservicestests/Android.mk @@ -20,22 +20,21 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_STATIC_JAVA_LIBRARIES := \ - services.core \ - services.devicepolicy \ frameworks-base-testutils \ + services.core \ androidx-test \ mockito-target-extended-minus-junit4 \ + platform-test-annotations \ ShortcutManagerTestUtils \ - compatibility-device-util \ - truth-prebuilt + truth-prebuilt \ -LOCAL_JAVA_LIBRARIES := \ - android.test.mock +LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner LOCAL_JNI_SHARED_LIBRARIES := \ libdexmakerjvmtiagent \ - libstaticjvmtiagent + libstaticjvmtiagent \ +LOCAL_CERTIFICATE := platform LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_COMPATIBILITY_SUITE := device-tests diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index 247e446d1bfc..c9aa63153a5d 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -17,7 +17,10 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.mockingservicestests"> - <application android:debuggable="true"> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + + <application android:testOnly="true" + android:debuggable="true"> <uses-library android:name="android.test.runner" /> </application> diff --git a/services/tests/mockingservicestests/AndroidTest.xml b/services/tests/mockingservicestests/AndroidTest.xml index adfee96d4e28..7782d570856f 100644 --- a/services/tests/mockingservicestests/AndroidTest.xml +++ b/services/tests/mockingservicestests/AndroidTest.xml @@ -19,6 +19,7 @@ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> <option name="test-file-name" value="FrameworksMockingServicesTests.apk" /> </target_preparer> diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java new file mode 100644 index 000000000000..de3d285cd23a --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server; + +import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; + +import android.app.ActivityManager; +import android.app.AlarmManager; +import android.app.IActivityManager; +import android.app.IUidObserver; +import android.app.PendingIntent; +import android.app.usage.UsageStatsManagerInternal; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoSession; + +import javax.annotation.concurrent.GuardedBy; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AlarmManagerServiceTest { + private static final String TAG = AlarmManagerServiceTest.class.getSimpleName(); + private static final String TEST_CALLING_PACKAGE = "com.android.framework.test-package"; + private static final int SYSTEM_UI_UID = 123456789; + private static final int TEST_CALLING_UID = 12345; + private static final long DEFAULT_TIMEOUT = 5_000; + + private AlarmManagerService mService; + @Mock + private IActivityManager mIActivityManager; + @Mock + private UsageStatsManagerInternal mUsageStatsManagerInternal; + @Mock + private AppStateTracker mAppStateTracker; + @Mock + private AlarmManagerService.ClockReceiver mClockReceiver; + @Mock + private PowerManager.WakeLock mWakeLock; + + private MockitoSession mMockingSession; + private Injector mInjector; + private volatile long mNowElapsedTest; + @GuardedBy("mTestTimer") + private TestTimer mTestTimer = new TestTimer(); + + static class TestTimer { + private long mElapsed; + boolean mExpired; + + synchronized long getElapsed() { + return mElapsed; + } + + synchronized void set(long millisElapsed) { + mElapsed = millisElapsed; + } + + synchronized long expire() { + mExpired = true; + notify(); + return mElapsed; + } + } + + public class Injector extends AlarmManagerService.Injector { + Injector(Context context) { + super(context); + } + + @Override + void init() { + // Do nothing. + } + + @Override + int waitForAlarm() { + synchronized (mTestTimer) { + if (!mTestTimer.mExpired) { + try { + mTestTimer.wait(); + } catch (InterruptedException ie) { + Log.e(TAG, "Wait interrupted!", ie); + return 0; + } + } + mTestTimer.mExpired = false; + } + return AlarmManagerService.IS_WAKEUP_MASK; // Doesn't matter, just evaluate. + } + + @Override + void setKernelTimezone(int minutesWest) { + // Do nothing. + } + + @Override + void setAlarm(int type, long millis) { + mTestTimer.set(millis); + } + + @Override + void setKernelTime(long millis) { + } + + @Override + int getSystemUiUid() { + return SYSTEM_UI_UID; + } + + @Override + boolean isAlarmDriverPresent() { + // Pretend the driver is present, so code does not fall back to handler + return true; + } + + @Override + long getElapsedRealtime() { + return mNowElapsedTest; + } + + @Override + AlarmManagerService.ClockReceiver getClockReceiver(AlarmManagerService service) { + return mClockReceiver; + } + + @Override + PowerManager.WakeLock getAlarmWakeLock() { + return mWakeLock; + } + } + + @Before + public final void setUp() throws Exception { + mMockingSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class, Answers.CALLS_REAL_METHODS) + .mockStatic(LocalServices.class) + .mockStatic(Looper.class, Answers.CALLS_REAL_METHODS) + .startMocking(); + doReturn(mIActivityManager).when(ActivityManager::getService); + doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class)); + doReturn(null) + .when(() -> LocalServices.getService(DeviceIdleController.LocalService.class)); + doReturn(mUsageStatsManagerInternal).when( + () -> LocalServices.getService(UsageStatsManagerInternal.class)); + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), + eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong())) + .thenReturn(STANDBY_BUCKET_ACTIVE); + doReturn(Looper.getMainLooper()).when(Looper::myLooper); + + final Context context = InstrumentationRegistry.getTargetContext(); + mInjector = spy(new Injector(context)); + mService = new AlarmManagerService(context, mInjector); + spyOn(mService); + doNothing().when(mService).publishBinderService(any(), any()); + mService.onStart(); + mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + mService.mConstants.MIN_FUTURITY = 0; + + assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID); + assertEquals(mService.mClockReceiver, mClockReceiver); + assertEquals(mService.mWakeLock, mWakeLock); + verify(mIActivityManager).registerUidObserver(any(IUidObserver.class), anyInt(), anyInt(), + isNull()); + } + + private void setTestAlarm(int type, long triggerTime, PendingIntent operation) { + mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, 0, + operation, null, "test", AlarmManager.FLAG_STANDALONE, null, null, + TEST_CALLING_UID, TEST_CALLING_PACKAGE); + } + + private PendingIntent getNewMockPendingIntent() { + final PendingIntent mockPi = mock(PendingIntent.class, Answers.RETURNS_DEEP_STUBS); + when(mockPi.getCreatorUid()).thenReturn(TEST_CALLING_UID); + when(mockPi.getCreatorPackage()).thenReturn(TEST_CALLING_PACKAGE); + return mockPi; + } + + @Test + public void testSingleAlarmSet() { + final long triggerTime = mNowElapsedTest + 5000; + final PendingIntent alarmPi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi); + verify(mInjector).setAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime); + assertEquals(triggerTime, mTestTimer.getElapsed()); + } + + @Test + public void testSingleAlarmExpiration() throws Exception { + final long triggerTime = mNowElapsedTest + 5000; + final PendingIntent alarmPi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi); + + mNowElapsedTest = mTestTimer.expire(); + + final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor = + ArgumentCaptor.forClass(PendingIntent.OnFinished.class); + verify(alarmPi, timeout(DEFAULT_TIMEOUT)).send(any(Context.class), eq(0), + any(Intent.class), onFinishedCaptor.capture(), any(Handler.class), isNull(), any()); + verify(mWakeLock, timeout(DEFAULT_TIMEOUT)).acquire(); + onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null); + verify(mWakeLock, timeout(DEFAULT_TIMEOUT)).release(); + } + + @Test + public void testMinFuturity() { + mService.mConstants.MIN_FUTURITY = 10; + final long triggerTime = mNowElapsedTest + 1; + final long expectedTriggerTime = mNowElapsedTest + mService.mConstants.MIN_FUTURITY; + setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, getNewMockPendingIntent()); + verify(mInjector).setAlarm(ELAPSED_REALTIME_WAKEUP, expectedTriggerTime); + } + + @Test + public void testEarliestAlarmSet() { + final PendingIntent pi6 = getNewMockPendingIntent(); + final PendingIntent pi8 = getNewMockPendingIntent(); + final PendingIntent pi9 = getNewMockPendingIntent(); + + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 8, pi8); + assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed()); + + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 9, pi9); + assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed()); + + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, pi6); + assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed()); + + mService.removeLocked(pi6, null); + assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed()); + + mService.removeLocked(pi8, null); + assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed()); + } + + @Test + public void testStandbyBucketDelay_workingSet() throws Exception { + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent()); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent()); + assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed()); + + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), + anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET); + mNowElapsedTest = mTestTimer.expire(); + verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce()) + .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), + eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()); + final long expectedNextTrigger = mNowElapsedTest + + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_WORKING_SET); + assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: " + + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT, + () -> (mTestTimer.getElapsed() == expectedNextTrigger))); + } + + @Test + public void testStandbyBucketDelay_frequent() { + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent()); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent()); + assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed()); + + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), + anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT); + mNowElapsedTest = mTestTimer.expire(); + verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce()) + .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), + eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()); + final long expectedNextTrigger = mNowElapsedTest + + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_FREQUENT); + assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: " + + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT, + () -> (mTestTimer.getElapsed() == expectedNextTrigger))); + } + + @Test + public void testStandbyBucketDelay_rare() { + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent()); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent()); + assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed()); + + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), + anyLong())).thenReturn(STANDBY_BUCKET_RARE); + mNowElapsedTest = mTestTimer.expire(); + verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce()) + .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), + eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()); + final long expectedNextTrigger = mNowElapsedTest + + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_RARE); + assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: " + + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT, + () -> (mTestTimer.getElapsed() == expectedNextTrigger))); + } + + @After + public void tearDown() { + if (mMockingSession != null) { + mMockingSession.finishMocking(); + } + } + + private boolean pollingCheck(long timeout, Condition condition) { + final long deadline = SystemClock.uptimeMillis() + timeout; + boolean interrupted = false; + while (!condition.check() && SystemClock.uptimeMillis() < deadline) { + try { + Thread.sleep(500); + } catch (InterruptedException ie) { + interrupted = true; + } + } + if (interrupted) { + Thread.currentThread().interrupt(); + } + return condition.check(); + } + + @FunctionalInterface + interface Condition { + boolean check(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/BackgroundRestrictedAlarmsTest.java index 1f63d6165617..d248b8902e35 100644 --- a/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/BackgroundRestrictedAlarmsTest.java @@ -35,14 +35,13 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidJUnit4.class) -public class AlarmManagerServiceTest { +public class BackgroundRestrictedAlarmsTest { private SparseArray<ArrayList<Alarm>> addPendingAlarm( SparseArray<ArrayList<Alarm>> all, int uid, String name, boolean removeIt) { ArrayList<Alarm> uidAlarms = all.get(uid); if (uidAlarms == null) { all.put(uid, uidAlarms = new ArrayList<>()); } - // Details don't matter. uidAlarms.add(new Alarm( removeIt ? RTC : RTC_WAKEUP, 0, 0, 0, 0, 0, null, null, null, null, 0, null, uid, name)); |