summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Kweku Adams <kwekua@google.com> 2020-05-01 15:06:18 -0700
committer Kweku Adams <kwekua@google.com> 2020-05-06 14:05:03 -0700
commite6601764467007b02e892e013920ae67464581b9 (patch)
tree91b638f313de790419ed82fd04ee6e90f0da9703
parentbf8d4b4480dca70eac6f408204d50c97ebab85f6 (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
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java13
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java41
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java64
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));