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

Commit d57c1df3 authored by Nancy Zheng's avatar Nancy Zheng Committed by Android (Google) Code Review
Browse files

Merge "Add setting/experiment for small battery devices to have all forced app...

Merge "Add setting/experiment for small battery devices to have all forced app standby enabled except for when the device is charging."
parents db8e6303 525aaa13
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -10374,6 +10374,15 @@ public final class Settings {
         */
        public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";

        /**
         * Whether or not to enable Forced App Standby on small battery devices.
         * Type: int (0 for false, 1 for true)
         * Default: 0
         * @hide
         */
        public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED
                = "forced_app_standby_for_small_battery_enabled";

        /**
         * Whether or not Network Watchlist feature is enabled.
         * Type: int (0 for false, 1 for true)
+9 −0
Original line number Diff line number Diff line
@@ -41,4 +41,13 @@ message ForceAppStandbyTrackerProto {

  // Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
  repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;

  // Whether device is a small battery device
  optional bool is_small_battery_device = 6;

  // Whether force app standby for small battery device setting is enabled
  optional bool force_all_apps_standby_for_small_battery = 7;

  // Whether device is charging
  optional bool is_charging = 8;
}
+1 −0
Original line number Diff line number Diff line
@@ -213,6 +213,7 @@ public class SettingsBackupTest {
                    Settings.Global.FANCY_IME_ANIMATIONS,
                    Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
                    Settings.Global.FORCED_APP_STANDBY_ENABLED,
                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
                    Settings.Global.FSTRIM_MANDATORY_INTERVAL,
                    Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
                    Settings.Global.GLOBAL_HTTP_PROXY_HOST,
+130 −32
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -89,6 +91,9 @@ public class ForceAppStandbyTracker {

    private final MyHandler mHandler;

    @VisibleForTesting
    FeatureFlagsObserver mFlagsObserver;

    /**
     * Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
     */
@@ -113,14 +118,36 @@ public class ForceAppStandbyTracker {
    @GuardedBy("mLock")
    boolean mStarted;

    /**
     * Only used for small battery use-case.
     */
    @GuardedBy("mLock")
    boolean mIsPluggedIn;

    @GuardedBy("mLock")
    boolean mBatterySaverEnabled;

    /**
     * True if the forced app standby is currently enabled
     */
    @GuardedBy("mLock")
    boolean mForceAllAppsStandby;

    /**
     * True if the forced app standby for small battery devices feature is enabled in settings
     */
    @GuardedBy("mLock")
    boolean mForceAllAppsStandby;   // True if device is in extreme battery saver mode
    boolean mForceAllAppStandbyForSmallBattery;

    /**
     * True if the forced app standby feature is enabled in settings
     */
    @GuardedBy("mLock")
    boolean mForcedAppStandbyEnabled;   // True if the forced app standby feature is enabled
    boolean mForcedAppStandbyEnabled;

    private class FeatureFlagObserver extends ContentObserver {
        FeatureFlagObserver() {
    @VisibleForTesting
    class FeatureFlagsObserver extends ContentObserver {
        FeatureFlagsObserver() {
            super(null);
        }

@@ -128,6 +155,9 @@ public class ForceAppStandbyTracker {
            mContext.getContentResolver().registerContentObserver(
                    Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
                    false, this);

            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this);
        }

        boolean isForcedAppStandbyEnabled() {
@@ -135,8 +165,14 @@ public class ForceAppStandbyTracker {
                    Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
        }

        boolean isForcedAppStandbyForSmallBatteryEnabled() {
            return Settings.Global.getInt(mContext.getContentResolver(),
                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
        }

        @Override
        public void onChange(boolean selfChange) {
        public void onChange(boolean selfChange, Uri uri) {
            if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) {
                final boolean enabled = isForcedAppStandbyEnabled();
                synchronized (mLock) {
                    if (mForcedAppStandbyEnabled == enabled) {
@@ -144,11 +180,28 @@ public class ForceAppStandbyTracker {
                    }
                    mForcedAppStandbyEnabled = enabled;
                    if (DEBUG) {
                    Slog.d(TAG,
                            "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled);
                        Slog.d(TAG,"Forced app standby feature flag changed: "
                                + mForcedAppStandbyEnabled);
                    }
                }
                mHandler.notifyForcedAppStandbyFeatureFlagChanged();
            } else if (Settings.Global.getUriFor(
                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) {
                final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled();
                synchronized (mLock) {
                    if (mForceAllAppStandbyForSmallBattery == enabled) {
                        return;
                    }
                    mForceAllAppStandbyForSmallBattery = enabled;
                    if (DEBUG) {
                        Slog.d(TAG, "Forced app standby for small battery feature flag changed: "
                                + mForceAllAppStandbyForSmallBattery);
                    }
                    updateForceAllAppStandbyState();
                }
            } else {
                Slog.w(TAG, "Unexpected feature flag uri encountered: " + uri);
            }
            mHandler.notifyFeatureFlagChanged();
        }
    }

@@ -289,9 +342,11 @@ public class ForceAppStandbyTracker {
            mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
            mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
            mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
            final FeatureFlagObserver flagObserver = new FeatureFlagObserver();
            flagObserver.register();
            mForcedAppStandbyEnabled = flagObserver.isForcedAppStandbyEnabled();
            mFlagsObserver = new FeatureFlagsObserver();
            mFlagsObserver.register();
            mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
            mForceAllAppStandbyForSmallBattery =
                    mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();

            try {
                mIActivityManager.registerUidObserver(new UidObserver(),
@@ -306,16 +361,24 @@ public class ForceAppStandbyTracker {

            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_USER_REMOVED);
            filter.addAction(Intent.ACTION_BATTERY_CHANGED);
            mContext.registerReceiver(new MyReceiver(), filter);

            refreshForcedAppStandbyUidPackagesLocked();

            mPowerManagerInternal.registerLowPowerModeObserver(
                    ServiceType.FORCE_ALL_APPS_STANDBY,
                    (state) -> updateForceAllAppsStandby(state.batterySaverEnabled));
                    (state) -> {
                        synchronized (mLock) {
                            mBatterySaverEnabled = state.batterySaverEnabled;
                            updateForceAllAppStandbyState();
                        }
                    });

            mBatterySaverEnabled = mPowerManagerInternal.getLowPowerState(
                    ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled;

            updateForceAllAppsStandby(mPowerManagerInternal.getLowPowerState(
                    ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled);
            updateForceAllAppStandbyState();
        }
    }

@@ -340,6 +403,11 @@ public class ForceAppStandbyTracker {
        return LocalServices.getService(PowerManagerInternal.class);
    }

    @VisibleForTesting
    boolean isSmallBatteryDevice() {
        return ActivityManager.isSmallBatteryDevice();
    }

    /**
     * Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
     */
@@ -369,11 +437,20 @@ public class ForceAppStandbyTracker {
        }
    }

    private void updateForceAllAppStandbyState() {
        synchronized (mLock) {
            if (mForceAllAppStandbyForSmallBattery && isSmallBatteryDevice()) {
                toggleForceAllAppsStandbyLocked(!mIsPluggedIn);
            } else {
                toggleForceAllAppsStandbyLocked(mBatterySaverEnabled);
            }
        }
    }

    /**
     * Update {@link #mForceAllAppsStandby} and notifies the listeners.
     */
    void updateForceAllAppsStandby(boolean enable) {
        synchronized (mLock) {
    private void toggleForceAllAppsStandbyLocked(boolean enable) {
        if (enable == mForceAllAppsStandby) {
            return;
        }
@@ -381,7 +458,6 @@ public class ForceAppStandbyTracker {

        mHandler.notifyForceAllAppsStandbyChanged();
    }
    }

    private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
        final int size = mRunAnyRestrictedPackages.size();
@@ -515,6 +591,11 @@ public class ForceAppStandbyTracker {
                if (userId > 0) {
                    mHandler.doUserRemoved(userId);
                }
            } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
                synchronized (mLock) {
                    mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
                }
                updateForceAllAppStandbyState();
            }
        }
    }
@@ -533,7 +614,7 @@ public class ForceAppStandbyTracker {
        private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
        private static final int MSG_FORCE_ALL_CHANGED = 6;
        private static final int MSG_USER_REMOVED = 7;
        private static final int MSG_FEATURE_FLAG_CHANGED = 8;
        private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;

        public MyHandler(Looper looper) {
            super(looper);
@@ -563,8 +644,8 @@ public class ForceAppStandbyTracker {
            obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
        }

        public void notifyFeatureFlagChanged() {
            obtainMessage(MSG_FEATURE_FLAG_CHANGED).sendToTarget();
        public void notifyForcedAppStandbyFeatureFlagChanged() {
            obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
        }

        public void doUserRemoved(int userId) {
@@ -618,7 +699,7 @@ public class ForceAppStandbyTracker {
                        l.onForceAllAppsStandbyChanged(sender);
                    }
                    return;
                case MSG_FEATURE_FLAG_CHANGED:
                case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
                    // Feature flag for forced app standby changed.
                    final boolean unblockAlarms;
                    synchronized (mLock) {
@@ -844,6 +925,18 @@ public class ForceAppStandbyTracker {
            pw.print("Force all apps standby: ");
            pw.println(isForceAllAppsStandbyEnabled());

            pw.print(indent);
            pw.print("Small Battery Device: ");
            pw.println(isSmallBatteryDevice());

            pw.print(indent);
            pw.print("Force all apps standby for small battery device: ");
            pw.println(mForceAllAppStandbyForSmallBattery);

            pw.print(indent);
            pw.print("Plugged In: ");
            pw.println(mIsPluggedIn);

            pw.print(indent);
            pw.print("Foreground uids: [");

@@ -883,6 +976,11 @@ public class ForceAppStandbyTracker {
            final long token = proto.start(fieldId);

            proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY, mForceAllAppsStandby);
            proto.write(ForceAppStandbyTrackerProto.IS_SMALL_BATTERY_DEVICE,
                    isSmallBatteryDevice());
            proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY,
                    mForceAllAppStandbyForSmallBattery);
            proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsPluggedIn);

            for (int i = 0; i < mForegroundUids.size(); i++) {
                if (mForegroundUids.valueAt(i)) {
+57 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager.ServiceType;
@@ -51,6 +52,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.mock.MockContentResolver;
@@ -105,6 +107,9 @@ public class ForceAppStandbyTrackerTest {
        PowerManagerInternal injectPowerManagerInternal() {
            return mMockPowerManagerInternal;
        }

        @Override
        boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; };
    }

    private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1;
@@ -144,6 +149,7 @@ public class ForceAppStandbyTrackerTest {
    private FakeSettingsProvider mFakeSettingsProvider;

    private boolean mPowerSaveMode;
    private boolean mIsSmallBatteryDevice;

    private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet();

@@ -232,6 +238,7 @@ public class ForceAppStandbyTrackerTest {
        assertNotNull(mAppOpsCallback);
        assertNotNull(mPowerSaveObserver);
        assertNotNull(mReceiver);
        assertNotNull(instance.mFlagsObserver);
    }

    private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException {
@@ -852,6 +859,56 @@ public class ForceAppStandbyTrackerTest {
        assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
    }

    @Test
    public void testSmallBatteryAndPluggedIn() throws Exception {
        // This is a small battery device
        mIsSmallBatteryDevice = true;

        final ForceAppStandbyTrackerTestable instance = newInstance();
        callStart(instance);
        assertFalse(instance.isForceAllAppsStandbyEnabled());

        // Setting/experiment for all app standby for small battery is enabled
        Global.putInt(mMockContentResolver, Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1);
        instance.mFlagsObserver.onChange(true,
                Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED));
        assertTrue(instance.isForceAllAppsStandbyEnabled());

        // When battery is plugged in, force app standby is disabled
        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
        intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
        mReceiver.onReceive(mMockContext, intent);
        assertFalse(instance.isForceAllAppsStandbyEnabled());

        // When battery stops plugged in, force app standby is enabled
        mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED));
        assertTrue(instance.isForceAllAppsStandbyEnabled());
    }

    @Test
    public void testNotSmallBatteryAndPluggedIn() throws Exception {
        // Not a small battery device, so plugged in status should not affect forced app standby
        mIsSmallBatteryDevice = false;

        final ForceAppStandbyTrackerTestable instance = newInstance();
        callStart(instance);
        assertFalse(instance.isForceAllAppsStandbyEnabled());

        mPowerSaveMode = true;
        mPowerSaveObserver.accept(getPowerSaveState());
        assertTrue(instance.isForceAllAppsStandbyEnabled());

        // When battery is plugged in, force app standby is unaffected
        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
        intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
        mReceiver.onReceive(mMockContext, intent);
        assertTrue(instance.isForceAllAppsStandbyEnabled());

        // When battery stops plugged in, force app standby is unaffected
        mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED));
        assertTrue(instance.isForceAllAppsStandbyEnabled());
    }

    static int[] array(int... appIds) {
        Arrays.sort(appIds);
        return appIds;