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