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

Commit 2206af39 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

Extreme battery saver: AlarmManager

- AlarmManagerService now uses ForceAppStandbyTracker.

- Now AlarmManagerService uses the system + user power-save whitelist,
rather than just the user whitelist.

Test: Manual test
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
Bug 68769804

Change-Id: Ie2bd17fe0c3cb8b09ec4c4a78f9254277be16926
parent 5b8f69b0
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import "frameworks/base/core/proto/android/app/alarmmanager.proto";
import "frameworks/base/core/proto/android/app/pendingintent.proto";
import "frameworks/base/core/proto/android/internal/locallog.proto";
import "frameworks/base/core/proto/android/os/worksource.proto";
import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto";

package com.android.server;

@@ -32,10 +33,9 @@ message AlarmManagerServiceProto {
  optional int64 last_time_change_realtime = 4;
  // Current settings
  optional ConstantsProto settings = 5;
  // UIDs currently in the foreground.
  repeated int32 foreground_uids = 6;
  // Packages forced into app standby.
  repeated string forced_app_standby_packages = 7;

  // Dump from ForceAppStandbyTracker.
  optional ForceAppStandbyTrackerProto force_app_standby_tracker = 6;

  optional bool is_interactive = 8;
  // Only valid if is_interactive is false.
+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

syntax = "proto2";

package com.android.server;

option java_multiple_files = true;

// Dump from ForceAppStandbyTracker.
message ForceAppStandbyTrackerProto {
  // Whether all apps are forced standby or not.
  optional bool force_all_apps_standby = 1;

  // UIDs currently in the foreground.
  repeated int32 foreground_uids = 2;

  // App ids that are in power-save whitelist.
  repeated int32 power_save_whitelist_app_ids = 3;

  // App ids that are in temporary power-save whitelist.
  repeated int32 temp_power_save_whitelist_app_ids = 4;

  message RunAnyInBackgroundRestrictedPackages {
    optional int32 uid = 1;
    optional string package_name = 2;
  }

  // Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
  repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;
}
+81 −109
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -55,7 +54,6 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
@@ -81,6 +79,7 @@ import java.util.Locale;
import java.util.Random;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.function.Predicate;

import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.AlarmManager.RTC;
@@ -88,11 +87,17 @@ import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.ELAPSED_REALTIME;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.LocalLog;
import com.android.server.ForceAppStandbyTracker.Listener;

/**
 * Alarm manager implementaion.
 *
 * Unit test:
 atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java
 */
class AlarmManagerService extends SystemService {
    private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
    private static final int RTC_MASK = 1 << RTC;
@@ -131,13 +136,10 @@ class AlarmManagerService extends SystemService {
    final LocalLog mLog = new LocalLog(TAG);

    AppOpsManager mAppOps;
    IAppOpsService mAppOpsService;
    DeviceIdleController.LocalService mLocalDeviceIdleController;

    final Object mLock = new Object();

    ArraySet<String> mForcedAppStandbyPackages = new ArraySet<>();
    SparseBooleanArray mForegroundUids = new SparseBooleanArray();
    // List of alarms per uid deferred due to user applied background restrictions on the source app
    SparseArray<ArrayList<Alarm>> mPendingBackgroundAlarms = new SparseArray<>();
    long mNativeData;
@@ -183,12 +185,6 @@ class AlarmManagerService extends SystemService {
     */
    int mSystemUiUid;

    /**
     * The current set of user whitelisted apps for device idle mode, meaning these are allowed
     * to freely schedule alarms.
     */
    int[] mDeviceIdleUserWhitelist = new int[0];

    /**
     * For each uid, this is the last time we dispatched an "allow while idle" alarm,
     * used to determine the earliest we can dispatch the next such alarm. Times are in the
@@ -223,6 +219,8 @@ class AlarmManagerService extends SystemService {
    private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
            new SparseArray<>();

    private final ForceAppStandbyTracker mForceAppStandbyTracker;

    /**
     * All times are in milliseconds. These constants are kept synchronized with the system
     * global Settings. Any access to this class or its fields should be done while
@@ -757,6 +755,9 @@ class AlarmManagerService extends SystemService {
    public AlarmManagerService(Context context) {
        super(context);
        mConstants = new Constants(mHandler);

        mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
        mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
    }

    static long convertToElapsed(long when, int type) {
@@ -894,17 +895,48 @@ class AlarmManagerService extends SystemService {
        deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime());
    }

    void sendPendingBackgroundAlarmsForAppIdLocked(int appId) {
    /**
     * Check all alarms in {@link #mPendingBackgroundAlarms} and send the ones that are not
     * restricted.
     *
     * This is only called when the global "force all apps-standby" flag changes or when the
     * power save whitelist changes, so it's okay to be slow.
     */
    void sendAllUnrestrictedPendingBackgroundAlarmsLocked() {
        final ArrayList<Alarm> alarmsToDeliver = new ArrayList<>();
        for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
            final int uid = mPendingBackgroundAlarms.keyAt(i);
            final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
            if (UserHandle.getAppId(uid) == appId) {
                alarmsToDeliver.addAll(alarmsForUid);
                mPendingBackgroundAlarms.removeAt(i);

        findAllUnrestrictedPendingBackgroundAlarmsLockedInner(
                mPendingBackgroundAlarms, alarmsToDeliver, this::isBackgroundRestricted);

        if (alarmsToDeliver.size() > 0) {
            deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime());
        }
    }

    @VisibleForTesting
    static void findAllUnrestrictedPendingBackgroundAlarmsLockedInner(
            SparseArray<ArrayList<Alarm>> pendingAlarms, ArrayList<Alarm> unrestrictedAlarms,
            Predicate<Alarm> isBackgroundRestricted) {

        for (int uidIndex = pendingAlarms.size() - 1; uidIndex >= 0; uidIndex--) {
            final int uid = pendingAlarms.keyAt(uidIndex);
            final ArrayList<Alarm> alarmsForUid = pendingAlarms.valueAt(uidIndex);

            for (int alarmIndex = alarmsForUid.size() - 1; alarmIndex >= 0; alarmIndex--) {
                final Alarm alarm = alarmsForUid.get(alarmIndex);

                if (isBackgroundRestricted.test(alarm)) {
                    continue;
                }

                unrestrictedAlarms.add(alarm);
                alarmsForUid.remove(alarmIndex);
            }

            if (alarmsForUid.size() == 0) {
                pendingAlarms.removeAt(uidIndex);
            }
        }
        deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime());
    }

    private void deliverPendingBackgroundAlarmsLocked(ArrayList<Alarm> alarms, long nowELAPSED) {
@@ -1234,10 +1266,8 @@ class AlarmManagerService extends SystemService {
        } catch (RemoteException e) {
            // ignored; both services live in system_server
        }
        mAppOpsService = IAppOpsService.Stub.asInterface(
                ServiceManager.getService(Context.APP_OPS_SERVICE));
        publishBinderService(Context.ALARM_SERVICE, mService);
        publishLocalService(LocalService.class, new LocalService());
        mForceAppStandbyTracker.start();
    }

    @Override
@@ -1247,13 +1277,6 @@ class AlarmManagerService extends SystemService {
            mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
            mLocalDeviceIdleController
                    = LocalServices.getService(DeviceIdleController.LocalService.class);
            try {
                mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
                        new AppOpsWatcher());
            } catch (RemoteException rexc) {
                // Shouldn't happen as they are in the same process.
                Slog.e(TAG, "AppOps service not reachable", rexc);
            }
        }
    }

@@ -1582,8 +1605,7 @@ class AlarmManagerService extends SystemService {
            // timing restrictions.
            } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
                    || callingUid == mSystemUiUid
                    || Arrays.binarySearch(mDeviceIdleUserWhitelist,
                            UserHandle.getAppId(callingUid)) >= 0)) {
                    || mForceAppStandbyTracker.isUidPowerSaveWhitelisted(callingUid))) {
                flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
                flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
            }
@@ -1660,24 +1682,14 @@ class AlarmManagerService extends SystemService {
        }
    };

    public final class LocalService {
        public void setDeviceIdleUserWhitelist(int[] appids) {
            setDeviceIdleUserWhitelistImpl(appids);
        }
    }

    void dumpImpl(PrintWriter pw) {
        synchronized (mLock) {
            pw.println("Current Alarm Manager state:");
            mConstants.dump(pw);
            pw.println();

            pw.print("  Foreground uids: [");
            for (int i = 0; i < mForegroundUids.size(); i++) {
                if (mForegroundUids.valueAt(i)) pw.print(mForegroundUids.keyAt(i) + " ");
            }
            pw.println("]");
            pw.println("  Forced app standby packages: " + mForcedAppStandbyPackages);
            mForceAppStandbyTracker.dump(pw, "  ");

            final long nowRTC = System.currentTimeMillis();
            final long nowELAPSED = SystemClock.elapsedRealtime();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@@ -1717,7 +1729,6 @@ class AlarmManagerService extends SystemService {
            pw.print(" set at "); TimeUtils.formatDuration(mLastWakeupSet, nowELAPSED, pw);
            pw.println();
            pw.print("  Num time change events: "); pw.println(mNumTimeChanged);
            pw.println("  mDeviceIdleUserWhitelist=" + Arrays.toString(mDeviceIdleUserWhitelist));

            pw.println();
            pw.println("  Next alarm clock information: ");
@@ -1990,15 +2001,8 @@ class AlarmManagerService extends SystemService {

            mConstants.dumpProto(proto, AlarmManagerServiceProto.SETTINGS);

            final int foregroundUidsSize = mForegroundUids.size();
            for (int i = 0; i < foregroundUidsSize; i++) {
                if (mForegroundUids.valueAt(i)) {
                    proto.write(AlarmManagerServiceProto.FOREGROUND_UIDS, mForegroundUids.keyAt(i));
                }
            }
            for (String pkg : mForcedAppStandbyPackages) {
                proto.write(AlarmManagerServiceProto.FORCED_APP_STANDBY_PACKAGES, pkg);
            }
            mForceAppStandbyTracker.dumpProto(proto,
                    AlarmManagerServiceProto.FORCE_APP_STANDBY_TRACKER);

            proto.write(AlarmManagerServiceProto.IS_INTERACTIVE, mInteractive);
            if (!mInteractive) {
@@ -2022,9 +2026,6 @@ class AlarmManagerService extends SystemService {
            proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
                    nowElapsed - mLastWakeupSet);
            proto.write(AlarmManagerServiceProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
            for (int i : mDeviceIdleUserWhitelist) {
                proto.write(AlarmManagerServiceProto.DEVICE_IDLE_USER_WHITELIST_APP_IDS, i);
            }

            final TreeSet<Integer> users = new TreeSet<>();
            final int nextAlarmClockForUserSize = mNextAlarmClockForUser.size();
@@ -2266,28 +2267,6 @@ class AlarmManagerService extends SystemService {
        }
    }

    void setDeviceIdleUserWhitelistImpl(int[] appids) {
        synchronized (mLock) {
            // appids are sorted, just send pending alarms for any new appids added to the whitelist
            int i = 0, j = 0;
            while (i < appids.length) {
                while (j < mDeviceIdleUserWhitelist.length
                        && mDeviceIdleUserWhitelist[j] < appids[i]) {
                    j++;
                }
                if (j < mDeviceIdleUserWhitelist.length
                        && appids[i] != mDeviceIdleUserWhitelist[j]) {
                    if (DEBUG_BG_LIMIT) {
                        Slog.d(TAG, "Sending blocked alarms for whitelisted appid " + appids[j]);
                    }
                    sendPendingBackgroundAlarmsForAppIdLocked(appids[j]);
                }
                i++;
            }
            mDeviceIdleUserWhitelist = appids;
        }
    }

    AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
        synchronized (mLock) {
            return mNextAlarmClockForUser.get(userId);
@@ -2710,9 +2689,7 @@ class AlarmManagerService extends SystemService {
        final String sourcePackage =
                (alarm.operation != null) ? alarm.operation.getCreatorPackage() : alarm.packageName;
        final int sourceUid = alarm.creatorUid;
        return mForcedAppStandbyPackages.contains(sourcePackage) && !mForegroundUids.get(sourceUid)
                && Arrays.binarySearch(mDeviceIdleUserWhitelist, UserHandle.getAppId(sourceUid))
                < 0;
        return mForceAppStandbyTracker.areAlarmsRestricted(sourceUid, sourcePackage);
    }

    private native long init();
@@ -2859,7 +2836,8 @@ class AlarmManagerService extends SystemService {
        }
    }

    private static class Alarm {
    @VisibleForTesting
    static class Alarm {
        public final int type;
        public final long origWhen;
        public final boolean wakeup;
@@ -3476,17 +3454,10 @@ class AlarmManagerService extends SystemService {
                if (disabled) {
                    removeForStoppedLocked(uid);
                }
                mForegroundUids.delete(uid);
            }
        }

        @Override public void onUidActive(int uid) {
            synchronized (mLock) {
                if (!mForegroundUids.get(uid)) {
                    mForegroundUids.put(uid, true);
                    sendPendingBackgroundAlarmsLocked(uid, null);
                }
            }
        }

        @Override public void onUidIdle(int uid, boolean disabled) {
@@ -3494,7 +3465,6 @@ class AlarmManagerService extends SystemService {
                if (disabled) {
                    removeForStoppedLocked(uid);
                }
                mForegroundUids.delete(uid);
            }
        }

@@ -3502,27 +3472,29 @@ class AlarmManagerService extends SystemService {
        }
    };

    private final class AppOpsWatcher extends IAppOpsCallback.Stub {

    private final Listener mForceAppStandbyListener = new Listener() {
        @Override
        public void opChanged(int op, int uid, String packageName) throws RemoteException {
        public void unblockAllUnrestrictedAlarms() {
            synchronized (mLock) {
                final int mode = mAppOpsService.checkOperation(op, uid, packageName);
                if (DEBUG_BG_LIMIT) {
                    Slog.d(TAG,
                            "Appop changed for " + uid + ", " + packageName + " to " + mode);
                sendAllUnrestrictedPendingBackgroundAlarmsLocked();
            }
                final boolean changed;
                if (mode != AppOpsManager.MODE_ALLOWED) {
                    changed = mForcedAppStandbyPackages.add(packageName);
                } else {
                    changed = mForcedAppStandbyPackages.remove(packageName);
        }
                if (changed && mode == AppOpsManager.MODE_ALLOWED) {
                    sendPendingBackgroundAlarmsLocked(uid, packageName);

        @Override
        public void unblockAlarmsForUid(int uid) {
            synchronized (mLock) {
                sendPendingBackgroundAlarmsLocked(uid, null);
            }
        }

        @Override
        public void unblockAlarmsForUidPackage(int uid, String packageName) {
            synchronized (mLock) {
                sendPendingBackgroundAlarmsLocked(uid, packageName);
            }
        }
    };

    private final BroadcastStats getStatsLocked(PendingIntent pi) {
        String pkg = pi.getCreatorPackage();
+9 −10
Original line number Diff line number Diff line
@@ -119,7 +119,6 @@ public class DeviceIdleController extends SystemService
    private PowerManagerInternal mLocalPowerManager;
    private PowerManager mPowerManager;
    private ConnectivityService mConnectivityService;
    private AlarmManagerService.LocalService mLocalAlarmManager;
    private INetworkPolicyManager mNetworkPolicyManager;
    private SensorManager mSensorManager;
    private Sensor mMotionSensor;
@@ -1435,7 +1434,6 @@ public class DeviceIdleController extends SystemService
                mGoingIdleWakeLock.setReferenceCounted(true);
                mConnectivityService = (ConnectivityService)ServiceManager.getService(
                        Context.CONNECTIVITY_SERVICE);
                mLocalAlarmManager = getLocalService(AlarmManagerService.LocalService.class);
                mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
                        ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
                mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
@@ -1500,8 +1498,8 @@ public class DeviceIdleController extends SystemService

                mLocalActivityManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);

                passWhiteListToForceAppStandbyTrackerLocked();
                updateInteractivityLocked();
            }
            updateConnectivityState(null);
@@ -2477,13 +2475,7 @@ public class DeviceIdleController extends SystemService
            }
            mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
        }
        if (mLocalAlarmManager != null) {
            if (DEBUG) {
                Slog.d(TAG, "Setting alarm whitelist to "
                        + Arrays.toString(mPowerSaveWhitelistUserAppIdArray));
            }
            mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);
        }
        passWhiteListToForceAppStandbyTrackerLocked();
    }

    private void updateTempWhitelistAppIdsLocked(int appId, boolean adding) {
@@ -2509,6 +2501,7 @@ public class DeviceIdleController extends SystemService
            }
            mLocalPowerManager.setDeviceIdleTempWhitelist(mTempWhitelistAppIdArray);
        }
        passWhiteListToForceAppStandbyTrackerLocked();
    }

    private void reportPowerSaveWhitelistChangedLocked() {
@@ -2523,6 +2516,12 @@ public class DeviceIdleController extends SystemService
        getContext().sendBroadcastAsUser(intent, UserHandle.SYSTEM);
    }

    private void passWhiteListToForceAppStandbyTrackerLocked() {
        ForceAppStandbyTracker.getInstance(getContext()).setPowerSaveWhitelistAppIds(
                mPowerSaveWhitelistAllAppIdArray,
                mTempWhitelistAppIdArray);
    }

    void readConfigFileLocked() {
        if (DEBUG) Slog.d(TAG, "Reading config from " + mConfigFile.getBaseFile());
        mPowerSaveWhitelistUserApps.clear();
+537 −85

File changed.

Preview size limit exceeded, changes collapsed.

Loading