• 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 = 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