diff options
author | Dianne Hackborn <hackbod@google.com> | 2015-08-06 22:19:06 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2015-08-07 14:23:32 -0700 |
commit | 4a503b1ece485d44c15eb02ec2bcd464b46e6f7f (patch) | |
tree | 72a22a0708c74e5f99a8bf2f4cb68f3d6ac15cf7 | |
parent | 1d7c32548c30c62a35c54a624f89d5f9db31b9d4 (diff) | |
download | frameworks_base-4a503b1ece485d44c15eb02ec2bcd464b46e6f7f.zip frameworks_base-4a503b1ece485d44c15eb02ec2bcd464b46e6f7f.tar.gz frameworks_base-4a503b1ece485d44c15eb02ec2bcd464b46e6f7f.tar.bz2 |
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
8 files changed, 411 insertions, 63 deletions
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 class UsageStatsManagerInternal { 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 @@ import android.os.UserHandle; 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 @@ public class SparseIntArray implements Cloneable { } /** + * 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 @@ public class DeviceIdleController extends SystemService 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 class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService return getAppIdTempWhitelistInternal(); } + @Override public boolean isPowerSaveWhitelistExceptIdleApp(String name) { + return isPowerSaveWhitelistExceptIdleAppInternal(name); + } + @Override public boolean isPowerSaveWhitelistApp(String name) { return isPowerSaveWhitelistAppInternal(name); } @@ -679,6 +722,19 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 class DeviceIdleController extends SystemService } } + 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 class DeviceIdleController extends SystemService } } + public int[] getAppIdWhitelistExceptIdleInternal() { + synchronized (this) { + return mPowerSaveWhitelistExceptIdleAppIdArray; + } + } + public int[] getAppIdWhitelistInternal() { synchronized (this) { return mPowerSaveWhitelistAllAppIdArray; @@ -921,6 +1022,9 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService synchronized (this) { long token = Binder.clearCallingIdentity(); try { + exitForceIdleLocked(); stepIdleStateLocked(); pw.print("Stepped to: "); pw.println(stateToString(mState)); } finally { @@ -1369,6 +1502,33 @@ public class DeviceIdleController extends SystemService } } 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 @@ public class DeviceIdleController extends SystemService synchronized (this) { long token = Binder.clearCallingIdentity(); try { + exitForceIdleLocked(); if (!mEnabled) { mEnabled = true; becomeInactiveIfAppropriateLocked(); @@ -1431,6 +1592,12 @@ public class DeviceIdleController extends SystemService } } 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService } 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 @@ public class SystemConfig { 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 @@ public class SystemConfig { return mPermissions; } + public ArraySet<String> getAllowInPowerSaveExceptIdle() { + return mAllowInPowerSaveExceptIdle; + } + public ArraySet<String> getAllowInPowerSave() { return mAllowInPowerSave; } @@ -329,6 +338,17 @@ public class SystemConfig { 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.NetworkPolicy.WARNING_DISABLED; 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_NONE; 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** * 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mRestrictPower != enabled) { mRestrictPower = enabled; updateRulesForGlobalChangeLocked(true); - updateRulesForTempWhitelistChangeLocked(); } } } @@ -437,9 +447,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { int uid = mContext.getPackageManager().getPackageUid(packageName, userId); synchronized (mRulesLock) { - updateRulesForUidLocked(uid); + updateRuleForAppIdleLocked(uid); } } catch (NameNotFoundException nnfe) { } @@ -2422,10 +2471,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** * 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 a433ec4..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.ContentResolver; 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.AtomicFile; 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 @@ public class UsageStatsService extends SystemService implements } 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements } @Override + public int[] getIdleUidsForUser(int userId) { + return UsageStatsService.this.getIdleUidsForUser(userId); + } + + @Override public boolean isAppIdleParoleOn() { return mAppIdleParoled; } |