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
This commit is contained in:
@@ -9801,6 +9801,14 @@ public final class Settings {
|
||||
*/
|
||||
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)
|
||||
|
||||
@@ -211,6 +211,7 @@ public class SettingsBackupTest {
|
||||
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,
|
||||
|
||||
@@ -1162,12 +1162,12 @@ class AlarmManagerService extends SystemService {
|
||||
// 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
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.content.BroadcastReceiver;
|
||||
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.PowerManagerInternal;
|
||||
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 @@ import java.util.List;
|
||||
* - 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 @@ public class ForceAppStandbyTracker {
|
||||
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 @@ 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();
|
||||
|
||||
try {
|
||||
mIActivityManager.registerUidObserver(new UidObserver(),
|
||||
@@ -364,7 +407,7 @@ public class ForceAppStandbyTracker {
|
||||
*/
|
||||
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 @@ public class ForceAppStandbyTracker {
|
||||
}
|
||||
|
||||
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 @@ public class ForceAppStandbyTracker {
|
||||
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 class ForceAppStandbyTracker {
|
||||
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 @@ public class ForceAppStandbyTracker {
|
||||
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 @@ public class ForceAppStandbyTracker {
|
||||
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 @@ public class ForceAppStandbyTracker {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isRunAnyRestrictedLocked(uid, packageName);
|
||||
return mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,6 +831,9 @@ public class ForceAppStandbyTracker {
|
||||
|
||||
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());
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.os.RemoteException;
|
||||
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.Test;
|
||||
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 @@ public class BackgroundRestrictionsTest {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user