Merge "Add charging parole notification for network changes." into rvc-dev

This commit is contained in:
Kweku Adams
2020-05-07 01:25:29 +00:00
committed by Android (Google) Code Review
4 changed files with 167 additions and 10 deletions

View File

@@ -44,6 +44,14 @@ public interface AppStandbyInternal {
public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
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);

View File

@@ -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,10 +572,22 @@ 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());

View File

@@ -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(
@@ -3892,6 +3894,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) {

View File

@@ -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));