diff options
| author | 2020-05-01 15:06:18 -0700 | |
|---|---|---|
| committer | 2020-05-06 14:05:03 -0700 | |
| commit | e6601764467007b02e892e013920ae67464581b9 (patch) | |
| tree | 91b638f313de790419ed82fd04ee6e90f0da9703 | |
| parent | bf8d4b4480dca70eac6f408204d50c97ebab85f6 (diff) | |
Add charging parole notification for network changes.
This is a partial revert of the change to remove parole from
AppStandbyController (Ide382ad7fb9c7441f0a5232833ad39bf8c3a1e94). This
adds back the listener and notification of when parole (due to charging)
turns on and off so that NetworkPolicyManagerService can quickly update
the network policy for affected apps. AlarmManager and JobScheduler
already listen to charging status, so I'm excluding them from this
change to make it as simple and low-risk as possible.
This has a slight change to follow standard practice where a client
registering a listener retrieves the current value after registering
the listener instead of the registerListener() method calling the
listener's onStateChanged() method.
Bug: 151802309
Test: atest FrameworksServicesTests:AppStandbyControllerTests
Test: atest --rerun-until-failure 10 com.android.cts.net.HostsideRestrictBackgroundNetworkTests
Change-Id: I18d8d771d455456351e3a9a6c9469e4ec620ae4e
4 files changed, 167 insertions, 10 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index 6d9e3eddf616..887d82c6413f 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -45,6 +45,14 @@ public interface AppStandbyInternal { boolean idle, int bucket, int reason); /** + * Callback to inform listeners that the parole state has changed. This means apps are + * allowed to do work even if they're idle or in a low bucket. + */ + public void onParoleStateChanged(boolean isParoleOn) { + // No-op by default + } + + /** * Optional callback to inform the listener that the app has transitioned into * an active state due to user interaction. */ @@ -92,6 +100,11 @@ public interface AppStandbyInternal { boolean isAppIdleFiltered(String packageName, int appId, int userId, long elapsedRealtime); + /** + * @return true if currently app idle parole mode is on. + */ + boolean isInParole(); + int[] getIdleUidsForUser(int userId); void setAppIdleAsync(String packageName, boolean idle, int userId); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 24728dd8edca..cb5cb175ff24 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -214,8 +214,7 @@ public class AppStandbyController implements AppStandbyInternal { private AppIdleHistory mAppIdleHistory; @GuardedBy("mPackageAccessListeners") - private ArrayList<AppIdleStateChangeListener> - mPackageAccessListeners = new ArrayList<>(); + private final ArrayList<AppIdleStateChangeListener> mPackageAccessListeners = new ArrayList<>(); /** Whether we've queried the list of carrier privileged apps. */ @GuardedBy("mAppIdleLock") @@ -235,6 +234,7 @@ public class AppStandbyController implements AppStandbyInternal { static final int MSG_FORCE_IDLE_STATE = 4; static final int MSG_CHECK_IDLE_STATES = 5; static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8; + static final int MSG_PAROLE_STATE_CHANGED = 9; static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10; /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */ static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11; @@ -390,7 +390,16 @@ public class AppStandbyController implements AppStandbyInternal { @VisibleForTesting void setAppIdleEnabled(boolean enabled) { - mAppIdleEnabled = enabled; + synchronized (mAppIdleLock) { + if (mAppIdleEnabled != enabled) { + final boolean oldParoleState = isInParole(); + mAppIdleEnabled = enabled; + if (isInParole() != oldParoleState) { + postParoleStateChanged(); + } + } + } + } @Override @@ -563,11 +572,23 @@ public class AppStandbyController implements AppStandbyInternal { if (mIsCharging != isCharging) { if (DEBUG) Slog.d(TAG, "Setting mIsCharging to " + isCharging); mIsCharging = isCharging; + postParoleStateChanged(); } } } @Override + public boolean isInParole() { + return !mAppIdleEnabled || mIsCharging; + } + + private void postParoleStateChanged() { + if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED"); + mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED); + mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED); + } + + @Override public void postCheckIdleStates(int userId) { mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0)); } @@ -1502,6 +1523,15 @@ public class AppStandbyController implements AppStandbyInternal { } } + private void informParoleStateChanged() { + final boolean paroled = isInParole(); + synchronized (mPackageAccessListeners) { + for (AppIdleStateChangeListener listener : mPackageAccessListeners) { + listener.onParoleStateChanged(paroled); + } + } + } + @Override public void flushToDisk(int userId) { synchronized (mAppIdleLock) { @@ -1920,6 +1950,11 @@ public class AppStandbyController implements AppStandbyInternal { args.recycle(); break; + case MSG_PAROLE_STATE_CHANGED: + if (DEBUG) Slog.d(TAG, "Parole state: " + isInParole()); + informParoleStateChanged(); + break; + case MSG_CHECK_PACKAGE_IDLE_STATE: checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2, mInjector.elapsedRealtime()); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index ec941c8aea59..3283fd9b2c51 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -799,7 +799,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { writePolicyAL(); } - enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, true); setRestrictBackgroundUL(mLoadedRestrictBackground, "init_service"); updateRulesForGlobalChangeAL(false); updateNotificationsNL(); @@ -871,6 +870,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { new NetworkRequest.Builder().build(), mNetworkCallback); mAppStandby.addListener(new NetPolicyAppIdleStateChangeListener()); + synchronized (mUidRulesFirstLock) { + updateRulesForAppIdleParoleUL(); + } // Listen for subscriber changes mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener( @@ -3893,6 +3895,39 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** + * Toggle the firewall standby chain and inform listeners if the uid rules have effectively + * changed. + */ + @GuardedBy("mUidRulesFirstLock") + private void updateRulesForAppIdleParoleUL() { + final boolean paroled = mAppStandby.isInParole(); + final boolean enableChain = !paroled; + enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, enableChain); + + int ruleCount = mUidFirewallStandbyRules.size(); + for (int i = 0; i < ruleCount; i++) { + final int uid = mUidFirewallStandbyRules.keyAt(i); + int oldRules = mUidRules.get(uid); + if (enableChain) { + // Chain wasn't enabled before and the other power-related + // chains are whitelists, so we can clear the + // MASK_ALL_NETWORKS part of the rules and re-inform listeners if + // the effective rules result in blocking network access. + oldRules &= MASK_METERED_NETWORKS; + } else { + // Skip if it had no restrictions to begin with + if ((oldRules & MASK_ALL_NETWORKS) == 0) continue; + } + final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules, paroled); + if (newUidRules == RULE_NONE) { + mUidRules.delete(uid); + } else { + mUidRules.put(uid, newUidRules); + } + } + } + + /** * Update rules that might be changed by {@link #mRestrictBackground}, * {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value. */ @@ -4347,7 +4382,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private void updateRulesForPowerRestrictionsUL(int uid) { final int oldUidRules = mUidRules.get(uid, RULE_NONE); - final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules); + final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false); if (newUidRules == RULE_NONE) { mUidRules.delete(uid); @@ -4361,28 +4396,30 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * * @param uid the uid of the app to update rules for * @param oldUidRules the current rules for the uid, in order to determine if there's a change + * @param paroled whether to ignore idle state of apps and only look at other restrictions * * @return the new computed rules for the uid */ - private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules) { + private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) { if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, - "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules); + "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/" + + (paroled ? "P" : "-")); } try { - return updateRulesForPowerRestrictionsULInner(uid, oldUidRules); + return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, paroled); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } - private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules) { + private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, boolean paroled) { if (!isUidValidForBlacklistRules(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid); return RULE_NONE; } - final boolean isIdle = isUidIdle(uid); + final boolean isIdle = !paroled && isUidIdle(uid); final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode; final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); @@ -4452,6 +4489,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } catch (NameNotFoundException nnfe) { } } + + @Override + public void onParoleStateChanged(boolean isParoleOn) { + synchronized (mUidRulesFirstLock) { + mLogger.paroleStateChanged(isParoleOn); + updateRulesForAppIdleParoleUL(); + } + } } private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) { diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 39062f017a73..6718db768fdb 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -366,29 +366,87 @@ public class AppStandbyControllerTests { mInjector.mElapsedRealtime, false)); } + private static class TestParoleListener extends AppIdleStateChangeListener { + private boolean mIsParoleOn = false; + private CountDownLatch mLatch; + private boolean mIsExpecting = false; + private boolean mExpectedParoleState; + + boolean getParoleState() { + synchronized (this) { + return mIsParoleOn; + } + } + + void rearmLatch(boolean expectedParoleState) { + synchronized (this) { + mLatch = new CountDownLatch(1); + mIsExpecting = true; + mExpectedParoleState = expectedParoleState; + } + } + + void awaitOnLatch(long time) throws Exception { + mLatch.await(time, TimeUnit.MILLISECONDS); + } + + @Override + public void onAppIdleStateChanged(String packageName, int userId, boolean idle, + int bucket, int reason) { + } + + @Override + public void onParoleStateChanged(boolean isParoleOn) { + synchronized (this) { + // Only record information if it is being looked for + if (mLatch != null && mLatch.getCount() > 0) { + mIsParoleOn = isParoleOn; + if (mIsExpecting && isParoleOn == mExpectedParoleState) { + mLatch.countDown(); + } + } + } + } + } + @Test public void testIsAppIdle_Charging() throws Exception { + TestParoleListener paroleListener = new TestParoleListener(); + mController.addListener(paroleListener); + setChargingState(mController, false); mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + assertFalse(mController.isInParole()); + paroleListener.rearmLatch(true); setChargingState(mController, true); + paroleListener.awaitOnLatch(2000); + assertTrue(paroleListener.getParoleState()); assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + assertTrue(mController.isInParole()); + paroleListener.rearmLatch(false); setChargingState(mController, false); + paroleListener.awaitOnLatch(2000); + assertFalse(paroleListener.getParoleState()); assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + assertFalse(mController.isInParole()); } @Test public void testIsAppIdle_Enabled() throws Exception { setChargingState(mController, false); + TestParoleListener paroleListener = new TestParoleListener(); + mController.addListener(paroleListener); + setAppIdleEnabled(mController, true); mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); @@ -396,11 +454,17 @@ public class AppStandbyControllerTests { assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + paroleListener.rearmLatch(false); setAppIdleEnabled(mController, false); + paroleListener.awaitOnLatch(2000); + assertTrue(paroleListener.mIsParoleOn); assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + paroleListener.rearmLatch(true); setAppIdleEnabled(mController, true); + paroleListener.awaitOnLatch(2000); + assertFalse(paroleListener.getParoleState()); assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); |