diff options
author | Dianne Hackborn <hackbod@google.com> | 2015-04-29 20:33:50 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-04-29 20:33:51 +0000 |
commit | 9ac2718e7dcf274ad41fbb374bedabadc558634b (patch) | |
tree | 1426dba6414d1ad0109f52e632d16ba54a62b33c | |
parent | f105f61dd9ff790c6ace406e73c3bddac87a8925 (diff) | |
parent | 0b4daca9ba54b7252ea8c159218391380eb00c8a (diff) | |
download | frameworks_base-9ac2718e7dcf274ad41fbb374bedabadc558634b.zip frameworks_base-9ac2718e7dcf274ad41fbb374bedabadc558634b.tar.gz frameworks_base-9ac2718e7dcf274ad41fbb374bedabadc558634b.tar.bz2 |
Merge "Implement user-settable power save whitelist." into mnc-dev
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | core/java/android/net/INetworkPolicyManager.aidl | 2 | ||||
-rw-r--r-- | core/java/android/net/NetworkPolicyManager.java | 8 | ||||
-rw-r--r-- | core/java/android/os/IDeviceIdleController.aidl | 26 | ||||
-rw-r--r-- | core/java/android/os/PowerManager.java | 8 | ||||
-rw-r--r-- | core/java/com/android/internal/content/PackageMonitor.java | 1 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 2 | ||||
-rw-r--r-- | services/core/java/com/android/server/AlarmManagerService.java | 25 | ||||
-rw-r--r-- | services/core/java/com/android/server/DeviceIdleController.java | 879 | ||||
-rw-r--r-- | services/core/java/com/android/server/net/NetworkPolicyManagerService.java | 66 | ||||
-rw-r--r-- | services/core/java/com/android/server/power/DeviceIdleController.java | 489 | ||||
-rw-r--r-- | services/java/com/android/server/SystemServer.java | 4 |
12 files changed, 972 insertions, 539 deletions
@@ -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 c722fbc..7f5f377 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 bc03637..b4c7b2b 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 0000000..3cb29ff --- /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 01c9a21..1d9d7d2 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -920,6 +920,14 @@ public final class PowerManager { = "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 eff44bd..481ab0e 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 45c078d..942e6a6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -79,6 +79,8 @@ <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" /> <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.SCREEN_BRIGHTNESS_BOOST_CHANGED" /> diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 0e3867d..745c190 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<Alarm> mPendingWhileIdleAlarms = new ArrayList<>(); + ArrayList<Alarm> 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<Alarm> 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 0000000..b7bc0f0 --- /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<String, Integer> mPowerSaveWhitelistApps = new ArrayMap<>(); + + /** + * Package names the user has white-listed to opt out of power save restrictions. + */ + private final ArrayMap<String, Integer> 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<String> allowPower = sysConfig.getAllowInPowerSave(); + for (int i=0; i<allowPower.size(); i++) { + String pkg = allowPower.valueAt(i); + try { + ApplicationInfo ai = pm.getApplicationInfo(pkg, 0); + if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { + mPowerSaveWhitelistApps.put(ai.packageName, + UserHandle.getAppId(ai.uid)); + } + } catch (PackageManager.NameNotFoundException e) { + } + } + + readConfigFileLocked(); + updateWhitelistAppIdsLocked(); + + mScreenOn = true; + // Start out assuming we are charging. If we aren't, we will at least get + // a battery update the next time the level drops. + mCharging = true; + mState = STATE_ACTIVE; + mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT; + } + + publishBinderService(SERVICE_NAME, new BinderService()); + } + + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_SYSTEM_SERVICES_READY) { + synchronized (this) { + mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); + mBatteryStats = BatteryStatsService.getService(); + mLocalPowerManager = getLocalService(PowerManagerInternal.class); + mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); + mDisplayManager = (DisplayManager) getContext().getSystemService( + Context.DISPLAY_SERVICE); + mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE); + mSigMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION); + + Intent intent = new Intent(ACTION_STEP_IDLE_STATE) + .setPackage("android") + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0); + + mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + filter.addAction(ACTION_STEP_IDLE_STATE); + getContext().registerReceiver(mReceiver, filter); + + mDisplayManager.registerDisplayListener(mDisplayListener, null); + updateDisplayLocked(); + } + } + } + + public boolean addPowerSaveWhitelistAppInternal(String name) { + synchronized (this) { + try { + ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name, 0); + if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid)) == null) { + reportPowerSaveWhitelistChangedLocked(); + updateWhitelistAppIdsLocked(); + writeConfigFileLocked(); + } + return true; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + } + + public boolean removePowerSaveWhitelistAppInternal(String name) { + synchronized (this) { + if (mPowerSaveWhitelistUserApps.remove(name) != null) { + reportPowerSaveWhitelistChangedLocked(); + updateWhitelistAppIdsLocked(); + writeConfigFileLocked(); + return true; + } + } + return false; + } + + public String[] getSystemPowerWhitelistInternal() { + synchronized (this) { + int size = mPowerSaveWhitelistApps.size(); + String[] apps = new String[size]; + for (int i = 0; i < mPowerSaveWhitelistApps.size(); i++) { + apps[i] = mPowerSaveWhitelistApps.keyAt(i); + } + return apps; + } + } + + public String[] getFullPowerWhitelistInternal() { + synchronized (this) { + int size = mPowerSaveWhitelistApps.size() + mPowerSaveWhitelistUserApps.size(); + String[] apps = new String[size]; + int cur = 0; + for (int i = 0; i < mPowerSaveWhitelistApps.size(); i++) { + apps[cur] = mPowerSaveWhitelistApps.keyAt(i); + cur++; + } + for (int i = 0; i < mPowerSaveWhitelistUserApps.size(); i++) { + apps[cur] = mPowerSaveWhitelistUserApps.keyAt(i); + cur++; + } + return apps; + } + } + + public int[] getAppIdWhitelistInternal() { + synchronized (this) { + int size = mPowerSaveWhitelistAppIds.size(); + int[] appids = new int[size]; + for (int i = 0; i < size; i++) { + appids[i] = mPowerSaveWhitelistAppIds.keyAt(i); + } + return appids; + } + } + + void updateDisplayLocked() { + mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); + // We consider any situation where the display is showing something to be it on, + // because if there is anything shown we are going to be updating it at some + // frequency so can't be allowed to go into deep sleeps. + boolean screenOn = mCurDisplay.getState() != Display.STATE_OFF;; + if (!screenOn && mScreenOn) { + mScreenOn = false; + becomeInactiveIfAppropriateLocked(); + } else if (screenOn) { + mScreenOn = true; + becomeActiveLocked("screen"); + } + } + + void updateChargingLocked(boolean charging) { + if (!charging && mCharging) { + mCharging = false; + becomeInactiveIfAppropriateLocked(); + } else if (charging) { + mCharging = charging; + becomeActiveLocked("charging"); + } + } + + void scheduleReportActiveLocked(boolean fromMotion) { + Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, fromMotion ? 1 : 0, + mState == STATE_IDLE ? 1 : 0); + mHandler.sendMessage(msg); + } + + void becomeActiveLocked(String reason) { + if (mState != STATE_ACTIVE) { + EventLogTags.writeDeviceIdle(STATE_ACTIVE, reason); + scheduleReportActiveLocked(false); + mState = STATE_ACTIVE; + mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT; + mNextIdlePendingDelay = 0; + mNextIdleDelay = 0; + cancelAlarmLocked(); + stopMonitoringSignificantMotion(); + } + } + + void becomeInactiveIfAppropriateLocked() { + if (!mScreenOn && !mCharging && 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; + mNextIdlePendingDelay = 0; + mNextIdleDelay = 0; + scheduleAlarmLocked(mInactiveTimeout, false); + EventLogTags.writeDeviceIdle(mState, "no activity"); + } + } + + void stepIdleStateLocked() { + EventLogTags.writeDeviceIdleStep(); + + final long now = SystemClock.elapsedRealtime(); + if ((now+DEFAULT_MIN_TIME_TO_ALARM) > 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<mPowerSaveWhitelistApps.size(); i++) { + mPowerSaveWhitelistAppIds.put(mPowerSaveWhitelistApps.valueAt(i), true); + } + for (int i=0; i<mPowerSaveWhitelistUserApps.size(); i++) { + mPowerSaveWhitelistAppIds.put(mPowerSaveWhitelistUserApps.valueAt(i), true); + } + } + + private void reportPowerSaveWhitelistChangedLocked() { + Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + getContext().sendBroadcast(intent); + } + + void readConfigFileLocked() { + Slog.d(TAG, "Reading config from " + mConfigFile.getBaseFile()); + mPowerSaveWhitelistUserApps.clear(); + FileInputStream stream; + try { + stream = mConfigFile.openRead(); + } catch (FileNotFoundException e) { + return; + } + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(stream, null); + readConfigFileLocked(parser); + } catch (XmlPullParserException e) { + } finally { + try { + stream.close(); + } catch (IOException e) { + } + } + + } + + private void readConfigFileLocked(XmlPullParser parser) { + final PackageManager pm = getContext().getPackageManager(); + + try { + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + throw new IllegalStateException("no start tag found"); + } + + int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > 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 <config>: " + + 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<mPowerSaveWhitelistUserApps.size(); i++) { + String name = mPowerSaveWhitelistUserApps.keyAt(i); + out.startTag(null, "wl"); + out.attribute(null, "n", name); + out.endTag(null, "wl"); + } + out.endTag(null, "config"); + out.endDocument(); + } + + 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."); + pw.println(" whitelist"); + pw.println(" Add (prefix with +) or remove (prefix with -) packages."); + } + + 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<args.length; i++) { + String arg = args[i]; + if ("-h".equals(arg)) { + dumpHelp(pw); + return; + } else if ("step".equals(arg)) { + synchronized (this) { + stepIdleStateLocked(); + pw.print("Stepped to: "); pw.println(stateToString(mState)); + } + return; + } else if ("whitelist".equals(arg)) { + i++; + while (i < args.length) { + arg = args[i]; + i++; + if (arg.length() < 1 || (arg.charAt(0) != '-' + && arg.charAt(0) != '+')) { + pw.println("Package must be prefixed with + or -: " + arg); + return; + } + char op = arg.charAt(0); + String pkg = arg.substring(1); + if (op == '+') { + if (addPowerSaveWhitelistAppInternal(pkg)) { + pw.println("Added: " + pkg); + } else { + pw.println("Unknown package: " + pkg); + } + } else { + if (removePowerSaveWhitelistAppInternal(pkg)) { + pw.println("Removed: " + pkg); + } + } + } + return; + } else if (arg.length() > 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 818f0aa..e45092c 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<String> allowPower = sysConfig.getAllowInPowerSave(); - for (int i=0; i<allowPower.size(); i++) { - String pkg = allowPower.valueAt(i); - try { - ApplicationInfo ai = pm.getApplicationInfo(pkg, 0); - if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { - mPowerSaveWhitelistAppIds.put(UserHandle.getAppId(ai.uid), true); - } - } catch (PackageManager.NameNotFoundException e) { - } - } - + updatePowerSaveWhitelistLocked(); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mPowerManagerInternal.registerLowPowerModeObserver( new PowerManagerInternal.LowPowerModeListener() { @@ -406,6 +412,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { screenFilter.addAction(Intent.ACTION_SCREEN_OFF); mContext.registerReceiver(mScreenReceiver, screenFilter); + // listen for changes to power save whitelist + final IntentFilter whitelistFilter = new IntentFilter( + PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); + mContext.registerReceiver(mPowerSaveWhitelistReceiver, whitelistFilter, null, mHandler); + // watch for network interfaces to be claimed final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION); mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler); @@ -489,6 +500,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } }; + private BroadcastReceiver mPowerSaveWhitelistReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // on background handler thread, and POWER_SAVE_WHITELIST_CHANGED is protected + synchronized (mRulesLock) { + updatePowerSaveWhitelistLocked(); + updateRulesForGlobalChangeLocked(false); + } + } + }; + private BroadcastReceiver mScreenReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -1528,20 +1550,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return uids; } - @Override - public int[] getPowerSaveAppIdWhitelist() { - mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); - - synchronized (mRulesLock) { - int size = mPowerSaveWhitelistAppIds.size(); - int[] appids = new int[size]; - for (int i = 0; i < size; i++) { - appids[i] = mPowerSaveWhitelistAppIds.keyAt(i); - } - return appids; - } - } - /** * Remove any policies associated with given {@link UserHandle}, persisting * if any changes are made. diff --git a/services/core/java/com/android/server/power/DeviceIdleController.java b/services/core/java/com/android/server/power/DeviceIdleController.java deleted file mode 100644 index 6b29b9a..0000000 --- a/services/core/java/com/android/server/power/DeviceIdleController.java +++ /dev/null @@ -1,489 +0,0 @@ -/* - * 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.power; - -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.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.PowerManager; -import android.os.PowerManagerInternal; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.os.UserHandle; -import android.util.TimeUtils; -import android.view.Display; -import com.android.internal.app.IBatteryStats; -import com.android.server.SystemService; -import com.android.server.am.BatteryStatsService; -import com.android.server.EventLogTags; - -import java.io.FileDescriptor; -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"; - - 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; - 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"; - default: return Integer.toString(state); - } - } - - private int mState; - - private long mInactiveTimeout; - private long mNextAlarmTime; - private long mNextIdlePendingDelay; - private long mNextIdleDelay; - - private final Binder mBinder = new Binder() { - @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - DeviceIdleController.this.dump(fd, pw, args); - } - }; - - 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(); - } - } - }; - - public DeviceIdleController(Context context) { - super(context); - } - - @Override - public void onStart() { - synchronized (this) { - mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); - mBatteryStats = BatteryStatsService.getService(); - mLocalPowerManager = getLocalService(PowerManagerInternal.class); - mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); - mDisplayManager = (DisplayManager) getContext().getSystemService( - Context.DISPLAY_SERVICE); - mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE); - mSigMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION); - - Intent intent = new Intent(ACTION_STEP_IDLE_STATE) - .setPackage("android") - .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0); - - mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); - mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BATTERY_CHANGED); - filter.addAction(ACTION_STEP_IDLE_STATE); - getContext().registerReceiver(mReceiver, filter); - - mDisplayManager.registerDisplayListener(mDisplayListener, null); - - mScreenOn = true; - // Start out assuming we are charging. If we aren't, we will at least get - // a battery update the next time the level drops. - mCharging = true; - mState = STATE_ACTIVE; - mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT; - updateDisplayLocked(); - } - - publishBinderService("deviceidle", mBinder); - } - - void updateDisplayLocked() { - mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); - // We consider any situation where the display is showing something to be it on, - // because if there is anything shown we are going to be updating it at some - // frequency so can't be allowed to go into deep sleeps. - boolean screenOn = mCurDisplay.getState() != Display.STATE_OFF;; - if (!screenOn && mScreenOn) { - mScreenOn = false; - becomeInactiveIfAppropriateLocked(); - } else if (screenOn) { - mScreenOn = true; - becomeActiveLocked("screen"); - } - } - - void updateChargingLocked(boolean charging) { - if (!charging && mCharging) { - mCharging = false; - becomeInactiveIfAppropriateLocked(); - } else if (charging) { - mCharging = charging; - becomeActiveLocked("charging"); - } - } - - void becomeActiveLocked(String reason) { - if (mState != STATE_ACTIVE) { - EventLogTags.writeDeviceIdle(STATE_ACTIVE, reason); - mLocalPowerManager.setDeviceIdleMode(false); - try { - mNetworkPolicyManager.setDeviceIdleMode(false); - mBatteryStats.noteDeviceIdleMode(false, true, false); - } catch (RemoteException e) { - } - if (mState == STATE_IDLE) { - getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); - } - mState = STATE_ACTIVE; - mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT; - mNextIdlePendingDelay = 0; - mNextIdleDelay = 0; - cancelAlarmLocked(); - stopMonitoringSignificantMotion(); - } - } - - void becomeInactiveIfAppropriateLocked() { - if (!mScreenOn && !mCharging && 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; - mNextIdlePendingDelay = 0; - mNextIdleDelay = 0; - scheduleAlarmLocked(mInactiveTimeout, false); - EventLogTags.writeDeviceIdle(mState, "no activity"); - } - } - - void stepIdleStateLocked() { - EventLogTags.writeDeviceIdleStep(); - - final long now = SystemClock.elapsedRealtime(); - if ((now+DEFAULT_MIN_TIME_TO_ALARM) > 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<args.length; i++) { - String arg = args[i]; - if ("-h".equals(arg)) { - dumpHelp(pw); - return; - } else if ("step".equals(arg)) { - synchronized (this) { - stepIdleStateLocked(); - pw.print("Stepped to: "); pw.println(stateToString(mState)); - } - return; - } else if (arg.length() > 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 593853c..2922130 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 |