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

Commit e491821c authored by Makoto Onuki's avatar Makoto Onuki
Browse files

[3rd try] JobScheduler to use UID active state for job exemption

JobScheduler used to use procstate foreground state to decide whether
to exempt jobs, but it should use UID's active state instead, so that
if apps in the temp-whitelist schedule jobs, they run immediately.

Test: Boot
Test: atest CtsAlarmManagerTestCases
Test: atest CtsJobSchedulerTestCases
Test: atest CtsBatterySavingTestCases
Test: atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java

Bug: 72125364
Change-Id: I7a5628e76121199f3a5299e2a5576e1481574a6e
parent 27d86e90
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -348,4 +348,9 @@ public abstract class ActivityManagerInternal {
     * Returns is the caller has the same uid as the Recents component
     */
    public abstract boolean isCallerRecents(int callingUid);

    /**
     * Whether an UID is active or idle.
     */
    public abstract boolean isUidActive(int uid);
}
+22 −15
Original line number Diff line number Diff line
@@ -99,7 +99,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.LocalLog;
import com.android.server.ForceAppStandbyTracker.Listener;
import com.android.internal.util.Preconditions;
import com.android.server.AppStateTracker.Listener;

/**
 * Alarm manager implementaion.
@@ -250,7 +251,7 @@ class AlarmManagerService extends SystemService {
    private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
            new SparseArray<>();

    private final ForceAppStandbyTracker mForceAppStandbyTracker;
    private AppStateTracker mAppStateTracker;
    private boolean mAppStandbyParole;
    private ArrayMap<Pair<String, Integer>, Long> mLastAlarmDeliveredForPackage = new ArrayMap<>();

@@ -708,9 +709,6 @@ class AlarmManagerService extends SystemService {
        super(context);
        mConstants = new Constants(mHandler);

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

        publishLocalService(AlarmManagerInternal.class, new LocalService());
    }

@@ -1330,13 +1328,15 @@ class AlarmManagerService extends SystemService {
    @Override
    public void onBootPhase(int phase) {
        if (phase == PHASE_SYSTEM_SERVICES_READY) {
            mForceAppStandbyTracker.start();
            mConstants.start(getContext().getContentResolver());
            mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
            mLocalDeviceIdleController
                    = LocalServices.getService(DeviceIdleController.LocalService.class);
            mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
            mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker());

            mAppStateTracker = LocalServices.getService(AppStateTracker.class);
            mAppStateTracker.addListener(mForceAppStandbyListener);
        }
    }

@@ -1725,7 +1725,8 @@ class AlarmManagerService extends SystemService {
            // timing restrictions.
            } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
                    || UserHandle.isSameApp(callingUid, mSystemUiUid)
                    || mForceAppStandbyTracker.isUidPowerSaveWhitelisted(callingUid))) {
                    || ((mAppStateTracker != null)
                        && mAppStateTracker.isUidPowerSaveWhitelisted(callingUid)))) {
                flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
                flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
            }
@@ -1808,8 +1809,10 @@ class AlarmManagerService extends SystemService {
            mConstants.dump(pw);
            pw.println();

            mForceAppStandbyTracker.dump(pw, "  ");
            if (mAppStateTracker != null) {
                mAppStateTracker.dump(pw, "  ");
                pw.println();
            }

            pw.println("  App Standby Parole: " + mAppStandbyParole);
            pw.println();
@@ -2157,8 +2160,10 @@ class AlarmManagerService extends SystemService {

            mConstants.dumpProto(proto, AlarmManagerServiceProto.SETTINGS);

            mForceAppStandbyTracker.dumpProto(proto,
            if (mAppStateTracker != null) {
                mAppStateTracker.dumpProto(proto,
                        AlarmManagerServiceProto.FORCE_APP_STANDBY_TRACKER);
            }

            proto.write(AlarmManagerServiceProto.IS_INTERACTIVE, mInteractive);
            if (!mInteractive) {
@@ -2938,8 +2943,8 @@ class AlarmManagerService extends SystemService {
        }
        final String sourcePackage = alarm.sourcePackage;
        final int sourceUid = alarm.creatorUid;
        return mForceAppStandbyTracker.areAlarmsRestricted(sourceUid, sourcePackage,
                allowWhileIdle);
        return (mAppStateTracker != null) &&
                mAppStateTracker.areAlarmsRestricted(sourceUid, sourcePackage, allowWhileIdle);
    }

    private native long init();
@@ -2951,7 +2956,8 @@ class AlarmManagerService extends SystemService {

    private long getWhileIdleMinIntervalLocked(int uid) {
        final boolean dozing = mPendingIdleUntil != null;
        final boolean ebs = mForceAppStandbyTracker.isForceAllAppsStandbyEnabled();
        final boolean ebs = (mAppStateTracker != null)
                && mAppStateTracker.isForceAllAppsStandbyEnabled();
        if (!dozing && !ebs) {
            return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
        }
@@ -4139,7 +4145,8 @@ class AlarmManagerService extends SystemService {
            if (allowWhileIdle) {
                // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
                mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
                if (mForceAppStandbyTracker.isUidInForeground(alarm.creatorUid)) {
                if ((mAppStateTracker == null)
                        || mAppStateTracker.isUidInForeground(alarm.creatorUid)) {
                    mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true);
                } else {
                    mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false);
+47 −33
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.server.DeviceIdleController.LocalService;
import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage;
import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;

@@ -73,14 +72,14 @@ import java.util.List;
 * TODO: Make it a LocalService.
 *
 * Test:
  atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
  atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
 */
public class ForceAppStandbyTracker {
public class AppStateTracker {
    private static final String TAG = "ForceAppStandbyTracker";
    private static final boolean DEBUG = true;

    @GuardedBy("ForceAppStandbyTracker.class")
    private static ForceAppStandbyTracker sInstance;
    @GuardedBy("AppStateTracker.class")
    private static AppStateTracker sInstance;

    private final Object mLock = new Object();
    private final Context mContext;
@@ -89,6 +88,7 @@ public class ForceAppStandbyTracker {
    static final int TARGET_OP = AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;

    IActivityManager mIActivityManager;
    ActivityManagerInternal mActivityManagerInternal;
    AppOpsManager mAppOpsManager;
    IAppOpsService mAppOpsService;
    PowerManagerInternal mPowerManagerInternal;
@@ -172,6 +172,9 @@ public class ForceAppStandbyTracker {
        int EXEMPT_CHANGED = 6;
        int FORCE_ALL_CHANGED = 7;
        int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;

        int IS_UID_ACTIVE_CACHED = 9;
        int IS_UID_ACTIVE_RAW = 10;
    }

    private final StatLogger mStatLogger = new StatLogger(new String[] {
@@ -184,6 +187,9 @@ public class ForceAppStandbyTracker {
            "EXEMPT_CHANGED",
            "FORCE_ALL_CHANGED",
            "FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED",

            "IS_UID_ACTIVE_CACHED",
            "IS_UID_ACTIVE_RAW",
    });

    @VisibleForTesting
@@ -249,7 +255,7 @@ public class ForceAppStandbyTracker {
        /**
         * This is called when the OP_RUN_ANY_IN_BACKGROUND appops changed for a package.
         */
        private void onRunAnyAppOpsChanged(ForceAppStandbyTracker sender,
        private void onRunAnyAppOpsChanged(AppStateTracker sender,
                int uid, @NonNull String packageName) {
            updateJobsForUidPackage(uid, packageName);

@@ -264,14 +270,14 @@ public class ForceAppStandbyTracker {
        /**
         * This is called when the foreground state changed for a UID.
         */
        private void onUidForegroundStateChanged(ForceAppStandbyTracker sender, int uid) {
        private void onUidForegroundStateChanged(AppStateTracker sender, int uid) {
            onUidForeground(uid, sender.isUidInForeground(uid));
        }

        /**
         * This is called when the active/idle state changed for a UID.
         */
        private void onUidActiveStateChanged(ForceAppStandbyTracker sender, int uid) {
        private void onUidActiveStateChanged(AppStateTracker sender, int uid) {
            updateJobsForUid(uid);

            if (sender.isUidActive(uid)) {
@@ -282,7 +288,7 @@ public class ForceAppStandbyTracker {
        /**
         * This is called when an app-id(s) is removed from the power save whitelist.
         */
        private void onPowerSaveUnwhitelisted(ForceAppStandbyTracker sender) {
        private void onPowerSaveUnwhitelisted(AppStateTracker sender) {
            updateAllJobs();
            unblockAllUnrestrictedAlarms();
        }
@@ -291,14 +297,14 @@ public class ForceAppStandbyTracker {
         * This is called when the power save whitelist changes, excluding the
         * {@link #onPowerSaveUnwhitelisted} case.
         */
        private void onPowerSaveWhitelistedChanged(ForceAppStandbyTracker sender) {
        private void onPowerSaveWhitelistedChanged(AppStateTracker sender) {
            updateAllJobs();
        }

        /**
         * This is called when the temp whitelist changes.
         */
        private void onTempPowerSaveWhitelistChanged(ForceAppStandbyTracker sender) {
        private void onTempPowerSaveWhitelistChanged(AppStateTracker sender) {

            // TODO This case happens rather frequently; consider optimizing and update jobs
            // only for affected app-ids.
@@ -311,7 +317,7 @@ public class ForceAppStandbyTracker {
        /**
         * This is called when the EXEMPT bucket is updated.
         */
        private void onExemptChanged(ForceAppStandbyTracker sender) {
        private void onExemptChanged(AppStateTracker sender) {
            // This doesn't happen very often, so just re-evaluate all jobs / alarms.
            updateAllJobs();
            unblockAllUnrestrictedAlarms();
@@ -320,7 +326,7 @@ public class ForceAppStandbyTracker {
        /**
         * This is called when the global "force all apps standby" flag changes.
         */
        private void onForceAllAppsStandbyChanged(ForceAppStandbyTracker sender) {
        private void onForceAllAppsStandbyChanged(AppStateTracker sender) {
            updateAllJobs();

            if (!sender.isForceAllAppsStandbyEnabled()) {
@@ -377,30 +383,15 @@ public class ForceAppStandbyTracker {
        }
    }

    @VisibleForTesting
    ForceAppStandbyTracker(Context context, Looper looper) {
    public AppStateTracker(Context context, Looper looper) {
        mContext = context;
        mHandler = new MyHandler(looper);
    }

    private ForceAppStandbyTracker(Context context) {
        this(context, FgThread.get().getLooper());
    }

    /**
     * Get the singleton instance.
     */
    public static synchronized ForceAppStandbyTracker getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new ForceAppStandbyTracker(context);
        }
        return sInstance;
    }

    /**
     * Call it when the system is ready.
     */
    public void start() {
    public void onSystemServicesReady() {
        synchronized (mLock) {
            if (mStarted) {
                return;
@@ -408,6 +399,7 @@ public class ForceAppStandbyTracker {
            mStarted = true;

            mIActivityManager = Preconditions.checkNotNull(injectIActivityManager());
            mActivityManagerInternal = Preconditions.checkNotNull(injectActivityManagerInternal());
            mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
            mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
            mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
@@ -474,6 +466,11 @@ public class ForceAppStandbyTracker {
        return ActivityManager.getService();
    }

    @VisibleForTesting
    ActivityManagerInternal injectActivityManagerInternal() {
        return LocalServices.getService(ActivityManagerInternal.class);
    }

    @VisibleForTesting
    PowerManagerInternal injectPowerManagerInternal() {
        return LocalServices.getService(PowerManagerInternal.class);
@@ -804,7 +801,7 @@ public class ForceAppStandbyTracker {
                    return;
                }
            }
            final ForceAppStandbyTracker sender = ForceAppStandbyTracker.this;
            final AppStateTracker sender = AppStateTracker.this;

            long start = mStatLogger.getTime();
            switch (msg.what) {
@@ -1094,11 +1091,11 @@ public class ForceAppStandbyTracker {
    }

    /**
     * @return whether a UID is in active or not.
     * @return whether a UID is in active or not *based on cached information.*
     *
     * Note this information is based on the UID proc state callback, meaning it's updated
     * asynchronously and may subtly be stale. If the fresh data is needed, use
     * {@link ActivityManagerInternal#getUidProcessState} instead.
     * {@link #isUidActiveSynced} instead.
     */
    public boolean isUidActive(int uid) {
        if (UserHandle.isCore(uid)) {
@@ -1109,6 +1106,23 @@ public class ForceAppStandbyTracker {
        }
    }

    /**
     * @return whether a UID is in active or not *right now.*
     *
     * This gives the fresh information, but may access the activity manager so is slower.
     */
    public boolean isUidActiveSynced(int uid) {
        if (isUidActive(uid)) { // Use the cached one first.
            return true;
        }
        final long start = mStatLogger.getTime();

        final boolean ret = mActivityManagerInternal.isUidActive(uid);
        mStatLogger.logDurationStat(Stats.IS_UID_ACTIVE_RAW, start);

        return ret;
    }

    /**
     * @return whether a UID is in the foreground or not.
     *
+7 −1
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ import com.android.internal.os.AtomicFile;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -128,6 +129,7 @@ public class DeviceIdleController extends SystemService
    private Intent mIdleIntent;
    private Intent mLightIdleIntent;
    private AnyMotionDetector mAnyMotionDetector;
    private final AppStateTracker mAppStateTracker;
    private boolean mLightEnabled;
    private boolean mDeepEnabled;
    private boolean mForceIdle;
@@ -1371,6 +1373,8 @@ public class DeviceIdleController extends SystemService
        super(context);
        mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
        mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
        mAppStateTracker = new AppStateTracker(context, FgThread.get().getLooper());
        LocalServices.addService(AppStateTracker.class, mAppStateTracker);
    }

    boolean isAppOnWhitelistInternal(int appid) {
@@ -1501,6 +1505,8 @@ public class DeviceIdleController extends SystemService
                        (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
                        mHandler, mSensorManager, this, angleThreshold);

                mAppStateTracker.onSystemServicesReady();

                mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
                mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                        | Intent.FLAG_RECEIVER_FOREGROUND);
@@ -2615,7 +2621,7 @@ public class DeviceIdleController extends SystemService
    }

    private void passWhiteListToForceAppStandbyTrackerLocked() {
        ForceAppStandbyTracker.getInstance(getContext()).setPowerSaveWhitelistAppIds(
        mAppStateTracker.setPowerSaveWhitelistAppIds(
                mPowerSaveWhitelistExceptIdleAppIdArray,
                mTempWhitelistAppIdArray);
    }
+9 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server;

import android.os.SystemClock;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
@@ -33,6 +34,8 @@ import java.io.PrintWriter;
 * @hide
 */
public class StatLogger {
    private static final String TAG = "StatLogger";

    private final Object mLock = new Object();

    private final int SIZE;
@@ -66,8 +69,12 @@ public class StatLogger {
     */
    public void logDurationStat(int eventId, long start) {
        synchronized (mLock) {
            if (eventId >= 0 && eventId < SIZE) {
                mCountStats[eventId]++;
                mDurationStats[eventId] += (getTime() - start);
            } else {
                Slog.wtf(TAG, "Invalid event ID: " + eventId);
            }
        }
    }

Loading