Added runtime flag for forced-app-standby
Added a global setting which can be configured to get metrics on the
battery savings from this feature.
Test: atest \
FrameworksServicesTests:\
com.android.server.job.BackgroundRestrictionsTest
Fixes: 70579515
Change-Id: I541f2aac39847f9bf372240cafc4e0f0e5ebdc86
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2ec4906..66d1bba 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9802,6 +9802,14 @@
public static final java.lang.String APP_STANDBY_ENABLED = "app_standby_enabled";
/**
+ * Feature flag to enable or disable the Forced App Standby feature.
+ * Type: int (0 for false, 1 for true)
+ * Default: 1
+ * @hide
+ */
+ public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
+
+ /**
* Whether or not Network Watchlist feature is enabled.
* Type: int (0 for false, 1 for true)
* Default: 0
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 7cfedc8..68789f3 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -211,6 +211,7 @@
Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
+ Settings.Global.FORCED_APP_STANDBY_ENABLED,
Settings.Global.FSTRIM_MANDATORY_INTERVAL,
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
Settings.Global.GLOBAL_HTTP_PROXY_HOST,
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index a024d5a..86063c3 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1162,12 +1162,12 @@
// ignored; both services live in system_server
}
publishBinderService(Context.ALARM_SERVICE, mService);
- mForceAppStandbyTracker.start();
}
@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
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 61d3833..8776f3a 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -33,8 +34,10 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.ArraySet;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
@@ -59,15 +62,16 @@
* - Global "force all apps standby" mode enforced by battery saver.
*
* TODO: In general, we can reduce the number of callbacks by checking all signals before sending
- * each callback. For example, even when an UID comes into the foreground, if it wasn't
- * originally restricted, then there's no need to send an event.
- * Doing this would be error-prone, so we punt it for now, but we should revisit it later.
+ * each callback. For example, even when an UID comes into the foreground, if it wasn't
+ * originally restricted, then there's no need to send an event.
+ * Doing this would be error-prone, so we punt it for now, but we should revisit it later.
*
* 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/ForceAppStandbyTrackerTest.java
*/
public class ForceAppStandbyTracker {
private static final String TAG = "ForceAppStandbyTracker";
+ private static final boolean DEBUG = false;
@GuardedBy("ForceAppStandbyTracker.class")
private static ForceAppStandbyTracker sInstance;
@@ -107,7 +111,43 @@
boolean mStarted;
@GuardedBy("mLock")
- boolean mForceAllAppsStandby;
+ boolean mForceAllAppsStandby; // True if device is in extreme battery saver mode
+
+ @GuardedBy("mLock")
+ boolean mForcedAppStandbyEnabled; // True if the forced app standby feature is enabled
+
+ private class FeatureFlagObserver extends ContentObserver {
+ FeatureFlagObserver() {
+ super(null);
+ }
+
+ void register() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
+ false, this);
+ }
+
+ boolean isForcedAppStandbyEnabled() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ final boolean enabled = isForcedAppStandbyEnabled();
+ synchronized (mLock) {
+ if (mForcedAppStandbyEnabled == enabled) {
+ return;
+ }
+ mForcedAppStandbyEnabled = enabled;
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled);
+ }
+ }
+ mHandler.notifyFeatureFlagChanged();
+ }
+ }
public static abstract class Listener {
/**
@@ -246,6 +286,9 @@
mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
+ final FeatureFlagObserver flagObserver = new FeatureFlagObserver();
+ flagObserver.register();
+ mForcedAppStandbyEnabled = flagObserver.isForcedAppStandbyEnabled();
try {
mIActivityManager.registerUidObserver(new UidObserver(),
@@ -364,7 +407,7 @@
*/
boolean updateForcedAppStandbyUidPackageLocked(int uid, @NonNull String packageName,
boolean restricted) {
- final int index = findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
+ final int index = findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
final boolean wasRestricted = index >= 0;
if (wasRestricted == restricted) {
return false;
@@ -418,25 +461,30 @@
}
private final class UidObserver extends IUidObserver.Stub {
- @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq) {
}
- @Override public void onUidGone(int uid, boolean disabled) {
+ @Override
+ public void onUidGone(int uid, boolean disabled) {
uidToBackground(uid, /*remove=*/ true);
}
- @Override public void onUidActive(int uid) {
+ @Override
+ public void onUidActive(int uid) {
uidToForeground(uid);
}
- @Override public void onUidIdle(int uid, boolean disabled) {
+ @Override
+ public void onUidIdle(int uid, boolean disabled) {
// Just to avoid excessive memcpy, don't remove from the array in this case.
uidToBackground(uid, /*remove=*/ false);
}
- @Override public void onUidCachedChanged(int uid, boolean cached) {
+ @Override
+ public void onUidCachedChanged(int uid, boolean cached) {
}
- };
+ }
private final class AppOpsWatcher extends IAppOpsCallback.Stub {
@Override
@@ -481,8 +529,8 @@
private static final int MSG_ALL_WHITELIST_CHANGED = 4;
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;
public MyHandler(Looper looper) {
super(looper);
@@ -491,6 +539,7 @@
public void notifyUidForegroundStateChanged(int uid) {
obtainMessage(MSG_UID_STATE_CHANGED, uid, 0).sendToTarget();
}
+
public void notifyRunAnyAppOpsChanged(int uid, @NonNull String packageName) {
obtainMessage(MSG_RUN_ANY_CHANGED, uid, 0, packageName).sendToTarget();
}
@@ -511,12 +560,16 @@
obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
}
+ public void notifyFeatureFlagChanged() {
+ obtainMessage(MSG_FEATURE_FLAG_CHANGED).sendToTarget();
+ }
+
public void doUserRemoved(int userId) {
obtainMessage(MSG_USER_REMOVED, userId, 0).sendToTarget();
}
@Override
- public void dispatchMessage(Message msg) {
+ public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_REMOVED:
handleUserRemoved(msg.arg1);
@@ -562,6 +615,19 @@
l.onForceAllAppsStandbyChanged(sender);
}
return;
+ case MSG_FEATURE_FLAG_CHANGED:
+ // Feature flag for forced app standby changed.
+ final boolean unblockAlarms;
+ synchronized (mLock) {
+ unblockAlarms = !mForcedAppStandbyEnabled && !mForceAllAppsStandby;
+ }
+ for (Listener l: cloneListeners()) {
+ l.updateAllJobs();
+ if (unblockAlarms) {
+ l.unblockAllUnrestrictedAlarms();
+ }
+ }
+ return;
case MSG_USER_REMOVED:
handleUserRemoved(msg.arg1);
return;
@@ -701,7 +767,7 @@
return true;
}
- return isRunAnyRestrictedLocked(uid, packageName);
+ return mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName);
}
}
@@ -766,6 +832,9 @@
public void dump(PrintWriter pw, String indent) {
synchronized (mLock) {
pw.print(indent);
+ pw.println("Forced App Standby Feature enabled: " + mForcedAppStandbyEnabled);
+
+ pw.print(indent);
pw.print("Force all apps standby: ");
pw.println(isForceAllAppsStandbyEnabled());
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index 467b47a..14b118e 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -37,6 +37,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
@@ -50,7 +51,6 @@
import org.junit.runner.RunWith;
/**
- * TODO: Also add a test for temp power whitelist
* Tests that background restrictions on jobs work as expected.
* This test requires test-apps/JobTestApp to be installed on the device.
* To run this test from root of checkout:
@@ -144,15 +144,29 @@
awaitJobStop(DEFAULT_WAIT_TIMEOUT));
}
+ @Test
+ public void testFeatureFlag() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.FORCED_APP_STANDBY_ENABLED, 0);
+ scheduleAndAssertJobStarted();
+ setAppOpsModeAllowed(false);
+ mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
+ assertFalse("Job stopped even when feature flag was disabled",
+ awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+ }
+
@After
public void tearDown() throws Exception {
- Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
+ final Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
cancelJobsIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
cancelJobsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(cancelJobsIntent);
mContext.unregisterReceiver(mJobStateChangeReceiver);
+ Thread.sleep(500); // To avoid race with register in the next setUp
setAppOpsModeAllowed(true);
setPowerWhiteListed(false);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.FORCED_APP_STANDBY_ENABLED, 1);
}
private void setPowerWhiteListed(boolean whitelist) throws RemoteException {