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

Commit 172612c8 authored by Amith Yamasani's avatar Amith Yamasani
Browse files

Global app standby flag

Disable app standby features if app_standby_enabled
is set to 0. This allows for UI and experiments to
control the feature.

Bug: 70655630
Test: adb shell settings put global app_standby_enabled 0
      adb shell dumpsys usagestats
      adb shell am get-standby-bucket <packagename>

Change-Id: Id6c62b078e52040767100f2997832cc586bb0806
parent a629e4c3
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -9584,9 +9584,10 @@ public final class Settings {
        /**
         * App standby (app idle) specific settings.
         * This is encoded as a key=value list, separated by commas. Ex:
         *
         * <p>
         * "idle_duration=5000,parole_interval=4500"
         *
         * <p>
         * All durations are in millis.
         * The following keys are supported:
         *
         * <pre>
@@ -9735,6 +9736,15 @@ public final class Settings {
         */
        public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";

        /**
         * Whether or not App Standby feature is enabled. This controls throttling of apps
         * based on usage patterns and predictions.
         * Type: int (0 for false, 1 for true)
         * Default: 1
         * @hide
         */
        public static final java.lang.String APP_STANDBY_ENABLED = "app_standby_enabled";

        /**
         * Get the key that retrieves a bluetooth headset's priority.
         * @hide
+1 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ public class SettingsBackupTest {
                    Settings.Global.APN_DB_UPDATE_CONTENT_URL,
                    Settings.Global.APN_DB_UPDATE_METADATA_URL,
                    Settings.Global.APP_IDLE_CONSTANTS,
                    Settings.Global.APP_STANDBY_ENABLED,
                    Settings.Global.ASSISTED_GPS_ENABLED,
                    Settings.Global.AUDIO_SAFE_VOLUME_STATE,
                    Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
+29 −18
Original line number Diff line number Diff line
@@ -16,6 +16,15 @@

package com.android.server.usage;

import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_FORCED;
import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_USAGE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;

import android.app.usage.UsageStatsManager;
import android.os.SystemClock;
import android.util.ArrayMap;
@@ -197,14 +206,14 @@ public class AppIdleHistory {
                + (elapsedRealtime - mElapsedSnapshot);
        appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
        appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
        if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
        if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) {
            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
            if (DEBUG) {
                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
                        + ", reason=" + appUsageHistory.bucketingReason);
            }
        }
        appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
        appUsageHistory.bucketingReason = REASON_USAGE;

        return appUsageHistory.currentBucket;
    }
@@ -213,15 +222,15 @@ public class AppIdleHistory {
        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                elapsedRealtime, true);
        if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
        if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) {
            appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET;
            if (DEBUG) {
                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
                        + ", reason=" + appUsageHistory.bucketingReason);
            }
        }
        // TODO: Should this be a different reason for partial usage?
        appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
        appUsageHistory.bucketingReason = REASON_USAGE;

        return appUsageHistory.currentBucket;
    }
@@ -279,8 +288,8 @@ public class AppIdleHistory {
            appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
            appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
            appUsageHistory.lastPredictedTime = getElapsedTime(0);
            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_NEVER;
            appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT;
            appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
            appUsageHistory.bucketingReason = REASON_DEFAULT;
            appUsageHistory.lastInformedBucket = -1;
            userHistory.put(packageName, appUsageHistory);
        }
@@ -298,7 +307,7 @@ public class AppIdleHistory {
        if (appUsageHistory == null) {
            return false; // Default to not idle
        } else {
            return appUsageHistory.currentBucket >= UsageStatsManager.STANDBY_BUCKET_RARE;
            return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE;
            // Whether or not it's passed will now be externally calculated and the
            // bucket will be pushed to the history using setAppStandbyBucket()
            //return hasPassedThresholds(appUsageHistory, elapsedRealtime);
@@ -320,7 +329,7 @@ public class AppIdleHistory {
                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
        appUsageHistory.currentBucket = bucket;
        appUsageHistory.bucketingReason = reason;
        if (reason.startsWith(UsageStatsManager.REASON_PREDICTED)) {
        if (reason.startsWith(REASON_PREDICTED)) {
            appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
        }
        if (DEBUG) {
@@ -336,12 +345,14 @@ public class AppIdleHistory {
        return appUsageHistory.currentBucket;
    }

    public Map<String, Integer> getAppStandbyBuckets(int userId, long elapsedRealtime) {
    public Map<String, Integer> getAppStandbyBuckets(int userId, long elapsedRealtime,
            boolean appIdleEnabled) {
        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
        int size = userHistory.size();
        HashMap<String, Integer> buckets = new HashMap<>(size);
        for (int i = 0; i < size; i++) {
            buckets.put(userHistory.keyAt(i), userHistory.valueAt(i).currentBucket);
            buckets.put(userHistory.keyAt(i),
                    appIdleEnabled ? userHistory.valueAt(i).currentBucket : STANDBY_BUCKET_ACTIVE);
        }
        return buckets;
    }
@@ -363,12 +374,12 @@ public class AppIdleHistory {
        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                elapsedRealtime, true);
        if (idle) {
            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_RARE;
            appUsageHistory.bucketingReason = UsageStatsManager.REASON_FORCED;
            appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
            appUsageHistory.bucketingReason = REASON_FORCED;
        } else {
            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
            // This is to pretend that the app was just used, don't freeze the state anymore.
            appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
            appUsageHistory.bucketingReason = REASON_USAGE;
        }
        return appUsageHistory.currentBucket;
    }
@@ -471,12 +482,12 @@ public class AppIdleHistory {
                        String currentBucketString = parser.getAttributeValue(null,
                                ATTR_CURRENT_BUCKET);
                        appUsageHistory.currentBucket = currentBucketString == null
                                ? UsageStatsManager.STANDBY_BUCKET_ACTIVE
                                ? STANDBY_BUCKET_ACTIVE
                                : Integer.parseInt(currentBucketString);
                        appUsageHistory.bucketingReason =
                                parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
                        if (appUsageHistory.bucketingReason == null) {
                            appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT;
                            appUsageHistory.bucketingReason = REASON_DEFAULT;
                        }
                        appUsageHistory.lastInformedBucket = -1;
                        userHistory.put(packageName, appUsageHistory);
+52 −23
Original line number Diff line number Diff line
@@ -162,12 +162,14 @@ public class AppStandbyController {
    long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
    long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;

    boolean mAppIdleEnabled;
    volatile boolean mAppIdleEnabled;
    boolean mAppIdleTempParoled;
    boolean mCharging;
    private long mLastAppIdleParoledTime;
    private boolean mSystemServicesReady = false;

    private final DeviceStateReceiver mDeviceStateReceiver;

    private volatile boolean mPendingOneTimeCheckIdleStates;

    private final AppStandbyHandler mHandler;
@@ -178,7 +180,7 @@ public class AppStandbyController {
    private AppWidgetManager mAppWidgetManager;
    private PowerManager mPowerManager;
    private PackageManager mPackageManager;
    private Injector mInjector;
    Injector mInjector;


    AppStandbyController(Context context, Looper looper) {
@@ -190,14 +192,13 @@ public class AppStandbyController {
        mContext = mInjector.getContext();
        mHandler = new AppStandbyHandler(mInjector.getLooper());
        mPackageManager = mContext.getPackageManager();
        mAppIdleEnabled = mInjector.isAppIdleEnabled();
        mDeviceStateReceiver = new DeviceStateReceiver();

        if (mAppIdleEnabled) {
        IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
        deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
            mContext.registerReceiver(new DeviceStateReceiver(), deviceStates);
        }
        mContext.registerReceiver(mDeviceStateReceiver, deviceStates);

        synchronized (mAppIdleLock) {
            mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
                    mInjector.elapsedRealtime());
@@ -213,9 +214,14 @@ public class AppStandbyController {
                null, mHandler);
    }

    void setAppIdleEnabled(boolean enabled) {
        mAppIdleEnabled = enabled;
    }

    public void onBootPhase(int phase) {
        mInjector.onBootPhase(phase);
        if (phase == PHASE_SYSTEM_SERVICES_READY) {
            setAppIdleEnabled(mInjector.isAppIdleEnabled());
            // Observe changes to the threshold
            SettingsObserver settingsObserver = new SettingsObserver(mHandler);
            settingsObserver.registerObserver();
@@ -240,6 +246,8 @@ public class AppStandbyController {
    }

    void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
        if (!mAppIdleEnabled) return;

        // Get sync adapters for the authority
        String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
                authority, userId);
@@ -295,6 +303,7 @@ public class AppStandbyController {
    }

    boolean isParoledOrCharging() {
        if (!mAppIdleEnabled) return true;
        synchronized (mAppIdleLock) {
            return mAppIdleTempParoled || mCharging;
        }
@@ -520,6 +529,7 @@ public class AppStandbyController {
    }

    void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
        if (!mAppIdleEnabled) return;
        synchronized (mAppIdleLock) {
            // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
            // about apps that are on some kind of whitelist anyway.
@@ -559,6 +569,8 @@ public class AppStandbyController {
     * required.
     */
    void forceIdleState(String packageName, int userId, boolean idle) {
        if (!mAppIdleEnabled) return;

        final int appId = getAppId(packageName);
        if (appId < 0) return;
        final long elapsedRealtime = mInjector.elapsedRealtime();
@@ -763,7 +775,7 @@ public class AppStandbyController {
    }

    void setAppIdleAsync(String packageName, boolean idle, int userId) {
        if (packageName == null) return;
        if (packageName == null || !mAppIdleEnabled) return;

        mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
                .sendToTarget();
@@ -771,8 +783,8 @@ public class AppStandbyController {

    @StandbyBuckets public int getAppStandbyBucket(String packageName, int userId,
            long elapsedRealtime, boolean shouldObfuscateInstantApps) {
        if (shouldObfuscateInstantApps &&
                mInjector.isPackageEphemeral(userId, packageName)) {
        if (!mAppIdleEnabled || (shouldObfuscateInstantApps
                && mInjector.isPackageEphemeral(userId, packageName))) {
            return STANDBY_BUCKET_ACTIVE;
        }

@@ -783,7 +795,7 @@ public class AppStandbyController {

    public Map<String, Integer> getAppStandbyBuckets(int userId, long elapsedRealtime) {
        synchronized (mAppIdleLock) {
            return mAppIdleHistory.getAppStandbyBuckets(userId, elapsedRealtime);
            return mAppIdleHistory.getAppStandbyBuckets(userId, elapsedRealtime, mAppIdleEnabled);
        }
    }

@@ -1058,8 +1070,11 @@ public class AppStandbyController {
        }

        boolean isAppIdleEnabled() {
            return mContext.getResources().getBoolean(
            final boolean buildFlag = mContext.getResources().getBoolean(
                    com.android.internal.R.bool.config_enableAutoPowerModes);
            final boolean runtimeFlag = Settings.Global.getInt(mContext.getContentResolver(),
                    Settings.Global.APP_STANDBY_ENABLED, 1) == 1;
            return buildFlag && runtimeFlag;
        }

        boolean isCharging() {
@@ -1130,7 +1145,7 @@ public class AppStandbyController {
                    break;

                case MSG_CHECK_IDLE_STATES:
                    if (checkIdleStates(msg.arg1)) {
                    if (checkIdleStates(msg.arg1) && mAppIdleEnabled) {
                        mHandler.sendMessageDelayed(mHandler.obtainMessage(
                                MSG_CHECK_IDLE_STATES, msg.arg1, 0),
                                mCheckIdleIntervalMillis);
@@ -1229,6 +1244,8 @@ public class AppStandbyController {
        void registerObserver() {
            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.APP_IDLE_CONSTANTS), false, this);
            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.APP_STANDBY_ENABLED), false, this);
        }

        @Override
@@ -1238,7 +1255,17 @@ public class AppStandbyController {
        }

        void updateSettings() {
            synchronized (mAppIdleLock) {
            if (DEBUG) {
                Slog.d(TAG,
                        "appidle=" + Settings.Global.getString(mContext.getContentResolver(),
                                Settings.Global.APP_STANDBY_ENABLED));
                Slog.d(TAG, "appidleconstants=" + Settings.Global.getString(
                        mContext.getContentResolver(),
                        Settings.Global.APP_IDLE_CONSTANTS));
            }
            // Check if app_idle_enabled has changed
            setAppIdleEnabled(mInjector.isAppIdleEnabled());

            // Look at global settings for this.
            // TODO: Maybe apply different thresholds for different users.
            try {
@@ -1248,6 +1275,8 @@ public class AppStandbyController {
                // fallthrough, mParser is empty and all defaults will be returned.
            }

            synchronized (mAppIdleLock) {

                // Default: 24 hours between paroles
                mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
                        COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);