From 0b4daca9ba54b7252ea8c159218391380eb00c8a Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 27 Apr 2015 09:47:32 -0700 Subject: [PATCH] Implement user-settable power save whitelist. The whitelist is now maintained by DeviceIdleController, which is moving out into its own independent system service. Network stats now queries it for the whitelist, instead of collecting that itself. Also did a few improvements in alarm manager -- made the code for moving alarms out of the pending list more robust, and fixed the debug output to always print the contents of the pending list even if we aren't in a pending state. (That would have helped me identify the problem much earlier.) Change-Id: I0f7119d4c553c3af4d77b2f71246fa6e2c13c561 --- Android.mk | 1 + .../android/net/INetworkPolicyManager.aidl | 2 - .../android/net/NetworkPolicyManager.java | 8 - .../android/os/IDeviceIdleController.aidl | 26 + core/java/android/os/PowerManager.java | 8 + .../internal/content/PackageMonitor.java | 1 - core/res/AndroidManifest.xml | 2 + .../android/server/AlarmManagerService.java | 25 +- .../android/server/DeviceIdleController.java | 879 ++++++++++++++++++ .../net/NetworkPolicyManagerService.java | 66 +- .../server/power/DeviceIdleController.java | 489 ---------- .../java/com/android/server/SystemServer.java | 4 +- 12 files changed, 972 insertions(+), 539 deletions(-) create mode 100644 core/java/android/os/IDeviceIdleController.aidl create mode 100644 services/core/java/com/android/server/DeviceIdleController.java delete mode 100644 services/core/java/com/android/server/power/DeviceIdleController.java diff --git a/Android.mk b/Android.mk index bd8b16a8ea1f4..b71f08c98dfb4 100644 --- a/Android.mk +++ b/Android.mk @@ -197,6 +197,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/IBatteryPropertiesListener.aidl \ core/java/android/os/IBatteryPropertiesRegistrar.aidl \ core/java/android/os/ICancellationSignal.aidl \ + core/java/android/os/IDeviceIdleController.aidl \ core/java/android/os/IMessenger.aidl \ core/java/android/os/INetworkActivityListener.aidl \ core/java/android/os/INetworkManagementService.aidl \ diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index c722fbce32bfa..7f5f3776d3556 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -38,8 +38,6 @@ interface INetworkPolicyManager { boolean isUidForeground(int uid); - int[] getPowerSaveAppIdWhitelist(); - void registerListener(INetworkPolicyListener listener); void unregisterListener(INetworkPolicyListener listener); diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index bc036376735d4..b4c7b2b4d4d2f 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -129,14 +129,6 @@ public class NetworkPolicyManager { } } - public int[] getPowerSaveAppIdWhitelist() { - try { - return mService.getPowerSaveAppIdWhitelist(); - } catch (RemoteException e) { - return new int[0]; - } - } - public void registerListener(INetworkPolicyListener listener) { try { mService.registerListener(listener); diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl new file mode 100644 index 0000000000000..3cb29ff91c6b2 --- /dev/null +++ b/core/java/android/os/IDeviceIdleController.aidl @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** @hide */ +interface IDeviceIdleController { + void addPowerSaveWhitelistApp(String name); + void removePowerSaveWhitelistApp(String name); + String[] getSystemPowerWhitelist(); + String[] getFullPowerWhitelist(); + int[] getAppIdWhitelist(); +} diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 01c9a21441ab3..1d9d7d2191ae7 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -919,6 +919,14 @@ public final class PowerManager { public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED"; + /** + * @hide Intent that is broadcast when the set of power save whitelist apps has changed. + * This broadcast is only sent to registered receivers. + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_POWER_SAVE_WHITELIST_CHANGED + = "android.os.action.POWER_SAVE_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/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index eff44bd1c87fd..481ab0e5be31d 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -44,7 +44,6 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED); sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED); - sPackageFilt.addAction(Intent.ACTION_UID_REMOVED); sPackageFilt.addDataScheme("package"); sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED); sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 45c078dfe6de8..942e6a68b3866 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -79,6 +79,8 @@ + + diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 0e3867d6ff5cc..745c1906c46ce 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -461,7 +461,7 @@ class AlarmManagerService extends SystemService { // to run during this time are placed in mPendingWhileIdleAlarms Alarm mPendingIdleUntil = null; Alarm mNextWakeFromIdle = null; - final ArrayList mPendingWhileIdleAlarms = new ArrayList<>(); + ArrayList mPendingWhileIdleAlarms = new ArrayList<>(); public AlarmManagerService(Context context) { super(context); @@ -567,10 +567,14 @@ class AlarmManagerService extends SystemService { void restorePendingWhileIdleAlarmsLocked() { // Bring pending alarms back into the main list. - final long nowElapsed = SystemClock.elapsedRealtime(); - for (int i=mPendingWhileIdleAlarms.size() - 1; i >= 0 && mPendingIdleUntil == null; i--) { - Alarm a = mPendingWhileIdleAlarms.remove(i); - reAddAlarmLocked(a, nowElapsed, false); + if (mPendingWhileIdleAlarms.size() > 0) { + ArrayList alarms = mPendingWhileIdleAlarms; + mPendingWhileIdleAlarms = new ArrayList<>(); + final long nowElapsed = SystemClock.elapsedRealtime(); + for (int i=alarms.size() - 1; i >= 0; i--) { + Alarm a = alarms.get(i); + reAddAlarmLocked(a, nowElapsed, false); + } } // Reschedule everything. @@ -1053,11 +1057,16 @@ class AlarmManagerService extends SystemService { dumpAlarmList(pw, b.alarms, " ", nowELAPSED, nowRTC, sdf); } } - if (mPendingIdleUntil != null) { + if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) { pw.println(); pw.println("Idle mode state:"); - pw.print(" Idling until: "); pw.println(mPendingIdleUntil); - mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf); + pw.print(" Idling until: "); + if (mPendingIdleUntil != null) { + pw.println(mPendingIdleUntil); + mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf); + } else { + pw.println("null"); + } pw.println(" Pending alarms:"); dumpAlarmList(pw, mPendingWhileIdleAlarms, " ", nowELAPSED, nowRTC, sdf); } diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java new file mode 100644 index 0000000000000..b7bc0f02ea4f6 --- /dev/null +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -0,0 +1,879 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.AlarmManager; +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.PackageManager; +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.hardware.TriggerEvent; +import android.hardware.TriggerEventListener; +import android.hardware.display.DisplayManager; +import android.net.INetworkPolicyManager; +import android.os.Binder; +import android.os.Environment; +import android.os.FileUtils; +import android.os.Handler; +import android.os.IDeviceIdleController; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.PowerManagerInternal; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Slog; +import android.util.SparseBooleanArray; +import android.util.TimeUtils; +import android.util.Xml; +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.FastXmlSerializer; +import com.android.internal.util.XmlUtils; +import com.android.server.am.BatteryStatsService; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * Keeps track of device idleness and drives low power mode based on that. + */ +public class DeviceIdleController extends SystemService { + private static final String TAG = "DeviceIdleController"; + + public static final String SERVICE_NAME = "deviceidle"; + + private static final String ACTION_STEP_IDLE_STATE = + "com.android.server.device_idle.STEP_IDLE_STATE"; + + // TODO: These need to be moved to system settings. + + /** + * This is the time, after becoming inactive, at which we start looking at the + * motion sensor to determine if the device is being left alone. We don't do this + * 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 = 30*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 = 10*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 = 30*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; + /** + * 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; + /** + * Scaling factor to apply to current pending idle timeout each time we cycle through + * that state. + */ + private static final float DEFAULT_IDLE_PENDING_FACTOR = 2f; + /** + * 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 = 60*60*1000L; + /** + * Maximum idle duration we will be allowed to use. + */ + private static final long DEFAULT_MAX_IDLE_TIMEOUT = 6*60*60*1000L; + /** + * Scaling factor to apply to current idle timeout each time we cycle through that state. + */ + private static final float DEFAULT_IDLE_FACTOR = 2f; + /** + * 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 = 60*60*1000L; + + private AlarmManager mAlarmManager; + private IBatteryStats mBatteryStats; + private PowerManagerInternal mLocalPowerManager; + private INetworkPolicyManager mNetworkPolicyManager; + private DisplayManager mDisplayManager; + private SensorManager mSensorManager; + private Sensor mSigMotionSensor; + private PendingIntent mAlarmIntent; + private Intent mIdleIntent; + private Display mCurDisplay; + private boolean mScreenOn; + private boolean mCharging; + private boolean mSigMotionActive; + + /** Device is currently active. */ + private static final int STATE_ACTIVE = 0; + /** Device is inactve (screen off, no motion) and we are waiting to for idle. */ + private static final int STATE_INACTIVE = 1; + /** Device is past the initial inactive period, and waiting for the next idle period. */ + private static final int STATE_IDLE_PENDING = 2; + /** Device is in the idle state, trying to stay asleep as much as possible. */ + private static final int STATE_IDLE = 3; + /** Device is in the idle state, but temporarily out of idle to do regular maintenance. */ + private static final int STATE_IDLE_MAINTENANCE = 4; + private static String stateToString(int state) { + switch (state) { + case STATE_ACTIVE: return "ACTIVE"; + case STATE_INACTIVE: return "INACTIVE"; + case STATE_IDLE_PENDING: return "IDLE_PENDING"; + case STATE_IDLE: return "IDLE"; + case STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE"; + default: return Integer.toString(state); + } + } + + private int mState; + + private long mInactiveTimeout; + private long mNextAlarmTime; + private long mNextIdlePendingDelay; + private long mNextIdleDelay; + + public final AtomicFile mConfigFile; + + /** + * Package names the system has white-listed to opt out of power save restrictions. + */ + private final ArrayMap mPowerSaveWhitelistApps = new ArrayMap<>(); + + /** + * Package names the user has white-listed to opt out of power save restrictions. + */ + private final ArrayMap mPowerSaveWhitelistUserApps = new ArrayMap<>(); + + /** + * UIDs that have been white-listed to opt out of power save restrictions. + */ + private final SparseBooleanArray mPowerSaveWhitelistAppIds = new SparseBooleanArray(); + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { + int plugged = intent.getIntExtra("plugged", 0); + updateChargingLocked(plugged != 0); + } else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) { + synchronized (DeviceIdleController.this) { + stepIdleStateLocked(); + } + } + } + }; + + private final DisplayManager.DisplayListener mDisplayListener + = new DisplayManager.DisplayListener() { + @Override public void onDisplayAdded(int displayId) { + } + + @Override public void onDisplayRemoved(int displayId) { + } + + @Override public void onDisplayChanged(int displayId) { + if (displayId == Display.DEFAULT_DISPLAY) { + synchronized (DeviceIdleController.this) { + updateDisplayLocked(); + } + } + } + }; + + private final TriggerEventListener mSigMotionListener = new TriggerEventListener() { + @Override public void onTrigger(TriggerEvent event) { + synchronized (DeviceIdleController.this) { + significantMotionLocked(); + } + } + }; + + static final int MSG_WRITE_CONFIG = 1; + static final int MSG_REPORT_IDLE_ON = 2; + static final int MSG_REPORT_IDLE_OFF = 3; + static final int MSG_REPORT_ACTIVE = 4; + + final class MyHandler extends Handler { + MyHandler(Looper looper) { + super(looper); + } + + @Override public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_WRITE_CONFIG: { + handleWriteConfigFile(); + } break; + case MSG_REPORT_IDLE_ON: { + mLocalPowerManager.setDeviceIdleMode(true); + try { + mNetworkPolicyManager.setDeviceIdleMode(true); + mBatteryStats.noteDeviceIdleMode(true, false, false); + } catch (RemoteException e) { + } + getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); + } break; + case MSG_REPORT_IDLE_OFF: { + mLocalPowerManager.setDeviceIdleMode(false); + try { + mNetworkPolicyManager.setDeviceIdleMode(false); + mBatteryStats.noteDeviceIdleMode(false, false, false); + } catch (RemoteException e) { + } + getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); + } break; + case MSG_REPORT_ACTIVE: { + boolean fromMotion = msg.arg1 != 0; + boolean needBroadcast = msg.arg2 != 0; + mLocalPowerManager.setDeviceIdleMode(false); + try { + mNetworkPolicyManager.setDeviceIdleMode(false); + mBatteryStats.noteDeviceIdleMode(false, !fromMotion, fromMotion); + } catch (RemoteException e) { + } + if (needBroadcast) { + getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); + } + } break; + } + } + } + + final MyHandler mHandler; + + private final class BinderService extends IDeviceIdleController.Stub { + @Override public void addPowerSaveWhitelistApp(String name) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, + null); + addPowerSaveWhitelistAppInternal(name); + } + + @Override public void removePowerSaveWhitelistApp(String name) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, + null); + removePowerSaveWhitelistAppInternal(name); + } + + @Override public String[] getSystemPowerWhitelist() { + return getSystemPowerWhitelistInternal(); + } + + @Override public String[] getFullPowerWhitelist() { + return getFullPowerWhitelistInternal(); + } + + @Override public int[] getAppIdWhitelist() { + return getAppIdWhitelistInternal(); + } + + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + DeviceIdleController.this.dump(fd, pw, args); + } + } + + public DeviceIdleController(Context context) { + super(context); + mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml")); + mHandler = new MyHandler(BackgroundThread.getHandler().getLooper()); + } + + private static File getSystemDir() { + return new File(Environment.getDataDirectory(), "system"); + } + + @Override + public void onStart() { + final PackageManager pm = getContext().getPackageManager(); + + synchronized (this) { + SystemConfig sysConfig = SystemConfig.getInstance(); + ArraySet allowPower = sysConfig.getAllowInPowerSave(); + for (int i=0; i mAlarmManager.getNextWakeFromIdleTime()) { + // Whoops, there is an upcoming alarm. We don't actually want to go idle. + if (mState != STATE_ACTIVE) { + becomeActiveLocked("alarm"); + } + return; + } + + switch (mState) { + case STATE_INACTIVE: + // We have now been inactive long enough, it is time to start looking + // for significant motion and sleep some more while doing so. + startMonitoringSignificantMotion(); + scheduleAlarmLocked(DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT, false); + // Reset the upcoming idle delays. + mNextIdlePendingDelay = DEFAULT_IDLE_PENDING_TIMEOUT; + mNextIdleDelay = DEFAULT_IDLE_TIMEOUT; + mState = STATE_IDLE_PENDING; + EventLogTags.writeDeviceIdle(mState, "step"); + break; + case STATE_IDLE_PENDING: + case STATE_IDLE_MAINTENANCE: + // We have been waiting to become idle, and now it is time! This is the + // only case where we want to use a wakeup alarm, because we do want to + // drag the device out of its sleep state in this case to do the next + // scheduled work. + scheduleAlarmLocked(mNextIdleDelay, true); + mNextIdleDelay = (long)(mNextIdleDelay*DEFAULT_IDLE_FACTOR); + if (mNextIdleDelay > DEFAULT_MAX_IDLE_TIMEOUT) { + mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT; + } + mState = STATE_IDLE; + EventLogTags.writeDeviceIdle(mState, "step"); + mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON); + break; + case STATE_IDLE: + // We have been idling long enough, now it is time to do some work. + scheduleAlarmLocked(mNextIdlePendingDelay, false); + mNextIdlePendingDelay = (long)(mNextIdlePendingDelay*DEFAULT_IDLE_PENDING_FACTOR); + if (mNextIdlePendingDelay > DEFAULT_MAX_IDLE_PENDING_TIMEOUT) { + mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT; + } + mState = STATE_IDLE_MAINTENANCE; + EventLogTags.writeDeviceIdle(mState, "step"); + mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF); + break; + } + } + + void significantMotionLocked() { + // When the sensor goes off, its trigger is automatically removed. + mSigMotionActive = false; + // The device is not yet active, so we want to go back to the pending idle + // state to wait again for no motion. Note that we only monitor for significant + // motion after moving out of the inactive state, so no need to worry about that. + if (mState != STATE_ACTIVE) { + scheduleReportActiveLocked(true); + mState = STATE_ACTIVE; + mInactiveTimeout = DEFAULT_MOTION_INACTIVE_TIMEOUT; + EventLogTags.writeDeviceIdle(mState, "motion"); + becomeInactiveIfAppropriateLocked(); + } + } + + void startMonitoringSignificantMotion() { + if (mSigMotionSensor != null && !mSigMotionActive) { + mSensorManager.requestTriggerSensor(mSigMotionListener, mSigMotionSensor); + mSigMotionActive = true; + } + } + + void stopMonitoringSignificantMotion() { + if (mSigMotionActive) { + mSensorManager.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor); + mSigMotionActive = false; + } + } + + void cancelAlarmLocked() { + if (mNextAlarmTime != 0) { + mNextAlarmTime = 0; + mAlarmManager.cancel(mAlarmIntent); + } + } + + void scheduleAlarmLocked(long delay, boolean idleUntil) { + if (mSigMotionSensor == null) { + // If there is no significant motion sensor on this device, then we won't schedule + // alarms, because we can't determine if the device is not moving. This effectively + // turns off normal exeuction of device idling, although it is still possible to + // manually poke it by pretending like the alarm is going off. + return; + } + mNextAlarmTime = SystemClock.elapsedRealtime() + delay; + if (idleUntil) { + mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP, + mNextAlarmTime, mAlarmIntent); + } else { + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + mNextAlarmTime, mAlarmIntent); + } + } + + private void updateWhitelistAppIdsLocked() { + mPowerSaveWhitelistAppIds.clear(); + for (int i=0; i outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("wl")) { + String name = parser.getAttributeValue(null, "n"); + if (name != null) { + try { + ApplicationInfo ai = pm.getApplicationInfo(name, 0); + mPowerSaveWhitelistUserApps.put(ai.packageName, + UserHandle.getAppId(ai.uid)); + } catch (PackageManager.NameNotFoundException e) { + } + } + } else { + Slog.w(TAG, "Unknown element under : " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + } catch (IllegalStateException e) { + Slog.w(TAG, "Failed parsing config " + e); + } catch (NullPointerException e) { + Slog.w(TAG, "Failed parsing config " + e); + } catch (NumberFormatException e) { + Slog.w(TAG, "Failed parsing config " + e); + } catch (XmlPullParserException e) { + Slog.w(TAG, "Failed parsing config " + e); + } catch (IOException e) { + Slog.w(TAG, "Failed parsing config " + e); + } catch (IndexOutOfBoundsException e) { + Slog.w(TAG, "Failed parsing config " + e); + } + } + + void writeConfigFileLocked() { + mHandler.removeMessages(MSG_WRITE_CONFIG); + mHandler.sendEmptyMessageDelayed(MSG_WRITE_CONFIG, 5000); + } + + void handleWriteConfigFile() { + final ByteArrayOutputStream memStream = new ByteArrayOutputStream(); + + try { + synchronized (this) { + XmlSerializer out = new FastXmlSerializer(); + out.setOutput(memStream, "utf-8"); + writeConfigFileLocked(out); + } + } catch (IOException e) { + } + + synchronized (mConfigFile) { + FileOutputStream stream = null; + try { + stream = mConfigFile.startWrite(); + memStream.writeTo(stream); + stream.flush(); + FileUtils.sync(stream); + stream.close(); + mConfigFile.finishWrite(stream); + } catch (IOException e) { + Slog.w(TAG, "Error writing config file", e); + mConfigFile.failWrite(stream); + } + } + } + + void writeConfigFileLocked(XmlSerializer out) throws IOException { + out.startDocument(null, true); + out.startTag(null, "config"); + for (int i=0; i 0 && arg.charAt(0) == '-'){ + pw.println("Unknown option: " + arg); + return; + } else { + pw.println("Unknown command: " + arg); + return; + } + } + } + + synchronized (this) { + int size = mPowerSaveWhitelistApps.size(); + if (size > 0) { + pw.println(" Whitelist system apps:"); + for (int i = 0; i < size; i++) { + pw.print(" "); + pw.println(mPowerSaveWhitelistApps.keyAt(i)); + } + } + size = mPowerSaveWhitelistUserApps.size(); + if (size > 0) { + pw.println(" Whitelist user apps:"); + for (int i = 0; i < size; i++) { + pw.print(" "); + pw.println(mPowerSaveWhitelistUserApps.keyAt(i)); + } + } + size = mPowerSaveWhitelistAppIds.size(); + if (size > 0) { + pw.println(" Whitelist app uids:"); + for (int i = 0; i < size; i++) { + pw.print(" UID="); + pw.print(mPowerSaveWhitelistAppIds.keyAt(i)); + pw.print(": "); + pw.print(mPowerSaveWhitelistAppIds.valueAt(i)); + pw.println(); + } + } + pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor); + pw.print(" mCurDisplay="); pw.println(mCurDisplay); + pw.print(" mScreenOn="); pw.println(mScreenOn); + pw.print(" mCharging="); pw.println(mCharging); + pw.print(" mSigMotionActive="); pw.println(mSigMotionActive); + pw.print(" mState="); pw.println(stateToString(mState)); + pw.print(" mInactiveTimeout="); TimeUtils.formatDuration(mInactiveTimeout, pw); + pw.println(); + if (mNextAlarmTime != 0) { + pw.print(" mNextAlarmTime="); + TimeUtils.formatDuration(mNextAlarmTime, SystemClock.elapsedRealtime(), pw); + pw.println(); + } + if (mNextIdlePendingDelay != 0) { + pw.print(" mNextIdlePendingDelay="); + TimeUtils.formatDuration(mNextIdlePendingDelay, pw); + pw.println(); + } + if (mNextIdleDelay != 0) { + pw.print(" mNextIdleDelay="); + TimeUtils.formatDuration(mNextIdleDelay, pw); + pw.println(); + } + } + } +} diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 818f0aa0b36ed..e45092c0b605a 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -110,13 +110,16 @@ import android.os.Binder; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; +import android.os.IDeviceIdleController; import android.os.INetworkManagementService; import android.os.IPowerManager; import android.os.Message; import android.os.MessageQueue.IdleHandler; +import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -138,6 +141,7 @@ import android.util.TrustedTime; import android.util.Xml; import com.android.server.AppOpsService; +import com.android.server.DeviceIdleController; import libcore.io.IoUtils; import com.android.internal.R; @@ -245,6 +249,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private IConnectivityManager mConnManager; private INotificationManager mNotifManager; private PowerManagerInternal mPowerManagerInternal; + private IDeviceIdleController mDeviceIdleController; final Object mRulesLock = new Object(); @@ -321,6 +326,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mPowerManager = checkNotNull(powerManager, "missing powerManager"); mNetworkStats = checkNotNull(networkStats, "missing networkStats"); mNetworkManager = checkNotNull(networkManagement, "missing networkManagement"); + mDeviceIdleController = IDeviceIdleController.Stub.asInterface(ServiceManager.getService( + DeviceIdleController.SERVICE_NAME)); mTime = checkNotNull(time, "missing TrustedTime"); HandlerThread thread = new HandlerThread(TAG); @@ -342,28 +349,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mNotifManager = checkNotNull(notifManager, "missing INotificationManager"); } + void updatePowerSaveWhitelistLocked() { + try { + final int[] whitelist = mDeviceIdleController.getAppIdWhitelist(); + mPowerSaveWhitelistAppIds.clear(); + if (whitelist != null) { + for (int uid : whitelist) { + mPowerSaveWhitelistAppIds.put(uid, true); + } + } + } catch (RemoteException e) { + } + } + public void systemReady() { if (!isBandwidthControlEnabled()) { Slog.w(TAG, "bandwidth controls disabled, unable to enforce policy"); return; } - final PackageManager pm = mContext.getPackageManager(); - synchronized (mRulesLock) { - SystemConfig sysConfig = SystemConfig.getInstance(); - ArraySet allowPower = sysConfig.getAllowInPowerSave(); - for (int i=0; i mAlarmManager.getNextWakeFromIdleTime()) { - // Whoops, there is an upcoming alarm. We don't actually want to go idle. - if (mState != STATE_ACTIVE) { - becomeActiveLocked("alarm"); - } - return; - } - - switch (mState) { - case STATE_INACTIVE: - // We have now been inactive long enough, it is time to start looking - // for significant motion and sleep some more while doing so. - startMonitoringSignificantMotion(); - scheduleAlarmLocked(DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT, false); - // Reset the upcoming idle delays. - mNextIdlePendingDelay = DEFAULT_IDLE_PENDING_TIMEOUT; - mNextIdleDelay = DEFAULT_IDLE_TIMEOUT; - mState = STATE_IDLE_PENDING; - EventLogTags.writeDeviceIdle(mState, "step"); - break; - case STATE_IDLE_PENDING: - // We have been waiting to become idle, and now it is time! This is the - // only case where we want to use a wakeup alarm, because we do want to - // drag the device out of its sleep state in this case to do the next - // scheduled work. - scheduleAlarmLocked(mNextIdleDelay, true); - mNextIdleDelay = (long)(mNextIdleDelay*DEFAULT_IDLE_FACTOR); - if (mNextIdleDelay > DEFAULT_MAX_IDLE_TIMEOUT) { - mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT; - } - mState = STATE_IDLE; - EventLogTags.writeDeviceIdle(mState, "step"); - mLocalPowerManager.setDeviceIdleMode(true); - try { - mNetworkPolicyManager.setDeviceIdleMode(true); - mBatteryStats.noteDeviceIdleMode(true, false, false); - } catch (RemoteException e) { - } - getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); - break; - case STATE_IDLE: - // We have been idling long enough, now it is time to do some work. - scheduleAlarmLocked(mNextIdlePendingDelay, false); - mNextIdlePendingDelay = (long)(mNextIdlePendingDelay*DEFAULT_IDLE_PENDING_FACTOR); - if (mNextIdlePendingDelay > DEFAULT_MAX_IDLE_PENDING_TIMEOUT) { - mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT; - } - mState = STATE_IDLE_PENDING; - EventLogTags.writeDeviceIdle(mState, "step"); - mLocalPowerManager.setDeviceIdleMode(false); - try { - mNetworkPolicyManager.setDeviceIdleMode(false); - mBatteryStats.noteDeviceIdleMode(false, false, false); - } catch (RemoteException e) { - } - getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); - break; - } - } - - void significantMotionLocked() { - // When the sensor goes off, its trigger is automatically removed. - mSigMotionActive = false; - // The device is not yet active, so we want to go back to the pending idle - // state to wait again for no motion. Note that we only monitor for significant - // motion after moving out of the inactive state, so no need to worry about that. - if (mState != STATE_ACTIVE) { - mLocalPowerManager.setDeviceIdleMode(false); - try { - mNetworkPolicyManager.setDeviceIdleMode(false); - mBatteryStats.noteDeviceIdleMode(false, false, true); - } catch (RemoteException e) { - } - if (mState == STATE_IDLE) { - getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); - } - mState = STATE_ACTIVE; - mInactiveTimeout = DEFAULT_MOTION_INACTIVE_TIMEOUT; - EventLogTags.writeDeviceIdle(mState, "motion"); - becomeInactiveIfAppropriateLocked(); - } - } - - void startMonitoringSignificantMotion() { - if (mSigMotionSensor != null && !mSigMotionActive) { - mSensorManager.requestTriggerSensor(mSigMotionListener, mSigMotionSensor); - mSigMotionActive = true; - } - } - - void stopMonitoringSignificantMotion() { - if (mSigMotionActive) { - mSensorManager.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor); - mSigMotionActive = false; - } - } - - void cancelAlarmLocked() { - if (mNextAlarmTime != 0) { - mNextAlarmTime = 0; - mAlarmManager.cancel(mAlarmIntent); - } - } - - void scheduleAlarmLocked(long delay, boolean idleUntil) { - if (mSigMotionSensor == null) { - // If there is no significant motion sensor on this device, then we won't schedule - // alarms, because we can't determine if the device is not moving. This effectively - // turns off normal exeuction of device idling, although it is still possible to - // manually poke it by pretending like the alarm is going off. - return; - } - mNextAlarmTime = SystemClock.elapsedRealtime() + delay; - if (idleUntil) { - mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP, - mNextAlarmTime, mAlarmIntent); - } else { - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - mNextAlarmTime, mAlarmIntent); - } - } - - private void dumpHelp(PrintWriter pw) { - pw.println("Device idle controller (deviceidle) dump options:"); - pw.println(" [-h] [CMD]"); - pw.println(" -h: print this help text."); - pw.println("Commands:"); - pw.println(" step"); - pw.println(" Immediately step to next state, without waiting for alarm."); - } - - void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump DeviceIdleController from from pid=" - + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " without permission " + android.Manifest.permission.DUMP); - return; - } - - if (args != null) { - for (int i=0; i 0 && arg.charAt(0) == '-'){ - pw.println("Unknown option: " + arg); - dumpHelp(pw); - return; - } else { - pw.println("Unknown command: " + arg); - dumpHelp(pw); - return; - } - } - } - - synchronized (this) { - pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor); - pw.print(" mCurDisplay="); pw.println(mCurDisplay); - pw.print(" mScreenOn="); pw.println(mScreenOn); - pw.print(" mCharging="); pw.println(mCharging); - pw.print(" mSigMotionActive="); pw.println(mSigMotionActive); - pw.print(" mState="); pw.println(stateToString(mState)); - pw.print(" mInactiveTimeout="); TimeUtils.formatDuration(mInactiveTimeout, pw); - pw.println(); - if (mNextAlarmTime != 0) { - pw.print(" mNextAlarmTime="); - TimeUtils.formatDuration(mNextAlarmTime, SystemClock.elapsedRealtime(), pw); - pw.println(); - } - if (mNextIdlePendingDelay != 0) { - pw.print(" mNextIdlePendingDelay="); - TimeUtils.formatDuration(mNextIdlePendingDelay, pw); - pw.println(); - } - if (mNextIdleDelay != 0) { - pw.print(" mNextIdleDelay="); - TimeUtils.formatDuration(mNextIdleDelay, pw); - pw.println(); - } - } - } -} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 593853c172638..29221305fc86e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -76,7 +76,6 @@ import com.android.server.pm.Installer; import com.android.server.pm.LauncherAppsService; import com.android.server.pm.PackageManagerService; import com.android.server.pm.UserManagerService; -import com.android.server.power.DeviceIdleController; import com.android.server.power.PowerManagerService; import com.android.server.power.ShutdownThread; import com.android.server.restrictions.RestrictionsManagerService; @@ -599,6 +598,8 @@ public final class SystemServer { mSystemServiceManager.startService(PersistentDataBlockService.class); } + mSystemServiceManager.startService(DeviceIdleController.class); + // Always start the Device Policy Manager, so that the API is compatible with // API8. mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class); @@ -965,7 +966,6 @@ public final class SystemServer { if (!disableNonCoreServices) { mSystemServiceManager.startService(MediaProjectionManagerService.class); - mSystemServiceManager.startService(DeviceIdleController.class); } // Before things start rolling, be sure we have decided whether