Loading core/java/android/provider/Settings.java +8 −0 Original line number Diff line number Diff line Loading @@ -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) Loading core/tests/coretests/src/android/provider/SettingsBackupTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -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, Loading services/core/java/com/android/server/AlarmManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading services/core/java/com/android/server/ForceAppStandbyTracker.java +84 −15 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -64,10 +67,11 @@ import java.util.List; * 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; Loading Loading @@ -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 { /** Loading Loading @@ -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(), Loading Loading @@ -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 Loading Loading @@ -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); Loading @@ -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(); } Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -701,7 +767,7 @@ public class ForceAppStandbyTracker { return true; } return isRunAnyRestrictedLocked(uid, packageName); return mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName); } } Loading Loading @@ -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()); Loading services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java +16 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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: Loading Loading @@ -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 { Loading Loading
core/java/android/provider/Settings.java +8 −0 Original line number Diff line number Diff line Loading @@ -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) Loading
core/tests/coretests/src/android/provider/SettingsBackupTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -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, Loading
services/core/java/com/android/server/AlarmManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
services/core/java/com/android/server/ForceAppStandbyTracker.java +84 −15 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -64,10 +67,11 @@ import java.util.List; * 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; Loading Loading @@ -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 { /** Loading Loading @@ -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(), Loading Loading @@ -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 Loading Loading @@ -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); Loading @@ -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(); } Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -701,7 +767,7 @@ public class ForceAppStandbyTracker { return true; } return isRunAnyRestrictedLocked(uid, packageName); return mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName); } } Loading Loading @@ -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()); Loading
services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java +16 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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: Loading Loading @@ -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 { Loading