Ability to grant apps in app idle mode network.
This introduces a new whitelist that JobScheduler can use to request
that an app be whitelisted from network restrictions while in app idle.
This is intended to be a temporary whitelist and the app will still be
subject to other power saving restrictions.
Bug: 117846754
Bug: 111423978
Test: atest CtsHostsideNetworkTests
and atest NetworkPolicyManagerServiceTest
Change-Id: I75a77d95c0a2052b456cd011dcbc953fff09f34c
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 4f4b6bf..4bd8f45 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -64,6 +64,7 @@
private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11;
private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12;
private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13;
+ private static final int EVENT_APP_IDLE_WL_CHANGED = 14;
static final int NTWK_BLOCKED_POWER = 0;
static final int NTWK_ALLOWED_NON_METERED = 1;
@@ -145,6 +146,13 @@
}
}
+ void appIdleWlChanged(int uid, boolean isWhitelisted) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG, getAppIdleWlChangedLog(uid, isWhitelisted));
+ mEventsBuffer.appIdleWlChanged(uid, isWhitelisted);
+ }
+ }
+
void paroleStateChanged(boolean paroleOn) {
synchronized (mLock) {
if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn));
@@ -259,6 +267,10 @@
return "App idle state of uid " + uid + ": " + idle;
}
+ private static String getAppIdleWlChangedLog(int uid, boolean isWhitelisted) {
+ return "App idle whitelist state of uid " + uid + ": " + isWhitelisted;
+ }
+
private static String getParoleStateChanged(boolean paroleOn) {
return "Parole state: " + paroleOn;
}
@@ -409,6 +421,17 @@
data.timeStamp = System.currentTimeMillis();
}
+ public void appIdleWlChanged(int uid, boolean isWhitelisted) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_APP_IDLE_WL_CHANGED;
+ data.ifield1 = uid;
+ data.bfield1 = isWhitelisted;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
public void paroleStateChanged(boolean paroleOn) {
final Data data = getNextSlot();
if (data == null) return;
@@ -487,6 +510,8 @@
return getDeviceIdleModeEnabled(data.bfield1);
case EVENT_APP_IDLE_STATE_CHANGED:
return getAppIdleChangedLog(data.ifield1, data.bfield1);
+ case EVENT_APP_IDLE_WL_CHANGED:
+ return getAppIdleWlChangedLog(data.ifield1, data.bfield1);
case EVENT_PAROLE_STATE_CHANGED:
return getParoleStateChanged(data.bfield1);
case EVENT_TEMP_POWER_SAVE_WL_CHANGED:
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 099671d..7f650ee 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -105,6 +105,12 @@
public abstract void onAdminDataAvailable();
/**
+ * Control if a UID should be whitelisted even if it's in app idle mode. Other restrictions may
+ * still be in effect.
+ */
+ public abstract void setAppIdleWhitelist(int uid, boolean shouldWhitelist);
+
+ /**
* Sets a list of packages which are restricted by admin from accessing metered data.
*
* @param packageNames the list of restricted packages.
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index d799642..7750c37 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -464,6 +464,10 @@
@GuardedBy("mUidRulesFirstLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
+ // "Power save mode" is the concept used in the DeviceIdleController that includes various
+ // features including Doze and Battery Saver. It include Battery Saver, but "power save mode"
+ // and "battery saver" are not equivalent.
+
/**
* UIDs that have been white-listed to always be able to have network access
* in power save mode, except device idle (doze) still applies.
@@ -484,6 +488,13 @@
private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray();
/**
+ * UIDs that have been white-listed temporarily to be able to have network access despite being
+ * idle. Other power saving restrictions still apply.
+ */
+ @GuardedBy("mUidRulesFirstLock")
+ private final SparseBooleanArray mAppIdleTempWhitelistAppIds = new SparseBooleanArray();
+
+ /**
* UIDs that have been initially white-listed by system to avoid restricted background.
*/
@GuardedBy("mUidRulesFirstLock")
@@ -3372,6 +3383,20 @@
fout.decreaseIndent();
}
+ size = mAppIdleTempWhitelistAppIds.size();
+ if (size > 0) {
+ fout.println("App idle whitelist app ids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mAppIdleTempWhitelistAppIds.keyAt(i));
+ fout.print(": ");
+ fout.print(mAppIdleTempWhitelistAppIds.valueAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
size = mDefaultRestrictBackgroundWhitelistUids.size();
if (size > 0) {
fout.println("Default restrict background whitelist uids:");
@@ -3640,12 +3665,15 @@
}
/**
+ * Returns whether a uid is whitelisted from power saving restrictions (eg: Battery Saver, Doze
+ * mode, and app idle).
+ *
* @param deviceIdleMode if true then we don't consider
* {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is
* whitelisted.
*/
@GuardedBy("mUidRulesFirstLock")
- private boolean isWhitelistedBatterySaverUL(int uid, boolean deviceIdleMode) {
+ private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) {
final int appId = UserHandle.getAppId(uid);
boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
|| mPowerSaveWhitelistAppIds.get(appId);
@@ -3660,7 +3688,7 @@
@GuardedBy("mUidRulesFirstLock")
private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
if (enabled) {
- final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid,
+ final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid,
chain == FIREWALL_CHAIN_DOZABLE);
if (isWhitelisted || isUidForegroundOnRestrictPowerUL(uid)) {
setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW);
@@ -3712,8 +3740,10 @@
if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
&& !isUidForegroundOnRestrictPowerUL(uid)) {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
+ if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid);
} else {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+ if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL " + uid + " to DEFAULT");
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -3896,7 +3926,59 @@
return UserHandle.isApp(uid) && hasInternetPermissions(uid);
}
- private boolean isUidIdle(int uid) {
+ /**
+ * Set whether or not an app should be whitelisted for network access while in app idle. Other
+ * power saving restrictions may still apply.
+ */
+ @VisibleForTesting
+ public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+ synchronized (mUidRulesFirstLock) {
+ if (mAppIdleTempWhitelistAppIds.get(uid) == shouldWhitelist) {
+ // No change.
+ return;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mLogger.appIdleWlChanged(uid, shouldWhitelist);
+ if (shouldWhitelist) {
+ mAppIdleTempWhitelistAppIds.put(uid, true);
+ } else {
+ mAppIdleTempWhitelistAppIds.delete(uid);
+ }
+ updateRuleForAppIdleUL(uid);
+ updateRulesForPowerRestrictionsUL(uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ /** Return the list of UIDs currently in the app idle whitelist. */
+ @VisibleForTesting
+ public int[] getAppIdleWhitelist() {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mUidRulesFirstLock) {
+ final int len = mAppIdleTempWhitelistAppIds.size();
+ int[] uids = new int[len];
+ for (int i = 0; i < len; ++i) {
+ uids[i] = mAppIdleTempWhitelistAppIds.keyAt(i);
+ }
+ return uids;
+ }
+ }
+
+ /** Returns if the UID is currently considered idle. */
+ @VisibleForTesting
+ public boolean isUidIdle(int uid) {
+ synchronized (mUidRulesFirstLock) {
+ if (mAppIdleTempWhitelistAppIds.get(uid)) {
+ // UID is temporarily whitelisted.
+ return false;
+ }
+ }
+
final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
final int userId = UserHandle.getUserId(uid);
@@ -3940,6 +4022,7 @@
mPowerSaveWhitelistExceptIdleAppIds.delete(uid);
mPowerSaveWhitelistAppIds.delete(uid);
mPowerSaveTempWhitelistAppIds.delete(uid);
+ mAppIdleTempWhitelistAppIds.delete(uid);
// ...then update iptables asynchronously.
mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget();
@@ -3984,7 +4067,7 @@
* <li>@{code bw_happy_box}: UIDs added to this chain have access (whitelist), unless they're
* also blacklisted.
* <li>@{code bw_data_saver}: when enabled (through {@link #setRestrictBackground(boolean)}),
- * no UIDs other those whitelisted will have access.
+ * no UIDs other than those whitelisted will have access.
* <ul>
*
* <p>The @{code bw_penalty_box} and @{code bw_happy_box} are primarily managed through the
@@ -4194,7 +4277,7 @@
final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
- final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, mDeviceIdleMode);
+ final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
final int oldRule = oldUidRules & MASK_ALL_NETWORKS;
int newRule = RULE_NONE;
@@ -5023,6 +5106,11 @@
}
@Override
+ public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+ NetworkPolicyManagerService.this.setAppIdleWhitelist(uid, shouldWhitelist);
+ }
+
+ @Override
public void setMeteredRestrictedPackages(Set<String> packageNames, int userId) {
setMeteredRestrictedPackagesInternal(packageNames, userId);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 56d41c5..156c01d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -78,6 +78,8 @@
pw.println(" Adds a UID to the whitelist for restrict background usage.");
pw.println(" add restrict-background-blacklist UID");
pw.println(" Adds a UID to the blacklist for restrict background usage.");
+ pw.println(" add app-idle-whitelist UID");
+ pw.println(" Adds a UID to the temporary app idle whitelist.");
pw.println(" get restrict-background");
pw.println(" Gets the global restrict background usage status.");
pw.println(" list wifi-networks [true|false]");
@@ -92,6 +94,8 @@
pw.println(" Removes a UID from the whitelist for restrict background usage.");
pw.println(" remove restrict-background-blacklist UID");
pw.println(" Removes a UID from the blacklist for restrict background usage.");
+ pw.println(" remove app-idle-whitelist UID");
+ pw.println(" Removes a UID from the temporary app idle whitelist.");
pw.println(" set metered-network ID [undefined|true|false]");
pw.println(" Toggles whether the given wi-fi network is metered.");
pw.println(" set restrict-background BOOLEAN");
@@ -142,6 +146,8 @@
return -1;
}
switch(type) {
+ case "app-idle-whitelist":
+ return listAppIdleWhitelist();
case "wifi-networks":
return listWifiNetworks();
case "restrict-background-whitelist":
@@ -165,6 +171,8 @@
return addRestrictBackgroundWhitelist();
case "restrict-background-blacklist":
return addRestrictBackgroundBlacklist();
+ case "app-idle-whitelist":
+ return addAppIdleWhitelist();
}
pw.println("Error: unknown add type '" + type + "'");
return -1;
@@ -182,14 +190,20 @@
return removeRestrictBackgroundWhitelist();
case "restrict-background-blacklist":
return removeRestrictBackgroundBlacklist();
+ case "app-idle-whitelist":
+ return removeAppIdleWhitelist();
}
pw.println("Error: unknown remove type '" + type + "'");
return -1;
}
private int listUidPolicies(String msg, int policy) throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
final int[] uids = mInterface.getUidsWithPolicy(policy);
+ return listUidList(msg, uids);
+ }
+
+ private int listUidList(String msg, int[] uids) {
+ final PrintWriter pw = getOutPrintWriter();
pw.print(msg); pw.print(": ");
if (uids.length == 0) {
pw.println("none");
@@ -214,6 +228,12 @@
POLICY_REJECT_METERED_BACKGROUND);
}
+ private int listAppIdleWhitelist() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final int[] uids = mInterface.getAppIdleWhitelist();
+ return listUidList("App Idle whitelisted UIDs", uids);
+ }
+
private int getRestrictBackground() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
pw.print("Restrict background status: ");
@@ -277,6 +297,23 @@
return resetUidPolicy("not blacklisted", POLICY_REJECT_METERED_BACKGROUND);
}
+ private int setAppIdleWhitelist(boolean isWhitelisted) {
+ final int uid = getUidFromNextArg();
+ if (uid < 0) {
+ return uid;
+ }
+ mInterface.setAppIdleWhitelist(uid, isWhitelisted);
+ return 0;
+ }
+
+ private int addAppIdleWhitelist() throws RemoteException {
+ return setAppIdleWhitelist(true);
+ }
+
+ private int removeAppIdleWhitelist() throws RemoteException {
+ return setAppIdleWhitelist(false);
+ }
+
private int listWifiNetworks() {
final PrintWriter pw = getOutPrintWriter();
final String arg = getNextArg();