Merge "Injecting system calls into alarm manager"
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 26ef42f..499c03d 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.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.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 @@
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 @@
// 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 @@
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 @@
* 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 @@
}
}
- final Constants mConstants;
+ Constants mConstants;
// Alarm delivery ordering bookkeeping
static final int PRIO_TICK = 0;
@@ -510,7 +509,7 @@
flags = seed.flags;
alarms.add(seed);
if (seed.operation == mTimeTickSender) {
- mLastTickAdded = System.currentTimeMillis();
+ mLastTickAdded = mInjector.getCurrentTimeMillis();
}
}
@@ -535,7 +534,7 @@
}
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 @@
mNextAlarmClockMayChange = true;
}
if (alarm.operation == mTimeTickSender) {
- mLastTickRemoved = System.currentTimeMillis();
+ mLastTickRemoved = mInjector.getCurrentTimeMillis();
}
} else {
if (alarm.whenElapsed > newStart) {
@@ -734,17 +733,20 @@
Alarm mNextWakeFromIdle = null;
ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>();
- public AlarmManagerService(Context context) {
+ @VisibleForTesting
+ AlarmManagerService(Context context, Injector injector) {
super(context);
- mConstants = new Constants(mHandler);
-
- publishLocalService(AlarmManagerInternal.class, new LocalService());
+ mInjector = injector;
}
- static long convertToElapsed(long when, int type) {
+ AlarmManagerService(Context context) {
+ this(context, new Injector(context));
+ }
+
+ 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 @@
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 @@
alarmsToDeliver = alarmsForUid;
mPendingBackgroundAlarms.remove(uid);
}
- deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime());
+ deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime());
}
/**
@@ -1001,7 +1003,7 @@
mPendingBackgroundAlarms, alarmsToDeliver, this::isBackgroundRestricted);
if (alarmsToDeliver.size() > 0) {
- deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime());
+ deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime());
}
}
@@ -1088,7 +1090,7 @@
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 @@
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 @@
@Override
public void onStart() {
- mNativeData = init();
- mNextWakeup = mNextNonWakeup = 0;
+ mInjector.init();
- // We have to set current TimeZone info to kernel
- // because kernel doesn't keep this after reboot
- setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
+ synchronized (mLock) {
+ mHandler = new AlarmHandler(Looper.myLooper());
+ mConstants = new Constants(mHandler);
- // Also sure that we're booting with a halfway sensible current time
- if (mNativeData != 0) {
+ mNextWakeup = mNextNonWakeup = 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");
+ Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
}
- } catch (NameNotFoundException e) {
+
+ 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
+ }
}
-
- 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.");
- }
-
- 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 @@
// 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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
}
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 @@
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 @@
|| (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 @@
{
super("AlarmManager");
mFalseWakeups = 0;
- mWtfThreshold = 10;
+ mWtfThreshold = 100;
}
public void run()
@@ -3533,14 +3608,16 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
}
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 b9f8fdb..0b6a33f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -883,7 +883,8 @@
}
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 34de9df..8c02833 100644
--- a/services/tests/mockingservicestests/Android.mk
+++ b/services/tests/mockingservicestests/Android.mk
@@ -20,22 +20,21 @@
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 247e446..c9aa631 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 adfee96..7782d57 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 0000000..de3d285
--- /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
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java
rename to services/tests/servicestests/src/com/android/server/BackgroundRestrictedAlarmsTest.java
index 1f63d61..d248b89 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 @@
@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));