• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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