Fix issue #22989030: Separate battery whitelists
We now have a new whitelist you can put apps in, which
opts them out of the old battery saver mode and new app idle,
but doesn't keep them from going in to doze. This is for a few
special cases that we had previously whitelisted for battery saver,
and inherited to the new modes... ultimately we should figure out
how to get these apps out of the whitelist completely, but this
will help for now.
Apps in this new whitelist are not shown in the UI, because they
are still significantly restricted by not being able to operate
normally in doze. This also means they are still visible in the
list of all apps for the user to be able to put them on/off the
complete whitelist if that is what they really want.
In the course of doing this, I needed to clean up code in the
network policy manager to better separate management of the
two firewall rules that now have different whitelists applied
to them. This also hopefully just generally simplifies and cleans
up that code. Hopefully!
Change-Id: I92e15f2f85899571dd8b049b5e3eb1354f55f353
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 9113426..948ea1e 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -77,6 +77,13 @@
public abstract boolean isAppIdle(String packageName, int userId);
/**
+ * Returns all of the uids for a given user where all packages associating with that uid
+ * are in the app idle state -- there are no associated apps that are not idle. This means
+ * all of the returned uids can be safely considered app idle.
+ */
+ public abstract int[] getIdleUidsForUser(int userId);
+
+ /**
* @return True if currently app idle parole mode is on. This means all idle apps are allow to
* run for a short period of time.
*/
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index d3eec1e..f55883a 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -22,10 +22,14 @@
interface IDeviceIdleController {
void addPowerSaveWhitelistApp(String name);
void removePowerSaveWhitelistApp(String name);
+ String[] getSystemPowerWhitelistExceptIdle();
String[] getSystemPowerWhitelist();
+ String[] getFullPowerWhitelistExceptIdle();
String[] getFullPowerWhitelist();
+ int[] getAppIdWhitelistExceptIdle();
int[] getAppIdWhitelist();
int[] getAppIdTempWhitelist();
+ boolean isPowerSaveWhitelistExceptIdleApp(String name);
boolean isPowerSaveWhitelistApp(String name);
void addPowerSaveTempWhitelistApp(String name, long duration, int userId, String reason);
long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason);
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index 2b85a21..e5c729d 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -184,6 +184,14 @@
}
/**
+ * Directly set the value at a particular index.
+ * @hide
+ */
+ public void setValueAt(int index, int value) {
+ mValues[index] = value;
+ }
+
+ /**
* Returns the index for which {@link #keyAt} would return the
* specified key, or a negative number if the specified
* key is not mapped.
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 579d2df..350310c 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -141,6 +141,6 @@
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
- <allow-in-power-save package="com.android.providers.downloads" />
+ <allow-in-power-save-except-idle package="com.android.providers.downloads" />
</permissions>
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 7561c7d..e678bbc 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -113,6 +113,7 @@
private Display mCurDisplay;
private AnyMotionDetector mAnyMotionDetector;
private boolean mEnabled;
+ private boolean mForceIdle;
private boolean mScreenOn;
private boolean mCharging;
private boolean mSigMotionActive;
@@ -151,7 +152,14 @@
public final AtomicFile mConfigFile;
/**
- * Package names the system has white-listed to opt out of power save restrictions.
+ * Package names the system has white-listed to opt out of power save restrictions,
+ * except for device idle mode.
+ */
+ private final ArrayMap<String, Integer> mPowerSaveWhitelistAppsExceptIdle = new ArrayMap<>();
+
+ /**
+ * Package names the system has white-listed to opt out of power save restrictions for
+ * all modes.
*/
private final ArrayMap<String, Integer> mPowerSaveWhitelistApps = new ArrayMap<>();
@@ -161,11 +169,30 @@
private final ArrayMap<String, Integer> mPowerSaveWhitelistUserApps = new ArrayMap<>();
/**
+ * App IDs of built-in system apps that have been white-listed except for idle modes.
+ */
+ private final SparseBooleanArray mPowerSaveWhitelistSystemAppIdsExceptIdle
+ = new SparseBooleanArray();
+
+ /**
* App IDs of built-in system apps that have been white-listed.
*/
private final SparseBooleanArray mPowerSaveWhitelistSystemAppIds = new SparseBooleanArray();
/**
+ * App IDs that have been white-listed to opt out of power save restrictions, except
+ * for device idle modes.
+ */
+ private final SparseBooleanArray mPowerSaveWhitelistExceptIdleAppIds = new SparseBooleanArray();
+
+ /**
+ * Current app IDs that are in the complete power save white list, but shouldn't be
+ * excluded from idle modes. This array can be shared with others because it will not be
+ * modified once set.
+ */
+ private int[] mPowerSaveWhitelistExceptIdleAppIdArray = new int[0];
+
+ /**
* App IDs that have been white-listed to opt out of power save restrictions.
*/
private final SparseBooleanArray mPowerSaveWhitelistAllAppIds = new SparseBooleanArray();
@@ -583,14 +610,26 @@
removePowerSaveWhitelistAppInternal(name);
}
+ @Override public String[] getSystemPowerWhitelistExceptIdle() {
+ return getSystemPowerWhitelistExceptIdleInternal();
+ }
+
@Override public String[] getSystemPowerWhitelist() {
return getSystemPowerWhitelistInternal();
}
+ @Override public String[] getFullPowerWhitelistExceptIdle() {
+ return getFullPowerWhitelistExceptIdleInternal();
+ }
+
@Override public String[] getFullPowerWhitelist() {
return getFullPowerWhitelistInternal();
}
+ @Override public int[] getAppIdWhitelistExceptIdle() {
+ return getAppIdWhitelistExceptIdleInternal();
+ }
+
@Override public int[] getAppIdWhitelist() {
return getAppIdWhitelistInternal();
}
@@ -599,6 +638,10 @@
return getAppIdTempWhitelistInternal();
}
+ @Override public boolean isPowerSaveWhitelistExceptIdleApp(String name) {
+ return isPowerSaveWhitelistExceptIdleAppInternal(name);
+ }
+
@Override public boolean isPowerSaveWhitelistApp(String name) {
return isPowerSaveWhitelistAppInternal(name);
}
@@ -679,6 +722,19 @@
mEnabled = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enableAutoPowerModes);
SystemConfig sysConfig = SystemConfig.getInstance();
+ ArraySet<String> allowPowerExceptIdle = sysConfig.getAllowInPowerSaveExceptIdle();
+ for (int i=0; i<allowPowerExceptIdle.size(); i++) {
+ String pkg = allowPowerExceptIdle.valueAt(i);
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
+ if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+ int appid = UserHandle.getAppId(ai.uid);
+ mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
+ mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();
for (int i=0; i<allowPower.size(); i++) {
String pkg = allowPower.valueAt(i);
@@ -686,6 +742,10 @@
ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
int appid = UserHandle.getAppId(ai.uid);
+ // These apps are on both the whitelist-except-idle as well
+ // as the full whitelist, so they apply in all cases.
+ mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
+ mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
mPowerSaveWhitelistApps.put(ai.packageName, appid);
mPowerSaveWhitelistSystemAppIds.put(appid, true);
}
@@ -783,17 +843,45 @@
return false;
}
+ public String[] getSystemPowerWhitelistExceptIdleInternal() {
+ synchronized (this) {
+ int size = mPowerSaveWhitelistAppsExceptIdle.size();
+ String[] apps = new String[size];
+ for (int i = 0; i < size; i++) {
+ apps[i] = mPowerSaveWhitelistAppsExceptIdle.keyAt(i);
+ }
+ return apps;
+ }
+ }
+
public String[] getSystemPowerWhitelistInternal() {
synchronized (this) {
int size = mPowerSaveWhitelistApps.size();
String[] apps = new String[size];
- for (int i = 0; i < mPowerSaveWhitelistApps.size(); i++) {
+ for (int i = 0; i < size; i++) {
apps[i] = mPowerSaveWhitelistApps.keyAt(i);
}
return apps;
}
}
+ public String[] getFullPowerWhitelistExceptIdleInternal() {
+ synchronized (this) {
+ int size = mPowerSaveWhitelistAppsExceptIdle.size() + mPowerSaveWhitelistUserApps.size();
+ String[] apps = new String[size];
+ int cur = 0;
+ for (int i = 0; i < mPowerSaveWhitelistAppsExceptIdle.size(); i++) {
+ apps[cur] = mPowerSaveWhitelistAppsExceptIdle.keyAt(i);
+ cur++;
+ }
+ for (int i = 0; i < mPowerSaveWhitelistUserApps.size(); i++) {
+ apps[cur] = mPowerSaveWhitelistUserApps.keyAt(i);
+ cur++;
+ }
+ return apps;
+ }
+ }
+
public String[] getFullPowerWhitelistInternal() {
synchronized (this) {
int size = mPowerSaveWhitelistApps.size() + mPowerSaveWhitelistUserApps.size();
@@ -811,6 +899,13 @@
}
}
+ public boolean isPowerSaveWhitelistExceptIdleAppInternal(String packageName) {
+ synchronized (this) {
+ return mPowerSaveWhitelistAppsExceptIdle.containsKey(packageName)
+ || mPowerSaveWhitelistUserApps.containsKey(packageName);
+ }
+ }
+
public boolean isPowerSaveWhitelistAppInternal(String packageName) {
synchronized (this) {
return mPowerSaveWhitelistApps.containsKey(packageName)
@@ -818,6 +913,12 @@
}
}
+ public int[] getAppIdWhitelistExceptIdleInternal() {
+ synchronized (this) {
+ return mPowerSaveWhitelistExceptIdleAppIdArray;
+ }
+ }
+
public int[] getAppIdWhitelistInternal() {
synchronized (this) {
return mPowerSaveWhitelistAllAppIdArray;
@@ -921,6 +1022,9 @@
Slog.d(TAG, "Removing UID " + uid + " from temp whitelist");
}
updateTempWhitelistAppIdsLocked();
+ if (mNetworkPolicyTempWhitelistCallback != null) {
+ mHandler.post(mNetworkPolicyTempWhitelistCallback);
+ }
reportTempWhitelistChangedLocked();
try {
mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_TEMP_WHITELIST_FINISH,
@@ -949,10 +1053,14 @@
if (DEBUG) Slog.d(TAG, "updateDisplayLocked: screenOn=" + screenOn);
if (!screenOn && mScreenOn) {
mScreenOn = false;
- becomeInactiveIfAppropriateLocked();
+ if (!mForceIdle) {
+ becomeInactiveIfAppropriateLocked();
+ }
} else if (screenOn) {
mScreenOn = true;
- becomeActiveLocked("screen", Process.myUid());
+ if (!mForceIdle) {
+ becomeActiveLocked("screen", Process.myUid());
+ }
}
}
@@ -960,10 +1068,14 @@
if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);
if (!charging && mCharging) {
mCharging = false;
- becomeInactiveIfAppropriateLocked();
+ if (!mForceIdle) {
+ becomeInactiveIfAppropriateLocked();
+ }
} else if (charging) {
mCharging = charging;
- becomeActiveLocked("charging", Process.myUid());
+ if (!mForceIdle) {
+ becomeActiveLocked("charging", Process.myUid());
+ }
}
}
@@ -989,7 +1101,7 @@
void becomeInactiveIfAppropriateLocked() {
if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
- if (!mScreenOn && !mCharging && mEnabled && mState == STATE_ACTIVE) {
+ if (((!mScreenOn && !mCharging) || mForceIdle) && mEnabled && mState == STATE_ACTIVE) {
// Screen has turned off; we are now going to become inactive and start
// waiting to see if we will ultimately go idle.
mState = STATE_INACTIVE;
@@ -1010,6 +1122,15 @@
becomeInactiveIfAppropriateLocked();
}
+ void exitForceIdleLocked() {
+ if (mForceIdle) {
+ mForceIdle = false;
+ if (mScreenOn || mCharging) {
+ becomeActiveLocked("exit-force-idle", Process.myUid());
+ }
+ }
+ }
+
void stepIdleStateLocked() {
if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
EventLogTags.writeDeviceIdleStep();
@@ -1138,20 +1259,28 @@
mNextAlarmTime, mSensingAlarmIntent);
}
- private void updateWhitelistAppIdsLocked() {
- mPowerSaveWhitelistAllAppIds.clear();
- for (int i=0; i<mPowerSaveWhitelistApps.size(); i++) {
- mPowerSaveWhitelistAllAppIds.put(mPowerSaveWhitelistApps.valueAt(i), true);
+ private static int[] buildAppIdArray(ArrayMap<String, Integer> systemApps,
+ ArrayMap<String, Integer> userApps, SparseBooleanArray outAppIds) {
+ outAppIds.clear();
+ for (int i=0; i<systemApps.size(); i++) {
+ outAppIds.put(systemApps.valueAt(i), true);
}
- for (int i=0; i<mPowerSaveWhitelistUserApps.size(); i++) {
- mPowerSaveWhitelistAllAppIds.put(mPowerSaveWhitelistUserApps.valueAt(i), true);
+ for (int i=0; i<userApps.size(); i++) {
+ outAppIds.put(userApps.valueAt(i), true);
}
- int size = mPowerSaveWhitelistAllAppIds.size();
+ int size = outAppIds.size();
int[] appids = new int[size];
for (int i = 0; i < size; i++) {
- appids[i] = mPowerSaveWhitelistAllAppIds.keyAt(i);
+ appids[i] = outAppIds.keyAt(i);
}
- mPowerSaveWhitelistAllAppIdArray = appids;
+ return appids;
+ }
+
+ private void updateWhitelistAppIdsLocked() {
+ mPowerSaveWhitelistExceptIdleAppIdArray = buildAppIdArray(mPowerSaveWhitelistAppsExceptIdle,
+ mPowerSaveWhitelistUserApps, mPowerSaveWhitelistExceptIdleAppIds);
+ mPowerSaveWhitelistAllAppIdArray = buildAppIdArray(mPowerSaveWhitelistApps,
+ mPowerSaveWhitelistUserApps, mPowerSaveWhitelistAllAppIds);
if (mLocalPowerManager != null) {
if (DEBUG) {
Slog.d(TAG, "Setting wakelock whitelist to "
@@ -1320,6 +1449,9 @@
pw.println("Commands:");
pw.println(" step");
pw.println(" Immediately step to next state, without waiting for alarm.");
+ pw.println(" force-idle");
+ pw.println(" Force directly into idle mode, regardless of other device state.");
+ pw.println(" Use \"step\" to get out.");
pw.println(" disable");
pw.println(" Completely disable device idle mode.");
pw.println(" enable");
@@ -1362,6 +1494,7 @@
synchronized (this) {
long token = Binder.clearCallingIdentity();
try {
+ exitForceIdleLocked();
stepIdleStateLocked();
pw.print("Stepped to: "); pw.println(stateToString(mState));
} finally {
@@ -1369,6 +1502,33 @@
}
}
return;
+ } else if ("force-idle".equals(arg)) {
+ synchronized (this) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ if (!mEnabled) {
+ pw.println("Unable to go idle; not enabled");
+ return;
+ }
+ mForceIdle = true;
+ becomeInactiveIfAppropriateLocked();
+ int curState = mState;
+ while (curState != STATE_IDLE) {
+ stepIdleStateLocked();
+ if (curState == mState) {
+ pw.print("Unable to go idle; stopped at ");
+ pw.println(stateToString(mState));
+ exitForceIdleLocked();
+ return;
+ }
+ curState = mState;
+ }
+ pw.println("Now forced in to idle mode");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ return;
} else if ("disable".equals(arg)) {
synchronized (this) {
long token = Binder.clearCallingIdentity();
@@ -1387,6 +1547,7 @@
synchronized (this) {
long token = Binder.clearCallingIdentity();
try {
+ exitForceIdleLocked();
if (!mEnabled) {
mEnabled = true;
becomeInactiveIfAppropriateLocked();
@@ -1431,6 +1592,12 @@
}
} else {
synchronized (this) {
+ for (int j=0; j<mPowerSaveWhitelistAppsExceptIdle.size(); j++) {
+ pw.print("system-excidle,");
+ pw.print(mPowerSaveWhitelistAppsExceptIdle.keyAt(j));
+ pw.print(",");
+ pw.println(mPowerSaveWhitelistAppsExceptIdle.valueAt(j));
+ }
for (int j=0; j<mPowerSaveWhitelistApps.size(); j++) {
pw.print("system,");
pw.print(mPowerSaveWhitelistApps.keyAt(j));
@@ -1481,7 +1648,15 @@
synchronized (this) {
mConstants.dump(pw);
- int size = mPowerSaveWhitelistApps.size();
+ int size = mPowerSaveWhitelistAppsExceptIdle.size();
+ if (size > 0) {
+ pw.println(" Whitelist (except idle) system apps:");
+ for (int i = 0; i < size; i++) {
+ pw.print(" ");
+ pw.println(mPowerSaveWhitelistAppsExceptIdle.keyAt(i));
+ }
+ }
+ size = mPowerSaveWhitelistApps.size();
if (size > 0) {
pw.println(" Whitelist system apps:");
for (int i = 0; i < size; i++) {
@@ -1497,6 +1672,15 @@
pw.println(mPowerSaveWhitelistUserApps.keyAt(i));
}
}
+ size = mPowerSaveWhitelistExceptIdleAppIds.size();
+ if (size > 0) {
+ pw.println(" Whitelist (except idle) all app ids:");
+ for (int i = 0; i < size; i++) {
+ pw.print(" ");
+ pw.print(mPowerSaveWhitelistExceptIdleAppIds.keyAt(i));
+ pw.println();
+ }
+ }
size = mPowerSaveWhitelistAllAppIds.size();
if (size > 0) {
pw.println(" Whitelist all app ids:");
@@ -1531,6 +1715,7 @@
}
pw.print(" mEnabled="); pw.println(mEnabled);
+ pw.print(" mForceIdle="); pw.println(mForceIdle);
pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor);
pw.print(" mCurDisplay="); pw.println(mCurDisplay);
pw.print(" mScreenOn="); pw.println(mScreenOn);
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index ad5406c..cd61347 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -84,6 +84,11 @@
final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();
// These are the packages that are white-listed to be able to run in the
+ // background while in power save mode (but not whitelisted from device idle modes),
+ // as read from the configuration files.
+ final ArraySet<String> mAllowInPowerSaveExceptIdle = new ArraySet<>();
+
+ // These are the packages that are white-listed to be able to run in the
// background while in power save mode, as read from the configuration files.
final ArraySet<String> mAllowInPowerSave = new ArraySet<>();
@@ -123,6 +128,10 @@
return mPermissions;
}
+ public ArraySet<String> getAllowInPowerSaveExceptIdle() {
+ return mAllowInPowerSaveExceptIdle;
+ }
+
public ArraySet<String> getAllowInPowerSave() {
return mAllowInPowerSave;
}
@@ -329,6 +338,17 @@
XmlUtils.skipCurrentTag(parser);
continue;
+ } else if ("allow-in-power-save-except-idle".equals(name) && !onlyFeatures) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mAllowInPowerSaveExceptIdle.add(pkgname);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+
} else if ("allow-in-power-save".equals(name) && !onlyFeatures) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 46bda8c..c0d0d13 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -39,6 +39,7 @@
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE;
@@ -46,7 +47,6 @@
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
-import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkPolicyManager.dumpPolicy;
import static android.net.NetworkPolicyManager.dumpRules;
@@ -284,6 +284,13 @@
/**
* UIDs that have been white-listed to always be able to have network access
+ * in power save mode, except device idle (doze) still applies.
+ * TODO: An int array might be sufficient
+ */
+ private final SparseBooleanArray mPowerSaveWhitelistExceptIdleAppIds = new SparseBooleanArray();
+
+ /**
+ * UIDs that have been white-listed to always be able to have network access
* in power save mode.
* TODO: An int array might be sufficient
*/
@@ -302,9 +309,6 @@
/** Foreground at UID granularity. */
final SparseIntArray mUidState = new SparseIntArray();
- /** The current maximum process state that we are considering to be foreground. */
- private int mCurForegroundState = ActivityManager.PROCESS_STATE_TOP;
-
private final RemoteCallbackList<INetworkPolicyListener>
mListeners = new RemoteCallbackList<>();
@@ -365,7 +369,14 @@
void updatePowerSaveWhitelistLocked() {
try {
- final int[] whitelist = mDeviceIdleController.getAppIdWhitelist();
+ int[] whitelist = mDeviceIdleController.getAppIdWhitelistExceptIdle();
+ mPowerSaveWhitelistExceptIdleAppIds.clear();
+ if (whitelist != null) {
+ for (int uid : whitelist) {
+ mPowerSaveWhitelistExceptIdleAppIds.put(uid, true);
+ }
+ }
+ whitelist = mDeviceIdleController.getAppIdWhitelist();
mPowerSaveWhitelistAppIds.clear();
if (whitelist != null) {
for (int uid : whitelist) {
@@ -425,7 +436,6 @@
if (mRestrictPower != enabled) {
mRestrictPower = enabled;
updateRulesForGlobalChangeLocked(true);
- updateRulesForTempWhitelistChangeLocked();
}
}
}
@@ -437,9 +447,12 @@
readPolicyLocked();
if (mRestrictBackground || mRestrictPower || mDeviceIdleMode) {
- updateRulesForGlobalChangeLocked(true);
- updateRulesForTempWhitelistChangeLocked();
+ updateRulesForGlobalChangeLocked(false);
updateNotificationsLocked();
+ } else {
+ // If we are not in any special mode, we just need to make sure the current
+ // app idle state is updated.
+ updateRulesForAppIdleLocked();
}
}
@@ -1907,7 +1920,6 @@
fout.print("Restrict background: "); fout.println(mRestrictBackground);
fout.print("Restrict power: "); fout.println(mRestrictPower);
fout.print("Device idle: "); fout.println(mDeviceIdleMode);
- fout.print("Current foreground state: "); fout.println(mCurForegroundState);
fout.println("Network policies:");
fout.increaseIndent();
for (int i = 0; i < mNetworkPolicy.size(); i++) {
@@ -1931,6 +1943,20 @@
}
fout.decreaseIndent();
+ size = mPowerSaveWhitelistExceptIdleAppIds.size();
+ if (size > 0) {
+ fout.println("Power save whitelist (except idle) app ids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mPowerSaveWhitelistExceptIdleAppIds.keyAt(i));
+ fout.print(": ");
+ fout.print(mPowerSaveWhitelistExceptIdleAppIds.valueAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
size = mPowerSaveWhitelistAppIds.size();
if (size > 0) {
fout.println("Power save whitelist app ids:");
@@ -1960,7 +1986,7 @@
int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
fout.print(" state=");
fout.print(state);
- fout.print(state <= mCurForegroundState ? " (fg)" : " (bg)");
+ fout.print(state <= ActivityManager.PROCESS_STATE_TOP ? " (fg)" : " (bg)");
fout.print(" rules=");
final int rulesIndex = mUidRules.indexOfKey(uid);
@@ -1988,7 +2014,7 @@
boolean isUidForegroundLocked(int uid) {
// only really in foreground when screen is also on
return mScreenOn && mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY)
- <= mCurForegroundState;
+ <= ActivityManager.PROCESS_STATE_TOP;
}
/**
@@ -2024,8 +2050,8 @@
}
void updateRulesForUidStateChangeLocked(int uid, int oldUidState, int newUidState) {
- final boolean oldForeground = oldUidState <= mCurForegroundState;
- final boolean newForeground = newUidState <= mCurForegroundState;
+ final boolean oldForeground = oldUidState <= ActivityManager.PROCESS_STATE_TOP;
+ final boolean newForeground = newUidState <= ActivityManager.PROCESS_STATE_TOP;
if (oldForeground != newForeground) {
updateRulesForUidLocked(uid);
}
@@ -2049,7 +2075,7 @@
// only update rules for anyone with foreground activities
final int size = mUidState.size();
for (int i = 0; i < size; i++) {
- if (mUidState.valueAt(i) <= mCurForegroundState) {
+ if (mUidState.valueAt(i) <= ActivityManager.PROCESS_STATE_TOP) {
final int uid = mUidState.keyAt(i);
updateRulesForUidLocked(uid);
}
@@ -2069,9 +2095,11 @@
for (int ui = users.size() - 1; ui >= 0; ui--) {
UserInfo user = users.get(ui);
for (int i = mPowerSaveTempWhitelistAppIds.size() - 1; i >= 0; i--) {
- int appId = mPowerSaveTempWhitelistAppIds.keyAt(i);
- int uid = UserHandle.getUid(user.id, appId);
- uidRules.put(uid, FIREWALL_RULE_ALLOW);
+ if (mPowerSaveTempWhitelistAppIds.valueAt(i)) {
+ int appId = mPowerSaveTempWhitelistAppIds.keyAt(i);
+ int uid = UserHandle.getUid(user.id, appId);
+ uidRules.put(uid, FIREWALL_RULE_ALLOW);
+ }
}
for (int i = mPowerSaveWhitelistAppIds.size() - 1; i >= 0; i--) {
int appId = mPowerSaveWhitelistAppIds.keyAt(i);
@@ -2089,6 +2117,45 @@
enableFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mDeviceIdleMode);
}
+ void updateRuleForDeviceIdleLocked(int uid) {
+ if (mDeviceIdleMode) {
+ int appId = UserHandle.getAppId(uid);
+ if (mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId)
+ || isProcStateAllowedWhileIdle(mUidState.get(uid))) {
+ setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_ALLOW);
+ } else {
+ setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT);
+ }
+ }
+ }
+
+ void updateRulesForAppIdleLocked() {
+ // Fully update the app idle firewall chain.
+ SparseIntArray uidRules = new SparseIntArray();
+ final List<UserInfo> users = mUserManager.getUsers();
+ for (int ui = users.size() - 1; ui >= 0; ui--) {
+ UserInfo user = users.get(ui);
+ int[] idleUids = mUsageStats.getIdleUidsForUser(user.id);
+ for (int uid : idleUids) {
+ if (!mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid), false)) {
+ uidRules.put(uid, FIREWALL_RULE_DENY);
+ }
+ }
+ }
+ setUidFirewallRules(FIREWALL_CHAIN_STANDBY, uidRules);
+ }
+
+ void updateRuleForAppIdleLocked(int uid) {
+ if (!isUidValidForRules(uid)) return;
+
+ int appId = UserHandle.getAppId(uid);
+ if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)) {
+ setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
+ } else {
+ setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+ }
+ }
+
void updateRulesForAppIdleParoleLocked() {
boolean enableChain = !mUsageStats.isAppIdleParoleOn();
enableFirewallChainLocked(FIREWALL_CHAIN_STANDBY, enableChain);
@@ -2101,14 +2168,8 @@
void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {
final PackageManager pm = mContext.getPackageManager();
- // If we are in restrict power mode, we allow all important apps
- // to have data access. Otherwise, we restrict data access to only
- // the top apps.
- mCurForegroundState = (!mRestrictBackground && mRestrictPower)
- ? ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
- : ActivityManager.PROCESS_STATE_TOP;
-
updateRulesForDeviceIdleLocked();
+ updateRulesForAppIdleLocked();
// update rules for all installed applications
final List<UserInfo> users = mUserManager.getUsers();
@@ -2138,10 +2199,9 @@
for (UserInfo user : users) {
for (int i = mPowerSaveTempWhitelistAppIds.size() - 1; i >= 0; i--) {
int appId = mPowerSaveTempWhitelistAppIds.keyAt(i);
- boolean isAllow = mPowerSaveTempWhitelistAppIds.valueAt(i);
int uid = UserHandle.getUid(user.id, appId);
- updateRulesForUidLocked(uid);
- setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, !isAllow);
+ updateRuleForAppIdleLocked(uid);
+ updateRuleForDeviceIdleLocked(uid);
}
}
}
@@ -2188,16 +2248,12 @@
final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
final boolean uidForeground = isUidForegroundLocked(uid);
- final boolean uidIdle = isUidIdle(uid);
// derive active rules based on policy and active state
int appId = UserHandle.getAppId(uid);
int uidRules = RULE_ALLOW_ALL;
- if (uidIdle && !mPowerSaveWhitelistAppIds.get(appId)
- && !mPowerSaveTempWhitelistAppIds.get(appId)) {
- uidRules = RULE_REJECT_ALL;
- } else if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
+ if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
// uid in background, and policy says to block metered data
uidRules = RULE_REJECT_METERED;
} else if (mRestrictBackground) {
@@ -2206,7 +2262,7 @@
uidRules = RULE_REJECT_METERED;
}
} else if (mRestrictPower) {
- final boolean whitelisted = mPowerSaveWhitelistAppIds.get(appId)
+ final boolean whitelisted = mPowerSaveWhitelistExceptIdleAppIds.get(appId)
|| mPowerSaveTempWhitelistAppIds.get(appId);
if (!whitelisted && !uidForeground
&& (uidPolicy & POLICY_ALLOW_BACKGROUND_BATTERY_SAVE) == 0) {
@@ -2232,13 +2288,6 @@
setUidNetworkRules(uid, rejectMetered);
}
- // Update firewall rules if necessary
- final boolean oldFirewallReject = (oldRules & RULE_REJECT_ALL) != 0;
- final boolean firewallReject = (uidRules & RULE_REJECT_ALL) != 0;
- if (oldFirewallReject != firewallReject) {
- setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, firewallReject);
- }
-
// dispatch changed rule to existing listeners
if (oldRules != uidRules) {
mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules).sendToTarget();
@@ -2260,7 +2309,7 @@
try {
int uid = mContext.getPackageManager().getPackageUid(packageName, userId);
synchronized (mRulesLock) {
- updateRulesForUidLocked(uid);
+ updateRuleForAppIdleLocked(uid);
}
} catch (NameNotFoundException nnfe) {
}
@@ -2422,10 +2471,9 @@
/**
* Add or remove a uid to the firewall blacklist for all network ifaces.
*/
- private void setUidFirewallRule(int chain, int uid, boolean rejectOnAll) {
+ private void setUidFirewallRule(int chain, int uid, int rule) {
try {
- mNetworkManager.setFirewallUidRule(chain, uid,
- rejectOnAll ? FIREWALL_RULE_DENY : FIREWALL_RULE_ALLOW);
+ mNetworkManager.setFirewallUidRule(chain, uid, rule);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting firewall uid rules", e);
} catch (RemoteException e) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index a433ec45..85f0665 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -35,6 +35,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
@@ -65,6 +66,7 @@
import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.view.Display;
@@ -799,7 +801,10 @@
}
if (packageName.equals("android")) return false;
try {
- if (mDeviceIdleController.isPowerSaveWhitelistApp(packageName)) {
+ // We allow all whitelisted apps, including those that don't want to be whitelisted
+ // for idle mode, because app idle (aka app standby) is really not as big an issue
+ // for controlling who participates vs. doze mode.
+ if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
return false;
}
} catch (RemoteException re) {
@@ -825,6 +830,72 @@
return isAppIdleUnfiltered(packageName, userService, timeNow, screenOnTime);
}
+ int[] getIdleUidsForUser(int userId) {
+ if (!mAppIdleEnabled) {
+ return new int[0];
+ }
+
+ final long timeNow;
+ final UserUsageStatsService userService;
+ final long screenOnTime;
+ synchronized (mLock) {
+ timeNow = checkAndGetTimeLocked();
+ userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+ screenOnTime = getScreenOnTimeLocked(timeNow);
+ }
+
+ List<ApplicationInfo> apps;
+ try {
+ ParceledListSlice<ApplicationInfo> slice
+ = AppGlobals.getPackageManager().getInstalledApplications(0, userId);
+ apps = slice.getList();
+ } catch (RemoteException e) {
+ return new int[0];
+ }
+
+ // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
+ // associated with that uid, upper 16 bits is the number of those apps that is idle.
+ SparseIntArray uidStates = new SparseIntArray();
+
+ // Now resolve all app state. Iterating over all apps, keeping track of how many
+ // we find for each uid and how many of those are idle.
+ for (int i = apps.size()-1; i >= 0; i--) {
+ ApplicationInfo ai = apps.get(i);
+
+ // Check whether this app is idle.
+ boolean idle = isAppIdleFiltered(ai.packageName, userId, userService, timeNow,
+ screenOnTime);
+
+ int index = uidStates.indexOfKey(ai.uid);
+ if (index < 0) {
+ uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
+ } else {
+ int value = uidStates.valueAt(index);
+ uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
+ }
+ }
+
+ int numIdle = 0;
+ for (int i = uidStates.size() - 1; i >= 0; i--) {
+ int value = uidStates.valueAt(i);
+ if ((value&0x7fff) == (value>>16)) {
+ numIdle++;
+ }
+ }
+
+ int[] res = new int[numIdle];
+ numIdle = 0;
+ for (int i = uidStates.size() - 1; i >= 0; i--) {
+ int value = uidStates.valueAt(i);
+ if ((value&0x7fff) == (value>>16)) {
+ res[numIdle] = uidStates.keyAt(i);
+ numIdle++;
+ }
+ }
+
+ return res;
+ }
+
void setAppIdle(String packageName, boolean idle, int userId) {
if (packageName == null) return;
@@ -1284,6 +1355,11 @@
}
@Override
+ public int[] getIdleUidsForUser(int userId) {
+ return UsageStatsService.this.getIdleUidsForUser(userId);
+ }
+
+ @Override
public boolean isAppIdleParoleOn() {
return mAppIdleParoled;
}