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