Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1d5ea8c3 authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

Adding metrics to alarm manager

Push atoms:
 - Every time an alarm is scheduled, we push some attributes of the
 alarm and the reason why such an alarm was allowed. This will help
 audit spammy behavior from apps that misuse alarm API, e.g.
 apps that have SCHEDULE_EXACT_ALARM.
 - Every time an alarm batch is delivered, we log the size of the batch
 and whether or not it was a wakeup batch. This is the essential measure
 of alarm manager's batching efficiency.

Pulled atoms:
 - Logging general stats on pending alarms every once in a while.

Test: atest CtsAlarmManagerTestCases
atest FrameworksMockingServicesTests:com.android.server.alarm

statsd-testdrive 377 378 10106

Bug: 161497385
Bug: 171306433
Bug: 177556103
Change-Id: Ib13c9c380b641d318c6b8777996aa37f9b3d1bfc
parent c9fcf22f
Loading
Loading
Loading
Loading
+40 −2
Original line number Diff line number Diff line
@@ -66,6 +66,23 @@ class Alarm {
     */
    public static final int BATTERY_SAVER_POLICY_INDEX = 3;

    /**
     * Reason to use for inexact alarms.
     */
    static final int EXACT_ALLOW_REASON_NOT_APPLICABLE = -1;
    /**
     * Caller had SCHEDULE_EXACT_ALARM permission.
     */
    static final int EXACT_ALLOW_REASON_PERMISSION = 0;
    /**
     * Caller was in the power allow-list.
     */
    static final int EXACT_ALLOW_REASON_ALLOW_LIST = 1;
    /**
     * Change wasn't enable for the caller due to compat reasons.
     */
    static final int EXACT_ALLOW_REASON_COMPAT = 2;

    public final int type;
    /**
     * The original trigger time supplied by the caller. This can be in the elapsed or rtc time base
@@ -92,13 +109,15 @@ class Alarm {
    /** The ultimate delivery time to be used for this alarm */
    private long mWhenElapsed;
    private long mMaxWhenElapsed;
    public int mExactAllowReason;
    public AlarmManagerService.PriorityClass priorityClass;
    /** Broadcast options to use when delivering this alarm */
    public Bundle mIdleOptions;

    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) {
            AlarmManager.AlarmClockInfo info, int uid, String pkgName, Bundle idleOptions,
            int exactAllowReason) {
        this.type = type;
        origWhen = when;
        wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP
@@ -119,6 +138,7 @@ class Alarm {
        this.uid = uid;
        packageName = pkgName;
        mIdleOptions = idleOptions;
        mExactAllowReason = exactAllowReason;
        sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
        creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid;
    }
@@ -216,7 +236,6 @@ class Alarm {
        sb.append(type);
        sb.append(" origWhen ");
        sb.append(origWhen);
        sb.append(" ");
        sb.append(" whenElapsed ");
        sb.append(getWhenElapsed());
        sb.append(" ");
@@ -240,6 +259,21 @@ class Alarm {
        }
    }

    private static String exactReasonToString(int reason) {
        switch (reason) {
            case EXACT_ALLOW_REASON_ALLOW_LIST:
                return "allow-listed";
            case EXACT_ALLOW_REASON_COMPAT:
                return "compat";
            case EXACT_ALLOW_REASON_PERMISSION:
                return "permission";
            case EXACT_ALLOW_REASON_NOT_APPLICABLE:
                return "N/A";
            default:
                return "--unknown--";
        }
    }

    public static String typeToString(int type) {
        switch (type) {
            case RTC:
@@ -270,6 +304,10 @@ class Alarm {
        }
        ipw.print(" window=");
        TimeUtils.formatDuration(windowLength, ipw);
        if (mExactAllowReason != EXACT_ALLOW_REASON_NOT_APPLICABLE) {
            ipw.print(" exactAllowReason=");
            ipw.print(exactReasonToString(mExactAllowReason));
        }
        ipw.print(" repeatInterval=");
        ipw.print(repeatInterval);
        ipw.print(" count=");
+41 −17
Original line number Diff line number Diff line
@@ -38,6 +38,10 @@ import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
import static com.android.server.alarm.Alarm.BATTERY_SAVER_POLICY_INDEX;
import static com.android.server.alarm.Alarm.DEVICE_IDLE_POLICY_INDEX;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_ALLOW_LIST;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_COMPAT;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;

import android.Manifest;
@@ -223,6 +227,7 @@ public class AlarmManagerService extends SystemService {

    private final Injector mInjector;
    int mBroadcastRefCount = 0;
    MetricsHelper mMetricsHelper;
    PowerManager.WakeLock mWakeLock;
    SparseIntArray mAlarmsPerUid = new SparseIntArray();
    ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>();
@@ -1221,7 +1226,7 @@ public class AlarmManagerService extends SystemService {
                setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
                        nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
                        null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
                        alarm.packageName, null);
                        alarm.packageName, null, EXACT_ALLOW_REASON_NOT_APPLICABLE);
                // Kernel alarms will be rescheduled as needed in setImplLocked
            }
        }
@@ -1442,6 +1447,7 @@ public class AlarmManagerService extends SystemService {
    @Override
    public void onStart() {
        mInjector.init();
        mMetricsHelper = new MetricsHelper(getContext());

        mListenerDeathRecipient = new IBinder.DeathRecipient() {
            @Override
@@ -1584,6 +1590,7 @@ public class AlarmManagerService extends SystemService {
                            });
                } catch (RemoteException e) {
                }
                mMetricsHelper.registerPuller(mAlarmStore);

                mLocalDeviceIdleController =
                        LocalServices.getService(DeviceIdleInternal.class);
@@ -1691,7 +1698,7 @@ public class AlarmManagerService extends SystemService {
    void setImpl(int type, long triggerAtTime, long windowLength, long interval,
            PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
            int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
            int callingUid, String callingPackage, Bundle idleOptions) {
            int callingUid, String callingPackage, Bundle idleOptions, int exactAllowReason) {
        if ((operation == null && directReceiver == null)
                || (operation != null && directReceiver != null)) {
            Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver");
@@ -1788,7 +1795,7 @@ public class AlarmManagerService extends SystemService {
            }
            setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, interval, operation,
                    directReceiver, listenerTag, flags, workSource, alarmClock, callingUid,
                    callingPackage, idleOptions);
                    callingPackage, idleOptions, exactAllowReason);
        }
    }

@@ -1796,10 +1803,10 @@ public class AlarmManagerService extends SystemService {
            long interval, PendingIntent operation, IAlarmListener directReceiver,
            String listenerTag, int flags, WorkSource workSource,
            AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage,
            Bundle idleOptions) {
            Bundle idleOptions, int exactAllowReason) {
        final Alarm a = new Alarm(type, when, whenElapsed, windowLength, interval,
                operation, directReceiver, listenerTag, workSource, flags, alarmClock,
                callingUid, callingPackage, idleOptions);
                callingUid, callingPackage, idleOptions, exactAllowReason);
        if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, callingPackage)) {
            Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
                    + " -- package not allowed to start");
@@ -1808,6 +1815,7 @@ public class AlarmManagerService extends SystemService {
        removeLocked(operation, directReceiver);
        incrementAlarmCount(a.uid);
        setImplLocked(a);
        MetricsHelper.pushAlarmScheduled(a);
    }

    /**
@@ -2220,6 +2228,8 @@ public class AlarmManagerService extends SystemService {

            // Make sure the caller is allowed to use the requested kind of alarm, and also
            // decide what quota and broadcast options to use.
            boolean allowListed = false;    // For logging the reason.
            boolean changeDisabled = false; // For logging the reason.
            Bundle idleOptions = null;
            if ((flags & FLAG_PRIORITIZE) != 0) {
                getContext().enforcePermission(
@@ -2236,6 +2246,7 @@ public class AlarmManagerService extends SystemService {
                    lowerQuota = !exact;
                    idleOptions = exact ? mOptsWithFgs.toBundle() : mOptsWithoutFgs.toBundle();
                } else {
                    changeDisabled = true;
                    needsPermission = false;
                    lowerQuota = allowWhileIdle;
                    idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
@@ -2250,6 +2261,8 @@ public class AlarmManagerService extends SystemService {
                        } else {
                            Slog.wtf(TAG, errorMessage);
                        }
                    } else {
                        allowListed = true;
                    }
                    // If the app is on the full system power allow-list (not except-idle), or we're
                    // in a soft failure mode, we still allow the alarms.
@@ -2263,15 +2276,25 @@ public class AlarmManagerService extends SystemService {
                    flags |= FLAG_ALLOW_WHILE_IDLE_COMPAT;
                }
            }

            // If this is an exact time alarm, then it can't be batched with other alarms.
            final int exactAllowReason;
            if (exact) {
                // If this is an exact time alarm, then it can't be batched with other alarms.
                flags |= AlarmManager.FLAG_STANDALONE;

                if (changeDisabled) {
                    exactAllowReason = EXACT_ALLOW_REASON_COMPAT;
                } else if (allowListed) {
                    exactAllowReason = EXACT_ALLOW_REASON_ALLOW_LIST;
                } else {
                    exactAllowReason = EXACT_ALLOW_REASON_PERMISSION;
                }
            } else {
                exactAllowReason = EXACT_ALLOW_REASON_NOT_APPLICABLE;
            }

            setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
                    listenerTag, flags, workSource, alarmClock, callingUid, callingPackage,
                    idleOptions);
                    idleOptions, exactAllowReason);
        }

        @Override
@@ -3501,8 +3524,8 @@ public class AlarmManagerService extends SystemService {
    private static native int setKernelTimezone(long nativeData, int minuteswest);
    private static native long getNextAlarm(long nativeData, int type);

    boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED) {
        boolean hasWakeup = false;
    int triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED) {
        int wakeUps = 0;
        final ArrayList<Alarm> pendingAlarms = mAlarmStore.removePendingAlarms(nowELAPSED);
        for (final Alarm alarm : pendingAlarms) {
            if (isBackgroundRestricted(alarm)) {
@@ -3559,11 +3582,11 @@ public class AlarmManagerService extends SystemService {
                setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
                        nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
                        null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
                        alarm.packageName, null);
                        alarm.packageName, null, EXACT_ALLOW_REASON_NOT_APPLICABLE);
            }

            if (alarm.wakeup) {
                hasWakeup = true;
                wakeUps++;
            }

            // We removed an alarm clock. Let the caller recompute the next alarm clock.
@@ -3584,7 +3607,7 @@ public class AlarmManagerService extends SystemService {
            }
        }

        return hasWakeup;
        return wakeUps;
    }

    long currentNonWakeupFuzzLocked(long nowELAPSED) {
@@ -3843,8 +3866,8 @@ public class AlarmManagerService extends SystemService {
                        }

                        mLastTrigger = nowELAPSED;
                        boolean hasWakeup = triggerAlarmsLocked(triggerList, nowELAPSED);
                        if (!hasWakeup && checkAllowNonWakeupDelayLocked(nowELAPSED)) {
                        final int wakeUps = triggerAlarmsLocked(triggerList, nowELAPSED);
                        if (wakeUps == 0 && checkAllowNonWakeupDelayLocked(nowELAPSED)) {
                            // if there are no wakeup alarms and the screen is off, we can
                            // delay what we have so far until the future.
                            if (mPendingNonWakeupAlarms.size() == 0) {
@@ -3896,6 +3919,7 @@ public class AlarmManagerService extends SystemService {
                            reorderAlarmsBasedOnStandbyBuckets(triggerPackages);
                            rescheduleKernelAlarmsLocked();
                            updateNextAlarmClockLocked();
                            MetricsHelper.pushAlarmBatchDelivered(triggerList.size(), wakeUps);
                        }
                    }

@@ -4126,7 +4150,7 @@ public class AlarmManagerService extends SystemService {

            setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
                    0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null,
                    Process.myUid(), "android", null);
                    Process.myUid(), "android", null, EXACT_ALLOW_REASON_ALLOW_LIST);

            // Finally, remember when we set the tick alarm
            synchronized (mLock) {
@@ -4146,7 +4170,7 @@ public class AlarmManagerService extends SystemService {
            final WorkSource workSource = null; // Let system take blame for date change events.
            setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, null, null,
                    AlarmManager.FLAG_STANDALONE, workSource, null,
                    Process.myUid(), "android", null);
                    Process.myUid(), "android", null, EXACT_ALLOW_REASON_ALLOW_LIST);
        }
    }

+5 −1
Original line number Diff line number Diff line
@@ -138,6 +138,11 @@ public interface AlarmStore {
     */
    String getName();

    /**
     * Returns the number of alarms that satisfy the given condition.
     */
    int getCount(Predicate<Alarm> condition);

    /**
     * A functional interface used to update the alarm. Used to describe the update in
     * {@link #updateAlarmDeliveries(AlarmDeliveryCalculator)}
@@ -153,4 +158,3 @@ public interface AlarmStore {
        boolean updateAlarmDelivery(Alarm a);
    }
}
+18 −1
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.server.alarm;

import static com.android.server.alarm.AlarmManagerService.DEBUG_BATCH;
import static com.android.server.alarm.AlarmManagerService.TAG;
import static com.android.server.alarm.AlarmManagerService.clampPositive;
import static com.android.server.alarm.AlarmManagerService.dumpAlarmList;
import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm;
@@ -50,10 +49,12 @@ public class BatchingAlarmStore implements AlarmStore {

    interface Stats {
        int REBATCH_ALL_ALARMS = 0;
        int GET_COUNT = 1;
    }

    final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{
            "REBATCH_ALL_ALARMS",
            "GET_COUNT",
    });

    private static final Comparator<Batch> sBatchOrder = Comparator.comparingLong(b -> b.mStart);
@@ -219,6 +220,22 @@ public class BatchingAlarmStore implements AlarmStore {
        return TAG;
    }

    @Override
    public int getCount(Predicate<Alarm> condition) {
        long start = mStatLogger.getTime();

        int count = 0;
        for (Batch b : mAlarmBatches) {
            for (int i = 0; i < b.size(); i++) {
                if (condition.test(b.get(i))) {
                    count++;
                }
            }
        }
        mStatLogger.logDurationStat(Stats.GET_COUNT, start);
        return count;
    }

    private void insertAndBatchAlarm(Alarm alarm) {
        final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
                : attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed());
+16 −0
Original line number Diff line number Diff line
@@ -47,11 +47,13 @@ public class LazyAlarmStore implements AlarmStore {
    interface Stats {
        int GET_NEXT_DELIVERY_TIME = 0;
        int GET_NEXT_WAKEUP_DELIVERY_TIME = 1;
        int GET_COUNT = 2;
    }

    final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{
            "GET_NEXT_DELIVERY_TIME",
            "GET_NEXT_WAKEUP_DELIVERY_TIME",
            "GET_COUNT",
    });

    // Decreasing time order because it is more efficient to remove from the tail of an array list.
@@ -221,4 +223,18 @@ public class LazyAlarmStore implements AlarmStore {
    public String getName() {
        return TAG;
    }

    @Override
    public int getCount(Predicate<Alarm> condition) {
        long start = mStatLogger.getTime();

        int count = 0;
        for (final Alarm a : mAlarms) {
            if (condition.test(a)) {
                count++;
            }
        }
        mStatLogger.logDurationStat(Stats.GET_COUNT, start);
        return count;
    }
}
Loading