1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.power.batterysaver; 17 18 import android.Manifest; 19 import android.app.ActivityManagerInternal; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.hardware.power.V1_0.PowerHint; 25 import android.os.BatteryManager; 26 import android.os.BatterySaverPolicyConfig; 27 import android.os.Handler; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.PowerManager; 31 import android.os.PowerManagerInternal; 32 import android.os.PowerManagerInternal.LowPowerModeListener; 33 import android.os.PowerSaveState; 34 import android.os.UserHandle; 35 import android.util.ArrayMap; 36 import android.util.Slog; 37 38 import com.android.internal.annotations.GuardedBy; 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.util.ArrayUtils; 41 import com.android.internal.util.Preconditions; 42 import com.android.server.EventLogTags; 43 import com.android.server.LocalServices; 44 import com.android.server.power.PowerManagerService; 45 import com.android.server.power.batterysaver.BatterySaverPolicy.BatterySaverPolicyListener; 46 import com.android.server.power.batterysaver.BatterySaverPolicy.Policy; 47 import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState; 48 import com.android.server.power.batterysaver.BatterySavingStats.DozeState; 49 import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState; 50 51 import java.util.ArrayList; 52 53 /** 54 * Responsible for battery saver mode transition logic. 55 * 56 * IMPORTANT: This class shares the power manager lock, which is very low in the lock hierarchy. 57 * Do not call out with the lock held. (Settings provider is okay.) 58 */ 59 public class BatterySaverController implements BatterySaverPolicyListener { 60 static final String TAG = "BatterySaverController"; 61 62 static final boolean DEBUG = BatterySaverPolicy.DEBUG; 63 64 private final Object mLock; 65 private final Context mContext; 66 private final MyHandler mHandler; 67 private final FileUpdater mFileUpdater; 68 69 private PowerManager mPowerManager; 70 71 private final BatterySaverPolicy mBatterySaverPolicy; 72 73 private final BatterySavingStats mBatterySavingStats; 74 75 @GuardedBy("mLock") 76 private final ArrayList<LowPowerModeListener> mListeners = new ArrayList<>(); 77 78 @GuardedBy("mLock") 79 private boolean mFullEnabled; 80 81 @GuardedBy("mLock") 82 private boolean mAdaptiveEnabled; 83 84 @GuardedBy("mLock") 85 private boolean mIsPluggedIn; 86 87 /** 88 * Whether full was previously enabled or not; only for the event logging. Only use it from 89 * {@link #handleBatterySaverStateChanged}. 90 */ 91 private boolean mFullPreviouslyEnabled; 92 93 /** 94 * Whether adaptive was previously enabled or not; only for the event logging. Only use it from 95 * {@link #handleBatterySaverStateChanged}. 96 */ 97 private boolean mAdaptivePreviouslyEnabled; 98 99 @GuardedBy("mLock") 100 private boolean mIsInteractive; 101 102 /** 103 * Read-only list of plugins. No need for synchronization. 104 */ 105 private final Plugin[] mPlugins; 106 107 public static final int REASON_PERCENTAGE_AUTOMATIC_ON = 0; 108 public static final int REASON_PERCENTAGE_AUTOMATIC_OFF = 1; 109 public static final int REASON_MANUAL_ON = 2; 110 public static final int REASON_MANUAL_OFF = 3; 111 public static final int REASON_STICKY_RESTORE = 4; 112 public static final int REASON_INTERACTIVE_CHANGED = 5; 113 public static final int REASON_POLICY_CHANGED = 6; 114 public static final int REASON_PLUGGED_IN = 7; 115 public static final int REASON_SETTING_CHANGED = 8; 116 public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON = 9; 117 public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF = 10; 118 public static final int REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED = 11; 119 public static final int REASON_TIMEOUT = 12; 120 reasonToString(int reason)121 static String reasonToString(int reason) { 122 switch (reason) { 123 case BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON: 124 return "Percentage Auto ON"; 125 case BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF: 126 return "Percentage Auto OFF"; 127 case BatterySaverController.REASON_MANUAL_ON: 128 return "Manual ON"; 129 case BatterySaverController.REASON_MANUAL_OFF: 130 return "Manual OFF"; 131 case BatterySaverController.REASON_STICKY_RESTORE: 132 return "Sticky restore"; 133 case BatterySaverController.REASON_INTERACTIVE_CHANGED: 134 return "Interactivity changed"; 135 case BatterySaverController.REASON_POLICY_CHANGED: 136 return "Policy changed"; 137 case BatterySaverController.REASON_PLUGGED_IN: 138 return "Plugged in"; 139 case BatterySaverController.REASON_SETTING_CHANGED: 140 return "Setting changed"; 141 case BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON: 142 return "Dynamic Warning Auto ON"; 143 case BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF: 144 return "Dynamic Warning Auto OFF"; 145 case BatterySaverController.REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED: 146 return "Adaptive Power Savings changed"; 147 case BatterySaverController.REASON_TIMEOUT: 148 return "timeout"; 149 default: 150 return "Unknown reason: " + reason; 151 } 152 } 153 154 /** 155 * Plugin interface. All methods are guaranteed to be called on the same (handler) thread. 156 */ 157 public interface Plugin { onSystemReady(BatterySaverController caller)158 void onSystemReady(BatterySaverController caller); 159 onBatterySaverChanged(BatterySaverController caller)160 void onBatterySaverChanged(BatterySaverController caller); 161 } 162 163 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 164 @Override 165 public void onReceive(Context context, Intent intent) { 166 if (DEBUG) { 167 Slog.d(TAG, "onReceive: " + intent); 168 } 169 switch (intent.getAction()) { 170 case Intent.ACTION_SCREEN_ON: 171 case Intent.ACTION_SCREEN_OFF: 172 if (!isPolicyEnabled()) { 173 updateBatterySavingStats(); 174 return; // No need to send it if not enabled. 175 } 176 // Don't send the broadcast, because we never did so in this case. 177 mHandler.postStateChanged(/*sendBroadcast=*/ false, 178 REASON_INTERACTIVE_CHANGED); 179 break; 180 case Intent.ACTION_BATTERY_CHANGED: 181 synchronized (mLock) { 182 mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0); 183 } 184 // Fall-through. 185 case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED: 186 case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED: 187 updateBatterySavingStats(); 188 break; 189 } 190 } 191 }; 192 193 /** 194 * Constructor. 195 */ BatterySaverController(Object lock, Context context, Looper looper, BatterySaverPolicy policy, BatterySavingStats batterySavingStats)196 public BatterySaverController(Object lock, Context context, Looper looper, 197 BatterySaverPolicy policy, BatterySavingStats batterySavingStats) { 198 mLock = lock; 199 mContext = context; 200 mHandler = new MyHandler(looper); 201 mBatterySaverPolicy = policy; 202 mBatterySaverPolicy.addListener(this); 203 mFileUpdater = new FileUpdater(context); 204 mBatterySavingStats = batterySavingStats; 205 206 // Initialize plugins. 207 mPlugins = new Plugin[] { 208 new BatterySaverLocationPlugin(mContext) 209 }; 210 } 211 212 /** 213 * Add a listener. 214 */ addListener(LowPowerModeListener listener)215 public void addListener(LowPowerModeListener listener) { 216 synchronized (mLock) { 217 mListeners.add(listener); 218 } 219 } 220 221 /** 222 * Called by {@link PowerManagerService} on system ready, *with no lock held*. 223 */ systemReady()224 public void systemReady() { 225 final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); 226 filter.addAction(Intent.ACTION_SCREEN_OFF); 227 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 228 filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); 229 filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); 230 mContext.registerReceiver(mReceiver, filter); 231 232 mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class) 233 .isRuntimeRestarted()); 234 mHandler.postSystemReady(); 235 } 236 getPowerManager()237 private PowerManager getPowerManager() { 238 if (mPowerManager == null) { 239 mPowerManager = 240 Preconditions.checkNotNull(mContext.getSystemService(PowerManager.class)); 241 } 242 return mPowerManager; 243 } 244 245 @Override onBatterySaverPolicyChanged(BatterySaverPolicy policy)246 public void onBatterySaverPolicyChanged(BatterySaverPolicy policy) { 247 if (!isPolicyEnabled()) { 248 return; // No need to send it if not enabled. 249 } 250 mHandler.postStateChanged(/*sendBroadcast=*/ true, REASON_POLICY_CHANGED); 251 } 252 253 private class MyHandler extends Handler { 254 private static final int MSG_STATE_CHANGED = 1; 255 256 private static final int ARG_DONT_SEND_BROADCAST = 0; 257 private static final int ARG_SEND_BROADCAST = 1; 258 259 private static final int MSG_SYSTEM_READY = 2; 260 MyHandler(Looper looper)261 public MyHandler(Looper looper) { 262 super(looper); 263 } 264 postStateChanged(boolean sendBroadcast, int reason)265 void postStateChanged(boolean sendBroadcast, int reason) { 266 obtainMessage(MSG_STATE_CHANGED, sendBroadcast ? 267 ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, reason).sendToTarget(); 268 } 269 postSystemReady()270 public void postSystemReady() { 271 obtainMessage(MSG_SYSTEM_READY, 0, 0).sendToTarget(); 272 } 273 274 @Override dispatchMessage(Message msg)275 public void dispatchMessage(Message msg) { 276 switch (msg.what) { 277 case MSG_STATE_CHANGED: 278 handleBatterySaverStateChanged( 279 msg.arg1 == ARG_SEND_BROADCAST, 280 msg.arg2); 281 break; 282 283 case MSG_SYSTEM_READY: 284 for (Plugin p : mPlugins) { 285 p.onSystemReady(BatterySaverController.this); 286 } 287 break; 288 } 289 } 290 } 291 292 /** Enable or disable full battery saver. */ 293 @VisibleForTesting enableBatterySaver(boolean enable, int reason)294 public void enableBatterySaver(boolean enable, int reason) { 295 synchronized (mLock) { 296 if (mFullEnabled == enable) { 297 return; 298 } 299 mFullEnabled = enable; 300 301 if (updatePolicyLevelLocked()) { 302 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); 303 } 304 } 305 } 306 updatePolicyLevelLocked()307 private boolean updatePolicyLevelLocked() { 308 if (mFullEnabled) { 309 return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_FULL); 310 } else if (mAdaptiveEnabled) { 311 return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_ADAPTIVE); 312 } else { 313 return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_OFF); 314 } 315 } 316 317 /** 318 * @return whether battery saver is enabled or not. This takes into 319 * account whether a policy says to advertise isEnabled so this can be propagated externally. 320 */ isEnabled()321 public boolean isEnabled() { 322 synchronized (mLock) { 323 return mFullEnabled 324 || (mAdaptiveEnabled && mBatterySaverPolicy.shouldAdvertiseIsEnabled()); 325 } 326 } 327 328 /** 329 * @return whether battery saver policy is enabled or not. This does not take into account 330 * whether a policy says to advertise isEnabled, so this shouldn't be propagated externally. 331 */ isPolicyEnabled()332 private boolean isPolicyEnabled() { 333 synchronized (mLock) { 334 return mFullEnabled || mAdaptiveEnabled; 335 } 336 } 337 isFullEnabled()338 boolean isFullEnabled() { 339 synchronized (mLock) { 340 return mFullEnabled; 341 } 342 } 343 isAdaptiveEnabled()344 boolean isAdaptiveEnabled() { 345 synchronized (mLock) { 346 return mAdaptiveEnabled; 347 } 348 } 349 setAdaptivePolicyLocked(String settings, String deviceSpecificSettings, int reason)350 boolean setAdaptivePolicyLocked(String settings, String deviceSpecificSettings, int reason) { 351 return setAdaptivePolicyLocked( 352 BatterySaverPolicy.Policy.fromSettings(settings, deviceSpecificSettings), 353 reason); 354 } 355 setAdaptivePolicyLocked(BatterySaverPolicyConfig config, int reason)356 boolean setAdaptivePolicyLocked(BatterySaverPolicyConfig config, int reason) { 357 return setAdaptivePolicyLocked(BatterySaverPolicy.Policy.fromConfig(config), reason); 358 } 359 setAdaptivePolicyLocked(Policy policy, int reason)360 boolean setAdaptivePolicyLocked(Policy policy, int reason) { 361 if (mBatterySaverPolicy.setAdaptivePolicyLocked(policy)) { 362 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); 363 return true; 364 } 365 return false; 366 } 367 resetAdaptivePolicyLocked(int reason)368 boolean resetAdaptivePolicyLocked(int reason) { 369 if (mBatterySaverPolicy.resetAdaptivePolicyLocked()) { 370 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); 371 return true; 372 } 373 return false; 374 } 375 setAdaptivePolicyEnabledLocked(boolean enabled, int reason)376 boolean setAdaptivePolicyEnabledLocked(boolean enabled, int reason) { 377 if (mAdaptiveEnabled == enabled) { 378 return false; 379 } 380 mAdaptiveEnabled = enabled; 381 if (updatePolicyLevelLocked()) { 382 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); 383 return true; 384 } 385 return false; 386 } 387 388 /** @return whether device is in interactive state. */ isInteractive()389 public boolean isInteractive() { 390 synchronized (mLock) { 391 return mIsInteractive; 392 } 393 } 394 395 /** @return Battery saver policy. */ getBatterySaverPolicy()396 public BatterySaverPolicy getBatterySaverPolicy() { 397 return mBatterySaverPolicy; 398 } 399 400 /** 401 * @return true if launch boost should currently be disabled. 402 */ isLaunchBoostDisabled()403 public boolean isLaunchBoostDisabled() { 404 return isPolicyEnabled() && mBatterySaverPolicy.isLaunchBoostDisabled(); 405 } 406 407 /** 408 * Dispatch power save events to the listeners. 409 * 410 * This method is always called on the handler thread. 411 * 412 * This method is called only in the following cases: 413 * - When battery saver becomes activated. 414 * - When battery saver becomes deactivated. 415 * - When battery saver is on and the interactive state changes. 416 * - When battery saver is on and the battery saver policy changes. 417 * - When adaptive battery saver becomes activated. 418 * - When adaptive battery saver becomes deactivated. 419 * - When adaptive battery saver is active (and full is off) and the policy changes. 420 */ handleBatterySaverStateChanged(boolean sendBroadcast, int reason)421 void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) { 422 final LowPowerModeListener[] listeners; 423 424 final boolean enabled; 425 final boolean isInteractive = getPowerManager().isInteractive(); 426 final ArrayMap<String, String> fileValues; 427 428 synchronized (mLock) { 429 enabled = mFullEnabled || mAdaptiveEnabled; 430 431 EventLogTags.writeBatterySaverMode( 432 mFullPreviouslyEnabled ? 1 : 0, // Previously off or on. 433 mAdaptivePreviouslyEnabled ? 1 : 0, // Previously off or on. 434 mFullEnabled ? 1 : 0, // Now off or on. 435 mAdaptiveEnabled ? 1 : 0, // Now off or on. 436 isInteractive ? 1 : 0, // Device interactive state. 437 enabled ? mBatterySaverPolicy.toEventLogString() : "", 438 reason); 439 440 mFullPreviouslyEnabled = mFullEnabled; 441 mAdaptivePreviouslyEnabled = mAdaptiveEnabled; 442 443 listeners = mListeners.toArray(new LowPowerModeListener[0]); 444 445 mIsInteractive = isInteractive; 446 447 if (enabled) { 448 fileValues = mBatterySaverPolicy.getFileValues(isInteractive); 449 } else { 450 fileValues = null; 451 } 452 } 453 454 final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class); 455 if (pmi != null) { 456 pmi.powerHint(PowerHint.LOW_POWER, isEnabled() ? 1 : 0); 457 } 458 459 updateBatterySavingStats(); 460 461 if (ArrayUtils.isEmpty(fileValues)) { 462 mFileUpdater.restoreDefault(); 463 } else { 464 mFileUpdater.writeFiles(fileValues); 465 } 466 467 for (Plugin p : mPlugins) { 468 p.onBatterySaverChanged(this); 469 } 470 471 if (sendBroadcast) { 472 473 if (DEBUG) { 474 Slog.i(TAG, "Sending broadcasts for mode: " + isEnabled()); 475 } 476 477 // Send the broadcasts and notify the listeners. We only do this when the battery saver 478 // mode changes, but not when only the screen state changes. 479 Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING) 480 .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, isEnabled()) 481 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 482 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 483 484 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); 485 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 486 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 487 488 // Send internal version that requires signature permission. 489 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL); 490 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 491 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 492 Manifest.permission.DEVICE_POWER); 493 494 for (LowPowerModeListener listener : listeners) { 495 final PowerSaveState result = 496 mBatterySaverPolicy.getBatterySaverPolicy(listener.getServiceType()); 497 listener.onLowPowerModeChanged(result); 498 } 499 } 500 } 501 updateBatterySavingStats()502 private void updateBatterySavingStats() { 503 final PowerManager pm = getPowerManager(); 504 if (pm == null) { 505 Slog.wtf(TAG, "PowerManager not initialized"); 506 return; 507 } 508 final boolean isInteractive = pm.isInteractive(); 509 final int dozeMode = 510 pm.isDeviceIdleMode() ? DozeState.DEEP 511 : pm.isLightDeviceIdleMode() ? DozeState.LIGHT 512 : DozeState.NOT_DOZING; 513 514 synchronized (mLock) { 515 if (mIsPluggedIn) { 516 mBatterySavingStats.startCharging(); 517 return; 518 } 519 mBatterySavingStats.transitionState( 520 mFullEnabled ? BatterySaverState.ON : 521 (mAdaptiveEnabled ? BatterySaverState.ADAPTIVE : BatterySaverState.OFF), 522 isInteractive ? InteractiveState.INTERACTIVE : InteractiveState.NON_INTERACTIVE, 523 dozeMode); 524 } 525 } 526 } 527