diff options
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; } |