diff options
| author | 2016-04-07 15:18:58 -0700 | |
|---|---|---|
| committer | 2016-04-07 16:42:41 -0700 | |
| commit | 88c4135d88eb59320fe93801088bcd6c47e50efb (patch) | |
| tree | 70efa7bafa417d037dfe7623e366f2eb0efb85da | |
| parent | d30606575783acd8689cfac604cba51e537b6b77 (diff) | |
Fix issue #26879170: Adjust doze maintenance windows...
...based on network availability
There is a new light maintenance state "waiting for network"
that we go in to after idle if the network is not currently
available. We will stay in this state the same duration as idle,
so if we are continually without network access this effectively
doubles the light doze idle durations.
Get rid of some wrongly copy/pasted code to allow doze light
to still work even if we don't have an SMD. It doesn't need
one.
Also a bunch of improvements to the shell commands to make it
easier to test / debug.
Change-Id: Iad024840661479dbfd54b5b3db6ab96fefe59bc0
| -rw-r--r-- | services/core/java/com/android/server/DeviceIdleController.java | 244 | ||||
| -rw-r--r-- | services/core/java/com/android/server/job/controllers/ConnectivityController.java | 4 |
2 files changed, 193 insertions, 55 deletions
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index ccb4647d8591..6a08191eef47 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -39,7 +39,9 @@ import android.location.LocationRequest; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; +import android.net.ConnectivityManager; import android.net.INetworkPolicyManager; +import android.net.NetworkInfo; import android.net.Uri; import android.os.BatteryStats; import android.os.Binder; @@ -114,6 +116,7 @@ public class DeviceIdleController extends SystemService private IBatteryStats mBatteryStats; private PowerManagerInternal mLocalPowerManager; private PowerManager mPowerManager; + private ConnectivityService mConnectivityService; private AlarmManagerService.LocalService mLocalAlarmManager; private INetworkPolicyManager mNetworkPolicyManager; private DisplayManager mDisplayManager; @@ -128,6 +131,7 @@ public class DeviceIdleController extends SystemService private boolean mLightEnabled; private boolean mDeepEnabled; private boolean mForceIdle; + private boolean mNetworkConnected; private boolean mScreenOn; private boolean mCharging; private boolean mNotMoving; @@ -173,16 +177,20 @@ public class DeviceIdleController extends SystemService private static final int LIGHT_STATE_PRE_IDLE = 3; /** Device is in the light idle state, trying to stay asleep as much as possible. */ private static final int LIGHT_STATE_IDLE = 4; + /** Device is in the light idle state, we want to go in to idle maintenance but are + * waiting for network connectivity before doing so. */ + private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5; /** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */ - private static final int LIGHT_STATE_IDLE_MAINTENANCE = 5; + private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6; /** Device light idle state is overriden, now applying deep doze state. */ - private static final int LIGHT_STATE_OVERRIDE = 6; + private static final int LIGHT_STATE_OVERRIDE = 7; private static String lightStateToString(int state) { switch (state) { case LIGHT_STATE_ACTIVE: return "ACTIVE"; case LIGHT_STATE_INACTIVE: return "INACTIVE"; case LIGHT_STATE_PRE_IDLE: return "PRE_IDLE"; case LIGHT_STATE_IDLE: return "IDLE"; + case LIGHT_STATE_WAITING_FOR_NETWORK: return "WAITING_FOR_NETWORK"; case LIGHT_STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE"; case LIGHT_STATE_OVERRIDE: return "OVERRIDE"; default: return Integer.toString(state); @@ -315,17 +323,27 @@ public class DeviceIdleController extends SystemService 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 (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { - if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { - Uri data = intent.getData(); - String ssp; - if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { - removePowerSaveWhitelistAppInternal(ssp); + switch (intent.getAction()) { + case ConnectivityManager.CONNECTIVITY_ACTION: { + synchronized (DeviceIdleController.this) { + updateConnectivityStateLocked(intent); } - } + } break; + case Intent.ACTION_BATTERY_CHANGED: { + synchronized (DeviceIdleController.this) { + int plugged = intent.getIntExtra("plugged", 0); + updateChargingLocked(plugged != 0); + } + } break; + case Intent.ACTION_PACKAGE_REMOVED: { + if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + Uri data = intent.getData(); + String ssp; + if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { + removePowerSaveWhitelistAppInternal(ssp); + } + } + } break; } } }; @@ -1318,6 +1336,7 @@ public class DeviceIdleController extends SystemService readConfigFileLocked(); updateWhitelistAppIdsLocked(); + mNetworkConnected = true; 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. @@ -1343,6 +1362,8 @@ public class DeviceIdleController extends SystemService mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "deviceidle_maint"); mActiveIdleWakeLock.setReferenceCounted(false); + mConnectivityService = (ConnectivityService)ServiceManager.getService( + Context.CONNECTIVITY_SERVICE); mLocalAlarmManager = getLocalService(AlarmManagerService.LocalService.class); mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface( ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); @@ -1395,11 +1416,14 @@ public class DeviceIdleController extends SystemService filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); + filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); getContext().registerReceiver(mReceiver, filter); mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray); mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray); mDisplayManager.registerDisplayListener(mDisplayListener, null); + updateConnectivityStateLocked(null); updateDisplayLocked(); } } @@ -1680,6 +1704,35 @@ public class DeviceIdleController extends SystemService } } + void updateConnectivityStateLocked(Intent connIntent) { + if (mConnectivityService != null) { + NetworkInfo ni = mConnectivityService.getActiveNetworkInfo(); + boolean conn; + if (ni == null) { + conn = false; + } else { + if (connIntent == null) { + conn = ni.isConnected(); + } else { + final int networkType = + connIntent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, + ConnectivityManager.TYPE_NONE); + if (ni.getType() != networkType) { + return; + } + conn = !connIntent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, + false); + } + } + if (conn != mNetworkConnected) { + mNetworkConnected = conn; + if (conn && mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) { + stepLightIdleStateLocked("network"); + } + } + } + } + void updateDisplayLocked() { mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); // We consider any situation where the display is showing something to be it on, @@ -1778,7 +1831,7 @@ public class DeviceIdleController extends SystemService if (mForceIdle) { mForceIdle = false; if (mScreenOn || mCharging) { - becomeActiveLocked("exit-force-idle", Process.myUid()); + becomeActiveLocked("exit-force", Process.myUid()); } } } @@ -1834,22 +1887,33 @@ public class DeviceIdleController extends SystemService mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT); break; case LIGHT_STATE_IDLE: - // We have been idling long enough, now it is time to do some work. - mActiveIdleOpCount = 1; - mActiveIdleWakeLock.acquire(); - mMaintenanceStartTime = SystemClock.elapsedRealtime(); - if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) { - mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET; - } else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) { - mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET; + case LIGHT_STATE_WAITING_FOR_NETWORK: + if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) { + // We have been idling long enough, now it is time to do some work. + mActiveIdleOpCount = 1; + mActiveIdleWakeLock.acquire(); + mMaintenanceStartTime = SystemClock.elapsedRealtime(); + if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) { + mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET; + } else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) { + mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET; + } + scheduleLightAlarmLocked(mCurIdleBudget); + if (DEBUG) Slog.d(TAG, + "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE."); + mLightState = LIGHT_STATE_IDLE_MAINTENANCE; + EventLogTags.writeDeviceIdleLight(mLightState, reason); + addEvent(EVENT_LIGHT_MAINTENANCE); + mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF); + } else { + // We'd like to do maintenance, but currently don't have network + // connectivity... let's try to wait until the network comes back. + // We'll only wait for another full idle period, however, and then give up. + scheduleLightAlarmLocked(mNextLightIdleDelay); + if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK."); + mLightState = LIGHT_STATE_WAITING_FOR_NETWORK; + EventLogTags.writeDeviceIdleLight(mLightState, reason); } - scheduleLightAlarmLocked(mCurIdleBudget); - if (DEBUG) Slog.d(TAG, - "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE."); - mLightState = LIGHT_STATE_IDLE_MAINTENANCE; - EventLogTags.writeDeviceIdleLight(mLightState, reason); - addEvent(EVENT_LIGHT_MAINTENANCE); - mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF); break; } } @@ -2209,13 +2273,6 @@ public class DeviceIdleController extends SystemService void scheduleLightAlarmLocked(long delay) { if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")"); - if (mMotionSensor == null) { - // If there is no 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 execution of device idling, although it is still possible to - // manually poke it by pretending like the alarm is going off. - return; - } mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay; mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler); @@ -2430,9 +2487,14 @@ public class DeviceIdleController extends SystemService pw.println(" Print this help text."); pw.println(" step [light|deep]"); pw.println(" Immediately step to next state, without waiting for alarm."); - pw.println(" force-idle"); + pw.println(" force-idle [light|deep]"); pw.println(" Force directly into idle mode, regardless of other device state."); - pw.println(" Use \"step\" to get out."); + pw.println(" force-inactive"); + pw.println(" Force to be inactive, ready to freely step idle states."); + pw.println(" unforce"); + pw.println(" Resume normal functioning after force-idle or force-inactive."); + pw.println(" get [light|deep|force|screen|charging|network]"); + pw.println(" Retrieve the current given state."); pw.println(" disable [light|deep|all]"); pw.println(" Completely disable device idle mode."); pw.println(" enable [light|deep|all]"); @@ -2472,12 +2534,10 @@ public class DeviceIdleController extends SystemService String arg = shell.getNextArg(); try { if (arg == null || "deep".equals(arg)) { - exitForceIdleLocked(); stepIdleStateLocked("s:shell"); pw.print("Stepped to deep: "); pw.println(stateToString(mState)); } else if ("light".equals(arg)) { - exitForceIdleLocked(); stepLightIdleStateLocked("s:shell"); pw.print("Stepped to light: "); pw.println(lightStateToString(mLightState)); } else { @@ -2492,29 +2552,104 @@ public class DeviceIdleController extends SystemService null); synchronized (this) { long token = Binder.clearCallingIdentity(); + String arg = shell.getNextArg(); try { - if (!mDeepEnabled) { - pw.println("Unable to go idle; not enabled"); - return -1; - } - mForceIdle = true; - becomeInactiveIfAppropriateLocked(); - int curState = mState; - while (curState != STATE_IDLE) { - stepIdleStateLocked("s:shell"); - if (curState == mState) { - pw.print("Unable to go idle; stopped at "); - pw.println(stateToString(mState)); - exitForceIdleLocked(); + if (arg == null || "deep".equals(arg)) { + if (!mDeepEnabled) { + pw.println("Unable to go deep idle; not enabled"); return -1; } - curState = mState; + mForceIdle = true; + becomeInactiveIfAppropriateLocked(); + int curState = mState; + while (curState != STATE_IDLE) { + stepIdleStateLocked("s:shell"); + if (curState == mState) { + pw.print("Unable to go deep idle; stopped at "); + pw.println(stateToString(mState)); + exitForceIdleLocked(); + return -1; + } + curState = mState; + } + pw.println("Now forced in to deep idle mode"); + } else if ("light".equals(arg)) { + mForceIdle = true; + becomeInactiveIfAppropriateLocked(); + int curLightState = mLightState; + while (curLightState != LIGHT_STATE_IDLE) { + stepIdleStateLocked("s:shell"); + if (curLightState == mLightState) { + pw.print("Unable to go light idle; stopped at "); + pw.println(lightStateToString(mLightState)); + exitForceIdleLocked(); + return -1; + } + curLightState = mLightState; + } + pw.println("Now forced in to light idle mode"); + } else { + pw.println("Unknown idle mode: " + arg); } - pw.println("Now forced in to idle mode"); } finally { Binder.restoreCallingIdentity(token); } } + } else if ("force-inactive".equals(cmd)) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, + null); + synchronized (this) { + long token = Binder.clearCallingIdentity(); + try { + mForceIdle = true; + becomeInactiveIfAppropriateLocked(); + pw.print("Light state: "); + pw.print(lightStateToString(mLightState)); + pw.print(", deep state: "); + pw.println(stateToString(mState)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } else if ("unforce".equals(cmd)) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, + null); + synchronized (this) { + long token = Binder.clearCallingIdentity(); + try { + exitForceIdleLocked(); + pw.print("Light state: "); + pw.print(lightStateToString(mLightState)); + pw.print(", deep state: "); + pw.println(stateToString(mState)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } else if ("get".equals(cmd)) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, + null); + synchronized (this) { + String arg = shell.getNextArg(); + if (arg != null) { + long token = Binder.clearCallingIdentity(); + try { + switch (arg) { + case "light": pw.println(lightStateToString(mLightState)); break; + case "deep": pw.println(stateToString(mState)); break; + case "force": pw.println(mForceIdle); break; + case "screen": pw.println(mScreenOn); break; + case "charging": pw.println(mCharging); break; + case "network": pw.println(mNetworkConnected); break; + default: pw.println("Unknown get option: " + arg); break; + } + } finally { + Binder.restoreCallingIdentity(token); + } + } else { + pw.println("Argument required"); + } + } } else if ("disable".equals(cmd)) { getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); @@ -2829,6 +2964,7 @@ public class DeviceIdleController extends SystemService pw.print(" mMotionSensor="); pw.println(mMotionSensor); pw.print(" mCurDisplay="); pw.println(mCurDisplay); pw.print(" mScreenOn="); pw.println(mScreenOn); + pw.print(" mNetworkConnected="); pw.println(mNetworkConnected); pw.print(" mCharging="); pw.println(mCharging); pw.print(" mMotionActive="); pw.println(mMotionListener.active); pw.print(" mNotMoving="); pw.println(mNotMoving); diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index 5ad8189da054..be9d80012ccf 100644 --- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -77,8 +77,10 @@ public class ConnectivityController extends StateController implements if (cs != null) { if (cs.getActiveNetworkInfo() != null) { mNetworkConnected = cs.getActiveNetworkInfo().isConnected(); + mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered(); + } else { + mNetworkConnected = mNetworkUnmetered = false; } - mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered(); } } |