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