1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.alarm; 18 19 import static android.app.AlarmManager.ELAPSED_REALTIME; 20 import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; 21 import static android.app.AlarmManager.RTC; 22 import static android.app.AlarmManager.RTC_WAKEUP; 23 24 import static com.android.server.alarm.AlarmManagerService.PRIORITY_NORMAL; 25 import static com.android.server.alarm.AlarmManagerService.addClampPositive; 26 27 import android.app.AlarmManager; 28 import android.app.IAlarmListener; 29 import android.app.PendingIntent; 30 import android.os.Bundle; 31 import android.os.WorkSource; 32 import android.util.IndentingPrintWriter; 33 import android.util.TimeUtils; 34 import android.util.proto.ProtoOutputStream; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 38 import java.text.SimpleDateFormat; 39 import java.util.Arrays; 40 import java.util.Date; 41 42 /** 43 * Class to describe an alarm that is used to the set the kernel timer that returns when the timer 44 * expires. The timer will wake up the device if the alarm is a "wakeup" alarm. 45 */ 46 class Alarm { 47 @VisibleForTesting 48 public static final int NUM_POLICIES = 4; 49 /** 50 * Index used to store the time the alarm was requested to expire. To be used with 51 * {@link #setPolicyElapsed(int, long)}. 52 */ 53 public static final int REQUESTER_POLICY_INDEX = 0; 54 /** 55 * Index used to store the earliest time the alarm can expire based on app-standby policy. 56 * To be used with {@link #setPolicyElapsed(int, long)}. 57 */ 58 public static final int APP_STANDBY_POLICY_INDEX = 1; 59 /** 60 * Index used to store the earliest time the alarm can expire based on the device's doze policy. 61 * To be used with {@link #setPolicyElapsed(int, long)}. 62 */ 63 public static final int DEVICE_IDLE_POLICY_INDEX = 2; 64 65 /** 66 * Index used to store the earliest time the alarm can expire based on battery saver policy. 67 * To be used with {@link #setPolicyElapsed(int, long)}. 68 */ 69 public static final int BATTERY_SAVER_POLICY_INDEX = 3; 70 71 /** 72 * Reason to use for inexact alarms. 73 */ 74 static final int EXACT_ALLOW_REASON_NOT_APPLICABLE = -1; 75 /** 76 * Caller had SCHEDULE_EXACT_ALARM permission. 77 */ 78 static final int EXACT_ALLOW_REASON_PERMISSION = 0; 79 /** 80 * Caller was in the power allow-list. 81 */ 82 static final int EXACT_ALLOW_REASON_ALLOW_LIST = 1; 83 /** 84 * Change wasn't enable for the caller due to compat reasons. 85 */ 86 static final int EXACT_ALLOW_REASON_COMPAT = 2; 87 /** 88 * Caller had USE_EXACT_ALARM permission. 89 */ 90 static final int EXACT_ALLOW_REASON_POLICY_PERMISSION = 3; 91 /** 92 * Caller used a listener alarm, which does not need permission to be exact. 93 */ 94 static final int EXACT_ALLOW_REASON_LISTENER = 4; 95 /** 96 * Caller used a prioritized alarm, which does not need permission to be exact. 97 */ 98 static final int EXACT_ALLOW_REASON_PRIORITIZED = 5; 99 100 public final int type; 101 /** 102 * The original trigger time supplied by the caller. This can be in the elapsed or rtc time base 103 * depending on the type of this alarm 104 */ 105 public final long origWhen; 106 public final boolean wakeup; 107 public final PendingIntent operation; 108 public final IAlarmListener listener; 109 public final String listenerTag; 110 public final String statsTag; 111 public final WorkSource workSource; 112 public final int flags; 113 public final AlarmManager.AlarmClockInfo alarmClock; 114 public final int uid; 115 public final int creatorUid; 116 public final String packageName; 117 public final String sourcePackage; 118 public final long windowLength; 119 public final long repeatInterval; 120 public int count; 121 /** The earliest time this alarm is eligible to fire according to each policy */ 122 private long[] mPolicyWhenElapsed; 123 /** The ultimate delivery time to be used for this alarm */ 124 private long mWhenElapsed; 125 private long mMaxWhenElapsed; 126 public int exactAllowReason; 127 @AlarmManagerService.DispatchPriority 128 public int priorityClass; 129 /** Broadcast options to use when delivering this alarm */ 130 public Bundle mIdleOptions; 131 public boolean mUsingReserveQuota; 132 Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval, PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags, AlarmManager.AlarmClockInfo info, int uid, String pkgName, Bundle idleOptions, int exactAllowReason)133 Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval, 134 PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags, 135 AlarmManager.AlarmClockInfo info, int uid, String pkgName, Bundle idleOptions, 136 int exactAllowReason) { 137 this.type = type; 138 origWhen = when; 139 wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP 140 || type == AlarmManager.RTC_WAKEUP; 141 mPolicyWhenElapsed = new long[NUM_POLICIES]; 142 mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] = requestedWhenElapsed; 143 mWhenElapsed = requestedWhenElapsed; 144 this.windowLength = windowLength; 145 mMaxWhenElapsed = addClampPositive(requestedWhenElapsed, windowLength); 146 repeatInterval = interval; 147 operation = op; 148 listener = rec; 149 this.listenerTag = listenerTag; 150 statsTag = makeTag(op, listenerTag, type); 151 workSource = ws; 152 this.flags = flags; 153 alarmClock = info; 154 this.uid = uid; 155 packageName = pkgName; 156 mIdleOptions = idleOptions; 157 this.exactAllowReason = exactAllowReason; 158 sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName; 159 creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid; 160 mUsingReserveQuota = false; 161 priorityClass = PRIORITY_NORMAL; 162 } 163 makeTag(PendingIntent pi, String tag, int type)164 public static String makeTag(PendingIntent pi, String tag, int type) { 165 final String alarmString = type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP 166 ? "*walarm*:" : "*alarm*:"; 167 return (pi != null) ? pi.getTag(alarmString) : (alarmString + tag); 168 } 169 170 // Returns true if either matches matches(PendingIntent pi, IAlarmListener rec)171 public boolean matches(PendingIntent pi, IAlarmListener rec) { 172 return (operation != null) 173 ? operation.equals(pi) 174 : rec != null && listener.asBinder().equals(rec.asBinder()); 175 } 176 matches(String packageName)177 public boolean matches(String packageName) { 178 return packageName.equals(sourcePackage); 179 } 180 181 /** 182 * Get the earliest time this alarm is allowed to expire based on the given policy. 183 * 184 * @param policyIndex The index of the policy. One of [{@link #REQUESTER_POLICY_INDEX}, 185 * {@link #APP_STANDBY_POLICY_INDEX}]. 186 */ 187 @VisibleForTesting getPolicyElapsed(int policyIndex)188 long getPolicyElapsed(int policyIndex) { 189 return mPolicyWhenElapsed[policyIndex]; 190 } 191 192 /** 193 * @return the time this alarm was requested to go off in the elapsed time base. 194 */ getRequestedElapsed()195 public long getRequestedElapsed() { 196 return mPolicyWhenElapsed[REQUESTER_POLICY_INDEX]; 197 } 198 199 /** 200 * Get the earliest time that this alarm should be delivered to the requesting app. 201 */ getWhenElapsed()202 public long getWhenElapsed() { 203 return mWhenElapsed; 204 } 205 206 /** 207 * Get the latest time that this alarm should be delivered to the requesting app. Will be equal 208 * to {@link #getWhenElapsed()} in case this is an exact alarm. 209 */ getMaxWhenElapsed()210 public long getMaxWhenElapsed() { 211 return mMaxWhenElapsed; 212 } 213 214 /** 215 * Set the earliest time this alarm can expire based on the passed policy index. 216 * 217 * @return {@code true} if this change resulted in a change in the ultimate delivery time (or 218 * time window in the case of inexact alarms) of this alarm. 219 * @see #getWhenElapsed() 220 * @see #getMaxWhenElapsed() 221 * @see #getPolicyElapsed(int) 222 */ setPolicyElapsed(int policyIndex, long policyElapsed)223 public boolean setPolicyElapsed(int policyIndex, long policyElapsed) { 224 mPolicyWhenElapsed[policyIndex] = policyElapsed; 225 return updateWhenElapsed(); 226 } 227 228 /** 229 * @return {@code true} if either {@link #mWhenElapsed} or {@link #mMaxWhenElapsed} changes 230 * due to this call. 231 */ updateWhenElapsed()232 private boolean updateWhenElapsed() { 233 final long oldWhenElapsed = mWhenElapsed; 234 mWhenElapsed = 0; 235 for (int i = 0; i < NUM_POLICIES; i++) { 236 mWhenElapsed = Math.max(mWhenElapsed, mPolicyWhenElapsed[i]); 237 } 238 239 final long oldMaxWhenElapsed = mMaxWhenElapsed; 240 // windowLength should always be >= 0 here. 241 final long maxRequestedElapsed = addClampPositive( 242 mPolicyWhenElapsed[REQUESTER_POLICY_INDEX], windowLength); 243 mMaxWhenElapsed = Math.max(maxRequestedElapsed, mWhenElapsed); 244 245 return (oldWhenElapsed != mWhenElapsed) || (oldMaxWhenElapsed != mMaxWhenElapsed); 246 } 247 248 @Override toString()249 public String toString() { 250 StringBuilder sb = new StringBuilder(128); 251 sb.append("Alarm{"); 252 sb.append(Integer.toHexString(System.identityHashCode(this))); 253 sb.append(" type "); 254 sb.append(type); 255 sb.append(" origWhen "); 256 sb.append(origWhen); 257 sb.append(" whenElapsed "); 258 sb.append(getWhenElapsed()); 259 sb.append(" "); 260 sb.append(sourcePackage); 261 sb.append('}'); 262 return sb.toString(); 263 } 264 policyIndexToString(int index)265 static String policyIndexToString(int index) { 266 switch (index) { 267 case REQUESTER_POLICY_INDEX: 268 return "requester"; 269 case APP_STANDBY_POLICY_INDEX: 270 return "app_standby"; 271 case DEVICE_IDLE_POLICY_INDEX: 272 return "device_idle"; 273 case BATTERY_SAVER_POLICY_INDEX: 274 return "battery_saver"; 275 default: 276 return "--unknown(" + index + ")--"; 277 } 278 } 279 exactReasonToString(int reason)280 private static String exactReasonToString(int reason) { 281 switch (reason) { 282 case EXACT_ALLOW_REASON_ALLOW_LIST: 283 return "allow-listed"; 284 case EXACT_ALLOW_REASON_COMPAT: 285 return "compat"; 286 case EXACT_ALLOW_REASON_PERMISSION: 287 return "permission"; 288 case EXACT_ALLOW_REASON_POLICY_PERMISSION: 289 return "policy_permission"; 290 case EXACT_ALLOW_REASON_LISTENER: 291 return "listener"; 292 case EXACT_ALLOW_REASON_PRIORITIZED: 293 return "prioritized"; 294 case EXACT_ALLOW_REASON_NOT_APPLICABLE: 295 return "N/A"; 296 default: 297 return "--unknown--"; 298 } 299 } 300 typeToString(int type)301 public static String typeToString(int type) { 302 switch (type) { 303 case RTC: 304 return "RTC"; 305 case RTC_WAKEUP: 306 return "RTC_WAKEUP"; 307 case ELAPSED_REALTIME: 308 return "ELAPSED"; 309 case ELAPSED_REALTIME_WAKEUP: 310 return "ELAPSED_WAKEUP"; 311 default: 312 return "--unknown--"; 313 } 314 } 315 dump(IndentingPrintWriter ipw, long nowELAPSED, SimpleDateFormat sdf)316 public void dump(IndentingPrintWriter ipw, long nowELAPSED, SimpleDateFormat sdf) { 317 final boolean isRtc = (type == RTC || type == RTC_WAKEUP); 318 ipw.print("tag="); 319 ipw.println(statsTag); 320 321 ipw.print("type="); 322 ipw.print(typeToString(type)); 323 ipw.print(" origWhen="); 324 if (isRtc) { 325 ipw.print(sdf.format(new Date(origWhen))); 326 } else { 327 TimeUtils.formatDuration(origWhen, nowELAPSED, ipw); 328 } 329 ipw.print(" window="); 330 TimeUtils.formatDuration(windowLength, ipw); 331 if (exactAllowReason != EXACT_ALLOW_REASON_NOT_APPLICABLE) { 332 ipw.print(" exactAllowReason="); 333 ipw.print(exactReasonToString(exactAllowReason)); 334 } 335 ipw.print(" repeatInterval="); 336 ipw.print(repeatInterval); 337 ipw.print(" count="); 338 ipw.print(count); 339 ipw.print(" flags=0x"); 340 ipw.println(Integer.toHexString(flags)); 341 342 ipw.print("policyWhenElapsed:"); 343 for (int i = 0; i < NUM_POLICIES; i++) { 344 ipw.print(" " + policyIndexToString(i) + "="); 345 TimeUtils.formatDuration(mPolicyWhenElapsed[i], nowELAPSED, ipw); 346 } 347 ipw.println(); 348 349 ipw.print("whenElapsed="); 350 TimeUtils.formatDuration(getWhenElapsed(), nowELAPSED, ipw); 351 ipw.print(" maxWhenElapsed="); 352 TimeUtils.formatDuration(mMaxWhenElapsed, nowELAPSED, ipw); 353 if (mUsingReserveQuota) { 354 ipw.print(" usingReserveQuota=true"); 355 } 356 ipw.println(); 357 358 if (alarmClock != null) { 359 ipw.println("Alarm clock:"); 360 361 ipw.print(" triggerTime="); 362 ipw.println(sdf.format(new Date(alarmClock.getTriggerTime()))); 363 364 ipw.print(" showIntent="); 365 ipw.println(alarmClock.getShowIntent()); 366 } 367 if (operation != null) { 368 ipw.print("operation="); 369 ipw.println(operation); 370 } 371 if (listener != null) { 372 ipw.print("listener="); 373 ipw.println(listener.asBinder()); 374 } 375 if (mIdleOptions != null) { 376 ipw.print("idle-options="); 377 ipw.println(mIdleOptions.toString()); 378 } 379 } 380 dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed)381 public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) { 382 final long token = proto.start(fieldId); 383 384 proto.write(AlarmProto.TAG, statsTag); 385 proto.write(AlarmProto.TYPE, type); 386 proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, getWhenElapsed() - nowElapsed); 387 proto.write(AlarmProto.WINDOW_LENGTH_MS, windowLength); 388 proto.write(AlarmProto.REPEAT_INTERVAL_MS, repeatInterval); 389 proto.write(AlarmProto.COUNT, count); 390 proto.write(AlarmProto.FLAGS, flags); 391 if (alarmClock != null) { 392 alarmClock.dumpDebug(proto, AlarmProto.ALARM_CLOCK); 393 } 394 if (operation != null) { 395 operation.dumpDebug(proto, AlarmProto.OPERATION); 396 } 397 if (listener != null) { 398 proto.write(AlarmProto.LISTENER, listener.asBinder().toString()); 399 } 400 401 proto.end(token); 402 } 403 404 /** 405 * Stores a snapshot of an alarm at any given time to be used for logging and diagnostics. 406 * This should intentionally avoid holding pointers to objects like {@link Alarm#operation}. 407 */ 408 static class Snapshot { 409 final int mType; 410 final String mTag; 411 final long[] mPolicyWhenElapsed; 412 Snapshot(Alarm a)413 Snapshot(Alarm a) { 414 mType = a.type; 415 mTag = a.statsTag; 416 mPolicyWhenElapsed = Arrays.copyOf(a.mPolicyWhenElapsed, NUM_POLICIES); 417 } 418 dump(IndentingPrintWriter pw, long nowElapsed)419 void dump(IndentingPrintWriter pw, long nowElapsed) { 420 pw.print("type", typeToString(mType)); 421 pw.print("tag", mTag); 422 pw.println(); 423 pw.print("policyWhenElapsed:"); 424 for (int i = 0; i < NUM_POLICIES; i++) { 425 pw.print(" " + policyIndexToString(i) + "="); 426 TimeUtils.formatDuration(mPolicyWhenElapsed[i], nowElapsed, pw); 427 } 428 pw.println(); 429 } 430 } 431 } 432