diff options
11 files changed, 307 insertions, 27 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index b4315fe..9259fb2 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -71,6 +71,7 @@ package android { field public static final java.lang.String CAPTURE_VIDEO_OUTPUT = "android.permission.CAPTURE_VIDEO_OUTPUT"; field public static final java.lang.String CHANGE_COMPONENT_ENABLED_STATE = "android.permission.CHANGE_COMPONENT_ENABLED_STATE"; field public static final java.lang.String CHANGE_CONFIGURATION = "android.permission.CHANGE_CONFIGURATION"; + field public static final java.lang.String CHANGE_DEVICE_IDLE_TEMP_WHITELIST = "android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"; field public static final java.lang.String CHANGE_NETWORK_STATE = "android.permission.CHANGE_NETWORK_STATE"; field public static final java.lang.String CHANGE_WIFI_MULTICAST_STATE = "android.permission.CHANGE_WIFI_MULTICAST_STATE"; field public static final java.lang.String CHANGE_WIFI_STATE = "android.permission.CHANGE_WIFI_STATE"; @@ -6331,6 +6332,7 @@ package android.app.usage { method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long); method public android.app.usage.UsageEvents queryEvents(long, long); method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long); + method public void whitelistAppTemporarily(java.lang.String, long, android.os.UserHandle); field public static final int INTERVAL_BEST = 4; // 0x4 field public static final int INTERVAL_DAILY = 0; // 0x0 field public static final int INTERVAL_MONTHLY = 2; // 0x2 diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index 254408a..a9328bc 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -32,4 +32,5 @@ interface IUsageStatsManager { UsageEvents queryEvents(long beginTime, long endTime, String callingPackage); void setAppInactive(String packageName, boolean inactive, int userId); boolean isAppInactive(String packageName, int userId); + void whitelistAppTemporarily(String packageName, long duration, int userId); } diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 34699d8..c74b0f2 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -16,6 +16,7 @@ package android.app.usage; +import android.annotation.SystemApi; import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.RemoteException; @@ -245,4 +246,25 @@ public final class UsageStatsManager { // fall through } } + + /** + * {@hide} + * Temporarily whitelist the specified app for a short duration. This is to allow an app + * receiving a high priority message to be able to access the network and acquire wakelocks + * even if the device is in power-save mode or the app is currently considered inactive. + * The caller must hold the CHANGE_DEVICE_IDLE_TEMP_WHITELIST permission. + * @param packageName The package name of the app to whitelist. + * @param duration Duration to whitelist the app for, in milliseconds. It is recommended that + * this be limited to 10s of seconds. Requested duration will be clamped to a few minutes. + * @param user The user for whom the package should be whitelisted. Passing in a user that is + * not the same as the caller's process will require the INTERACT_ACROSS_USERS permission. + * @see #isAppInactive(String) + */ + @SystemApi + public void whitelistAppTemporarily(String packageName, long duration, UserHandle user) { + try { + mService.whitelistAppTemporarily(packageName, duration, user.getIdentifier()); + } catch (RemoteException re) { + } + } } diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl index 602bfea..268295d 100644 --- a/core/java/android/os/IDeviceIdleController.aidl +++ b/core/java/android/os/IDeviceIdleController.aidl @@ -16,6 +16,8 @@ package android.os; +import android.os.UserHandle; + /** @hide */ interface IDeviceIdleController { void addPowerSaveWhitelistApp(String name); @@ -23,5 +25,7 @@ interface IDeviceIdleController { String[] getSystemPowerWhitelist(); String[] getFullPowerWhitelist(); int[] getAppIdWhitelist(); + int[] getAppIdTempWhitelist(); boolean isPowerSaveWhitelistApp(String name); + void addPowerSaveTempWhitelistApp(String name, long duration, int userId); } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 1d9d7d2..8b18f32 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -928,6 +928,14 @@ public final class PowerManager { = "android.os.action.POWER_SAVE_WHITELIST_CHANGED"; /** + * @hide Intent that is broadcast when the set of temporarily whitelisted apps has changed. + * This broadcast is only sent to registered receivers. + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED + = "android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED"; + + /** * Intent that is broadcast when the state of {@link #isPowerSaveMode()} is about to change. * This broadcast is only sent to registered receivers. * diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index e523285..e742f98 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -137,6 +137,8 @@ public abstract class PowerManagerInternal { public abstract void setDeviceIdleWhitelist(int[] appids); + public abstract void setDeviceIdleTempWhitelist(int[] appids); + public abstract void updateUidProcState(int uid, int procState); public abstract void uidGone(int uid); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1b77bfb..b0182e9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -81,6 +81,7 @@ <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" /> <protected-broadcast android:name="android.os.action.DEVICE_IDLE_MODE_CHANGED" /> <protected-broadcast android:name="android.os.action.POWER_SAVE_WHITELIST_CHANGED" /> + <protected-broadcast android:name="android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED" /> <protected-broadcast android:name="android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED" /> @@ -2137,6 +2138,12 @@ <permission android:name="android.permission.CHANGE_APP_IDLE_STATE" android:protectionLevel="signature" /> + <!-- @hide @SystemApi Allows an application to temporarily whitelist an inactive app to + access the network and acquire wakelocks. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" + android:protectionLevel="system|signature" /> + <!-- @SystemApi Allows an application to collect battery statistics --> <permission android:name="android.permission.BATTERY_STATS" android:protectionLevel="signature|system|development" /> diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 9b7b2d3..e9759c3 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -16,14 +16,19 @@ package com.android.server; +import android.Manifest; +import android.app.ActivityManagerNative; import android.app.AlarmManager; +import android.app.AppGlobals; import android.app.PendingIntent; import android.content.BroadcastReceiver; 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.PackageManager.NameNotFoundException; import android.hardware.Sensor; import android.hardware.SensorManager; import android.hardware.TriggerEvent; @@ -47,7 +52,9 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseLongArray; import android.util.TimeUtils; import android.util.Xml; import android.view.Display; @@ -55,6 +62,7 @@ import android.view.Display; import com.android.internal.app.IBatteryStats; import com.android.internal.os.AtomicFile; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import com.android.server.am.BatteryStatsService; @@ -72,6 +80,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; +import java.util.Arrays; /** * Keeps track of device idleness and drives low power mode based on that. @@ -79,7 +88,8 @@ import java.nio.charset.StandardCharsets; public class DeviceIdleController extends SystemService { private static final String TAG = "DeviceIdleController"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean DEBUG = false; + private static final boolean COMPRESS_TIME = false; public static final String SERVICE_NAME = "deviceidle"; @@ -94,29 +104,31 @@ public class DeviceIdleController extends SystemService { * immediately after going inactive just because we don't want to be continually running * the significant motion sensor whenever the screen is off. */ - private static final long DEFAULT_INACTIVE_TIMEOUT = !DEBUG ? 30*60*1000L - : 2 * 60 * 1000L; + private static final long DEFAULT_INACTIVE_TIMEOUT = !COMPRESS_TIME ? 30*60*1000L + : 3 * 60 * 1000L; /** * This is the time, after seeing motion, that we wait after becoming inactive from * that until we start looking for motion again. */ - private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT = !DEBUG ? 10*60*1000L + private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT = !COMPRESS_TIME ? 10*60*1000L : 60 * 1000L; /** * This is the time, after the inactive timeout elapses, that we will wait looking * for significant motion until we truly consider the device to be idle. */ - private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT = !DEBUG ? 30*60*1000L - : 2 * 60 * 1000L; + private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT = !COMPRESS_TIME ? 30*60*1000L + : 3 * 60 * 1000L; /** * This is the initial time, after being idle, that we will allow ourself to be back * in the IDLE_PENDING state allowing the system to run normally until we return to idle. */ - private static final long DEFAULT_IDLE_PENDING_TIMEOUT = 5*60*1000L; + private static final long DEFAULT_IDLE_PENDING_TIMEOUT = !COMPRESS_TIME ? 5*60*1000L + : 30 * 1000L; /** * Maximum pending idle timeout (time spent running) we will be allowed to use. */ - private static final long DEFAULT_MAX_IDLE_PENDING_TIMEOUT = 10*60*1000L; + private static final long DEFAULT_MAX_IDLE_PENDING_TIMEOUT = !COMPRESS_TIME ? 10*60*1000L + : 60 * 1000L; /** * Scaling factor to apply to current pending idle timeout each time we cycle through * that state. @@ -126,13 +138,13 @@ public class DeviceIdleController extends SystemService { * This is the initial time that we want to sit in the idle state before waking up * again to return to pending idle and allowing normal work to run. */ - private static final long DEFAULT_IDLE_TIMEOUT = !DEBUG ? 60*60*1000L - : 5 * 60 * 1000L; + private static final long DEFAULT_IDLE_TIMEOUT = !COMPRESS_TIME ? 60*60*1000L + : 6 * 60 * 1000L; /** * Maximum idle duration we will be allowed to use. */ - private static final long DEFAULT_MAX_IDLE_TIMEOUT = !DEBUG ? 6*60*60*1000L - : 10 * 60 * 1000L; + private static final long DEFAULT_MAX_IDLE_TIMEOUT = !COMPRESS_TIME ? 6*60*60*1000L + : 30 * 60 * 1000L; /** * Scaling factor to apply to current idle timeout each time we cycle through that state. */ @@ -141,8 +153,13 @@ public class DeviceIdleController extends SystemService { * This is the minimum time we will allow until the next upcoming alarm for us to * actually go in to idle mode. */ - private static final long DEFAULT_MIN_TIME_TO_ALARM = !DEBUG ? 60*60*1000L - : 5 * 60 * 1000L; + private static final long DEFAULT_MIN_TIME_TO_ALARM = !COMPRESS_TIME ? 60*60*1000L + : 6 * 60 * 1000L; + + /** + * Max amount of time to temporarily whitelist an app when it receives a high priority tickle. + */ + private static final long MAX_TEMP_APP_WHITELIST_DURATION = 5 * 60 * 1000L; private AlarmManager mAlarmManager; private IBatteryStats mBatteryStats; @@ -210,6 +227,17 @@ public class DeviceIdleController extends SystemService { */ private int[] mPowerSaveWhitelistAppIdArray = new int[0]; + /** + * List of end times for UIDs that are temporarily marked as being allowed to access + * the network and acquire wakelocks. Times are in milliseconds. + */ + private SparseLongArray mTempWhitelistAppIdEndTimes = new SparseLongArray(); + + /** + * Current app IDs of temporarily whitelist apps for high-priority messages. + */ + private int[] mTempWhitelistAppIdArray = new int[0]; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { @@ -252,6 +280,7 @@ public class DeviceIdleController extends SystemService { static final int MSG_REPORT_IDLE_ON = 2; static final int MSG_REPORT_IDLE_OFF = 3; static final int MSG_REPORT_ACTIVE = 4; + static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 5; final class MyHandler extends Handler { MyHandler(Looper looper) { @@ -294,6 +323,10 @@ public class DeviceIdleController extends SystemService { getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); } } break; + case MSG_TEMP_APP_WHITELIST_TIMEOUT: { + int uid = msg.arg1; + checkTempAppWhitelistTimeout(uid); + } break; } } } @@ -325,10 +358,39 @@ public class DeviceIdleController extends SystemService { return getAppIdWhitelistInternal(); } + @Override public int[] getAppIdTempWhitelist() { + return getAppIdTempWhitelistInternal(); + } + @Override public boolean isPowerSaveWhitelistApp(String name) { return isPowerSaveWhitelistAppInternal(name); } + @Override public void addPowerSaveTempWhitelistApp(String packageName, long duration, + int userId) throws RemoteException { + getContext().enforceCallingPermission( + Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, + "No permission to change device idle whitelist"); + userId = ActivityManagerNative.getDefault().handleIncomingUser( + Binder.getCallingPid(), + Binder.getCallingUid(), + userId, + /*allowAll=*/ false, + /*requireFull=*/ false, + "addAppBrieflyToWhitelist", null); + final long token = Binder.clearCallingIdentity(); + try { + PackageInfo pi = AppGlobals.getPackageManager() + .getPackageInfo(packageName, 0, userId); + if (pi == null) return; + DeviceIdleController.this.addPowerSaveTempWhitelistAppInternal(packageName, + duration, userId); + } catch (RemoteException re) { + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { DeviceIdleController.this.dump(fd, pw, args); } @@ -481,6 +543,70 @@ public class DeviceIdleController extends SystemService { } } + public int[] getAppIdTempWhitelistInternal() { + synchronized (this) { + return mTempWhitelistAppIdArray; + } + } + + /** + * Adds an app to the temporary whitelist and resets the endTime for granting the + * app an exemption to access network and acquire wakelocks. + */ + public void addPowerSaveTempWhitelistAppInternal(String packageName, long duration, + int userId) { + if (duration > MAX_TEMP_APP_WHITELIST_DURATION) { + duration = MAX_TEMP_APP_WHITELIST_DURATION; + } + try { + int uid = getContext().getPackageManager().getPackageUid(packageName, userId); + int appId = UserHandle.getAppId(uid); + final long timeNow = System.currentTimeMillis(); + synchronized (this) { + long currentEndTime = mTempWhitelistAppIdEndTimes.get(appId); + // Set the new end time + mTempWhitelistAppIdEndTimes.put(appId, timeNow + duration); + if (DEBUG) { + Slog.d(TAG, "Adding AppId " + appId + " to temp whitelist"); + } + if (currentEndTime == 0) { + // No pending timeout for the app id, post a delayed message + postTempActiveTimeoutMessage(appId, duration); + updateTempWhitelistAppIdsLocked(); + reportTempWhitelistChangedLocked(); + } + } + } catch (NameNotFoundException e) { + } + } + + private void postTempActiveTimeoutMessage(int uid, long delay) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TEMP_APP_WHITELIST_TIMEOUT, uid, 0), + delay); + } + + void checkTempAppWhitelistTimeout(int uid) { + final long timeNow = System.currentTimeMillis(); + synchronized (this) { + long endTime = mTempWhitelistAppIdEndTimes.get(uid); + if (endTime == 0) { + // Nothing to do + return; + } + if (timeNow >= endTime) { + mTempWhitelistAppIdEndTimes.delete(uid); + if (DEBUG) { + Slog.d(TAG, "Removing UID " + uid + " from temp whitelist"); + } + updateTempWhitelistAppIdsLocked(); + reportTempWhitelistChangedLocked(); + } else { + // Need more time + postTempActiveTimeoutMessage(uid, endTime - timeNow); + } + } + } + void updateDisplayLocked() { mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); // We consider any situation where the display is showing something to be it on, @@ -659,14 +785,41 @@ public class DeviceIdleController extends SystemService { } mPowerSaveWhitelistAppIdArray = appids; if (mLocalPowerManager != null) { + if (DEBUG) { + Slog.d(TAG, "Setting wakelock whitelist to " + + Arrays.toString(mPowerSaveWhitelistAppIdArray)); + } mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAppIdArray); } } + private void updateTempWhitelistAppIdsLocked() { + final int size = mTempWhitelistAppIdEndTimes.size(); + if (mTempWhitelistAppIdArray.length != size) { + mTempWhitelistAppIdArray = new int[size]; + } + for (int i = 0; i < size; i++) { + mTempWhitelistAppIdArray[i] = mTempWhitelistAppIdEndTimes.keyAt(i); + } + if (mLocalPowerManager != null) { + if (DEBUG) { + Slog.d(TAG, "Setting wakelock temp whitelist to " + + Arrays.toString(mTempWhitelistAppIdArray)); + } + mLocalPowerManager.setDeviceIdleTempWhitelist(mTempWhitelistAppIdArray); + } + } + private void reportPowerSaveWhitelistChangedLocked() { Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - getContext().sendBroadcast(intent); + getContext().sendBroadcastAsUser(intent, UserHandle.OWNER); + } + + private void reportTempWhitelistChangedLocked() { + Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + getContext().sendBroadcastAsUser(intent, UserHandle.OWNER); } void readConfigFileLocked() { @@ -817,11 +970,18 @@ public class DeviceIdleController extends SystemService { } if (args != null) { + int userId = UserHandle.USER_OWNER; for (int i=0; i<args.length; i++) { String arg = args[i]; if ("-h".equals(arg)) { dumpHelp(pw); return; + } else if ("-u".equals(arg)) { + i++; + if (i < args.length) { + arg = args[i]; + userId = Integer.parseInt(arg); + } } else if ("-a".equals(arg)) { // Ignore, we always dump all. } else if ("step".equals(arg)) { @@ -873,6 +1033,17 @@ public class DeviceIdleController extends SystemService { } } return; + } else if ("tempwhitelist".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("At least one package name must be specified"); + return; + } + while (i < args.length) { + arg = args[i]; + i++; + addPowerSaveTempWhitelistAppInternal(arg, 10000L, userId); + } } else if (arg.length() > 0 && arg.charAt(0) == '-'){ pw.println("Unknown option: " + arg); return; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 792d4ba..7673af4 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -283,9 +283,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub /** * 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 */ private final SparseBooleanArray mPowerSaveWhitelistAppIds = new SparseBooleanArray(); + private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray(); + /** Set of ifaces that are metered. */ private ArraySet<String> mMeteredIfaces = new ArraySet<>(); /** Set of over-limit templates that have been notified. */ @@ -371,6 +374,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub } } + void updatePowerSaveTempWhitelistLocked() { + try { + final int[] whitelist = mDeviceIdleController.getAppIdTempWhitelist(); + mPowerSaveTempWhitelistAppIds.clear(); + if (whitelist != null) { + for (int uid : whitelist) { + mPowerSaveTempWhitelistAppIds.put(uid, true); + } + } + } catch (RemoteException e) { + } + } + public void systemReady() { if (!isBandwidthControlEnabled()) { Slog.w(TAG, "bandwidth controls disabled, unable to enforce policy"); @@ -392,6 +408,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub if (mRestrictPower != enabled) { mRestrictPower = enabled; updateRulesForGlobalChangeLocked(true); + updateRulesForTempWhitelistChangeLocked(); } } } @@ -404,6 +421,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub if (mRestrictBackground || mRestrictPower || mDeviceIdleMode) { updateRulesForGlobalChangeLocked(true); + updateRulesForTempWhitelistChangeLocked(); updateNotificationsLocked(); } } @@ -428,6 +446,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub // listen for changes to power save whitelist final IntentFilter whitelistFilter = new IntentFilter( PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); + whitelistFilter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED); mContext.registerReceiver(mPowerSaveWhitelistReceiver, whitelistFilter, null, mHandler); // watch for network interfaces to be claimed @@ -496,8 +515,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub public void onReceive(Context context, Intent intent) { // on background handler thread, and POWER_SAVE_WHITELIST_CHANGED is protected synchronized (mRulesLock) { - updatePowerSaveWhitelistLocked(); - updateRulesForGlobalChangeLocked(false); + if (PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED.equals(intent.getAction())) { + updatePowerSaveWhitelistLocked(); + updateRulesForGlobalChangeLocked(false); + } else { + updatePowerSaveTempWhitelistLocked(); + updateRulesForTempWhitelistChangeLocked(); + } } } }; @@ -2019,6 +2043,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub } } + void updateRulesForTempWhitelistChangeLocked() { + final List<UserInfo> users = mUserManager.getUsers(); + for (UserInfo user : users) { + for (int i = mPowerSaveTempWhitelistAppIds.size() - 1; i >= 0; i--) { + int appId = mPowerSaveTempWhitelistAppIds.keyAt(i); + int uid = UserHandle.getUid(user.id, appId); + updateRulesForUidLocked(uid); + } + } + } + private static boolean isUidValidForRules(int uid) { // allow rules on specific system services, and any apps if (uid == android.os.Process.MEDIA_UID || uid == android.os.Process.DRM_UID @@ -2065,8 +2100,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub // derive active rules based on policy and active state + int appId = UserHandle.getAppId(uid); int uidRules = RULE_ALLOW_ALL; - if (uidIdle && !mPowerSaveWhitelistAppIds.get(UserHandle.getAppId(uid))) { + if (uidIdle && !mPowerSaveWhitelistAppIds.get(appId) + && !mPowerSaveTempWhitelistAppIds.get(appId)) { uidRules = RULE_REJECT_ALL; } else if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) { // uid in background, and policy says to block metered data @@ -2077,7 +2114,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub uidRules = RULE_REJECT_METERED; } } else if (mRestrictPower || mDeviceIdleMode) { - final boolean whitelisted = mPowerSaveWhitelistAppIds.get(UserHandle.getAppId(uid)); + final boolean whitelisted = mPowerSaveWhitelistAppIds.get(appId) + || mPowerSaveTempWhitelistAppIds.get(appId); if (!whitelisted && !uidForeground && (uidPolicy & POLICY_ALLOW_BACKGROUND_BATTERY_SAVE) == 0) { // uid is in background, restrict power use mode is on (so we want to diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index c1fe984..3af97db 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -438,6 +438,9 @@ public final class PowerManagerService extends SystemService // Set of app ids that we will always respect the wake locks for. int[] mDeviceIdleWhitelist = new int[0]; + // Set of app ids that are temporarily allowed to acquire wakelocks due to high-pri message + int[] mDeviceIdleTempWhitelist = new int[0]; + private final SparseIntArray mUidState = new SparseIntArray(); // True if theater mode is enabled @@ -2320,6 +2323,15 @@ public final class PowerManagerService extends SystemService } } + void setDeviceIdleTempWhitelistInternal(int[] appids) { + synchronized (mLock) { + mDeviceIdleTempWhitelist = appids; + if (mDeviceIdleMode) { + updateWakeLockDisabledStatesLocked(); + } + } + } + void updateUidProcStateInternal(int uid, int procState) { synchronized (mLock) { mUidState.put(uid, procState); @@ -2372,6 +2384,7 @@ public final class PowerManagerService extends SystemService // for application uids that are not whitelisted. if (appid >= Process.FIRST_APPLICATION_UID && Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 && + Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) < 0 && mUidState.get(wakeLock.mOwnerUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY) > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { @@ -2579,6 +2592,7 @@ public final class PowerManagerService extends SystemService pw.println(" mBatteryLevelLow=" + mBatteryLevelLow); pw.println(" mDeviceIdleMode=" + mDeviceIdleMode); pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist)); + pw.println(" mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist)); pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime)); pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime)); pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime)); @@ -3478,6 +3492,11 @@ public final class PowerManagerService extends SystemService } @Override + public void setDeviceIdleTempWhitelist(int[] appids) { + setDeviceIdleTempWhitelistInternal(appids); + } + + @Override public void updateUidProcState(int uid, int procState) { updateUidProcStateInternal(uid, procState); } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index ff3bb28..23057c4 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -90,23 +90,24 @@ public class UsageStatsService extends SystemService implements static final String TAG = "UsageStatsService"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + static final boolean DEBUG = false; + private static final boolean COMPRESS_TIME = false; private static final long TEN_SECONDS = 10 * 1000; private static final long ONE_MINUTE = 60 * 1000; private static final long TWENTY_MINUTES = 20 * 60 * 1000; - private static final long FLUSH_INTERVAL = DEBUG ? TEN_SECONDS : TWENTY_MINUTES; + private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES; private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds. - static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = DEBUG ? ONE_MINUTE * 4 + static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE; // 12 hours of screen-on time sans dream-time - static final long DEFAULT_WALLCLOCK_APP_IDLE_THRESHOLD_MILLIS = DEBUG ? ONE_MINUTE * 8 + static final long DEFAULT_WALLCLOCK_APP_IDLE_THRESHOLD_MILLIS = COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE; // 2 days - static final long DEFAULT_CHECK_IDLE_INTERVAL = DEBUG ? ONE_MINUTE + static final long DEFAULT_CHECK_IDLE_INTERVAL = COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE; // 8 hours - static final long DEFAULT_PAROLE_INTERVAL = DEBUG ? ONE_MINUTE * 10 + static final long DEFAULT_PAROLE_INTERVAL = COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE; // 24 hours between paroles - static final long DEFAULT_PAROLE_DURATION = DEBUG ? ONE_MINUTE + static final long DEFAULT_PAROLE_DURATION = COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE; // 10 minutes // Handler message types. @@ -338,7 +339,6 @@ public class UsageStatsService extends SystemService implements /** Check all running users' apps to see if they enter an idle state. */ void checkIdleStates() { - if (DEBUG) Slog.d(TAG, "Checking idle state"); final int[] runningUsers; try { runningUsers = ActivityManagerNative.getDefault().getRunningUserIds(); @@ -994,6 +994,12 @@ public class UsageStatsService extends SystemService implements } @Override + public void whitelistAppTemporarily(String packageName, long duration, int userId) + throws RemoteException { + mDeviceIdleController.addPowerSaveTempWhitelistApp(packageName, duration, userId); + } + + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { |