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