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.annotation.Nullable; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.pm.PackageManagerInternal; 25 import android.hardware.power.Mode; 26 import android.os.BatteryManager; 27 import android.os.BatterySaverPolicyConfig; 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.PowerManager; 32 import android.os.PowerManagerInternal; 33 import android.os.PowerManagerInternal.LowPowerModeListener; 34 import android.os.PowerSaveState; 35 import android.os.UserHandle; 36 import android.util.Slog; 37 38 import com.android.internal.R; 39 import com.android.internal.annotations.GuardedBy; 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.server.EventLogTags; 42 import com.android.server.LocalServices; 43 import com.android.server.power.PowerManagerService; 44 import com.android.server.power.batterysaver.BatterySaverPolicy.BatterySaverPolicyListener; 45 import com.android.server.power.batterysaver.BatterySaverPolicy.Policy; 46 import com.android.server.power.batterysaver.BatterySaverPolicy.PolicyLevel; 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 import com.android.server.power.batterysaver.BatterySavingStats.PlugState; 51 52 import java.util.ArrayList; 53 import java.util.Objects; 54 import java.util.Optional; 55 56 /** 57 * Responsible for battery saver mode transition logic. 58 * 59 * IMPORTANT: This class shares the power manager lock, which is very low in the lock hierarchy. 60 * Do not call out with the lock held. (Settings provider is okay.) 61 */ 62 public class BatterySaverController implements BatterySaverPolicyListener { 63 static final String TAG = "BatterySaverController"; 64 65 static final boolean DEBUG = BatterySaverPolicy.DEBUG; 66 67 private final Object mLock; 68 private final Context mContext; 69 private final MyHandler mHandler; 70 71 private PowerManager mPowerManager; 72 73 private final BatterySaverPolicy mBatterySaverPolicy; 74 75 private final BatterySavingStats mBatterySavingStats; 76 77 @GuardedBy("mLock") 78 private final ArrayList<LowPowerModeListener> mListeners = new ArrayList<>(); 79 80 /** 81 * Do not access directly; always use {@link #setFullEnabledLocked} 82 * and {@link #getFullEnabledLocked} 83 */ 84 @GuardedBy("mLock") 85 private boolean mFullEnabledRaw; 86 87 /** 88 * Do not access directly; always use {@link #setAdaptiveEnabledLocked} and 89 * {@link #getAdaptiveEnabledLocked}. 90 */ 91 @GuardedBy("mLock") 92 private boolean mAdaptiveEnabledRaw; 93 94 @GuardedBy("mLock") 95 private boolean mIsPluggedIn; 96 97 /** 98 * Whether full was previously enabled or not; only for the event logging. Only use it from 99 * {@link #handleBatterySaverStateChanged}. 100 */ 101 private boolean mFullPreviouslyEnabled; 102 103 /** 104 * Whether adaptive was previously enabled or not; only for the event logging. Only use it from 105 * {@link #handleBatterySaverStateChanged}. 106 */ 107 private boolean mAdaptivePreviouslyEnabled; 108 109 @GuardedBy("mLock") 110 private boolean mIsInteractive; 111 112 /** 113 * Package name that will receive an explicit manifest broadcast for 114 * {@link PowerManager#ACTION_POWER_SAVE_MODE_CHANGED}. It's {@code null} if it hasn't been 115 * retrieved yet. 116 */ 117 @Nullable 118 private Optional<String> mPowerSaveModeChangedListenerPackage; 119 120 public static final int REASON_PERCENTAGE_AUTOMATIC_ON = 0; 121 public static final int REASON_PERCENTAGE_AUTOMATIC_OFF = 1; 122 public static final int REASON_MANUAL_ON = 2; 123 public static final int REASON_MANUAL_OFF = 3; 124 public static final int REASON_STICKY_RESTORE = 4; 125 public static final int REASON_INTERACTIVE_CHANGED = 5; 126 public static final int REASON_POLICY_CHANGED = 6; 127 public static final int REASON_PLUGGED_IN = 7; 128 public static final int REASON_SETTING_CHANGED = 8; 129 public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON = 9; 130 public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF = 10; 131 public static final int REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED = 11; 132 public static final int REASON_TIMEOUT = 12; 133 public static final int REASON_FULL_POWER_SAVINGS_CHANGED = 13; 134 reasonToString(int reason)135 static String reasonToString(int reason) { 136 switch (reason) { 137 case BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON: 138 return "Percentage Auto ON"; 139 case BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF: 140 return "Percentage Auto OFF"; 141 case BatterySaverController.REASON_MANUAL_ON: 142 return "Manual ON"; 143 case BatterySaverController.REASON_MANUAL_OFF: 144 return "Manual OFF"; 145 case BatterySaverController.REASON_STICKY_RESTORE: 146 return "Sticky restore"; 147 case BatterySaverController.REASON_INTERACTIVE_CHANGED: 148 return "Interactivity changed"; 149 case BatterySaverController.REASON_POLICY_CHANGED: 150 return "Policy changed"; 151 case BatterySaverController.REASON_PLUGGED_IN: 152 return "Plugged in"; 153 case BatterySaverController.REASON_SETTING_CHANGED: 154 return "Setting changed"; 155 case BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON: 156 return "Dynamic Warning Auto ON"; 157 case BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF: 158 return "Dynamic Warning Auto OFF"; 159 case BatterySaverController.REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED: 160 return "Adaptive Power Savings changed"; 161 case BatterySaverController.REASON_TIMEOUT: 162 return "timeout"; 163 case BatterySaverController.REASON_FULL_POWER_SAVINGS_CHANGED: 164 return "Full Power Savings changed"; 165 default: 166 return "Unknown reason: " + reason; 167 } 168 } 169 170 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 171 @Override 172 public void onReceive(Context context, Intent intent) { 173 if (DEBUG) { 174 Slog.d(TAG, "onReceive: " + intent); 175 } 176 switch (intent.getAction()) { 177 case Intent.ACTION_SCREEN_ON: 178 case Intent.ACTION_SCREEN_OFF: 179 if (!isPolicyEnabled()) { 180 updateBatterySavingStats(); 181 return; // No need to send it if not enabled. 182 } 183 // We currently evaluate state only for CPU frequency changes. 184 // Don't send the broadcast, because we never did so in this case. 185 mHandler.postStateChanged(/*sendBroadcast=*/ false, 186 REASON_INTERACTIVE_CHANGED); 187 break; 188 case Intent.ACTION_BATTERY_CHANGED: 189 synchronized (mLock) { 190 mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0); 191 } 192 // Fall-through. 193 case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED: 194 case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED: 195 updateBatterySavingStats(); 196 break; 197 } 198 } 199 }; 200 201 /** 202 * Constructor. 203 */ BatterySaverController(Object lock, Context context, Looper looper, BatterySaverPolicy policy, BatterySavingStats batterySavingStats)204 public BatterySaverController(Object lock, Context context, Looper looper, 205 BatterySaverPolicy policy, BatterySavingStats batterySavingStats) { 206 mLock = lock; 207 mContext = context; 208 mHandler = new MyHandler(looper); 209 mBatterySaverPolicy = policy; 210 mBatterySaverPolicy.addListener(this); 211 mBatterySavingStats = batterySavingStats; 212 213 PowerManager.invalidatePowerSaveModeCaches(); 214 } 215 216 /** 217 * Add a listener. 218 */ addListener(LowPowerModeListener listener)219 public void addListener(LowPowerModeListener listener) { 220 synchronized (mLock) { 221 mListeners.add(listener); 222 } 223 } 224 225 /** 226 * Called by {@link PowerManagerService} on system ready, *with no lock held*. 227 */ systemReady()228 public void systemReady() { 229 final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); 230 filter.addAction(Intent.ACTION_SCREEN_OFF); 231 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 232 filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); 233 filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); 234 mContext.registerReceiver(mReceiver, filter); 235 236 mHandler.postSystemReady(); 237 } 238 getPowerManager()239 private PowerManager getPowerManager() { 240 if (mPowerManager == null) { 241 mPowerManager = 242 Objects.requireNonNull(mContext.getSystemService(PowerManager.class)); 243 } 244 return mPowerManager; 245 } 246 247 @Override onBatterySaverPolicyChanged(BatterySaverPolicy policy)248 public void onBatterySaverPolicyChanged(BatterySaverPolicy policy) { 249 if (!isPolicyEnabled()) { 250 return; // No need to send it if not enabled. 251 } 252 mHandler.postStateChanged(/*sendBroadcast=*/ true, REASON_POLICY_CHANGED); 253 } 254 255 private class MyHandler extends Handler { 256 private static final int MSG_STATE_CHANGED = 1; 257 258 private static final int ARG_DONT_SEND_BROADCAST = 0; 259 private static final int ARG_SEND_BROADCAST = 1; 260 261 private static final int MSG_SYSTEM_READY = 2; 262 MyHandler(Looper looper)263 public MyHandler(Looper looper) { 264 super(looper); 265 } 266 postStateChanged(boolean sendBroadcast, int reason)267 void postStateChanged(boolean sendBroadcast, int reason) { 268 obtainMessage(MSG_STATE_CHANGED, sendBroadcast ? 269 ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, reason).sendToTarget(); 270 } 271 postSystemReady()272 public void postSystemReady() { 273 obtainMessage(MSG_SYSTEM_READY, 0, 0).sendToTarget(); 274 } 275 276 @Override dispatchMessage(Message msg)277 public void dispatchMessage(Message msg) { 278 switch (msg.what) { 279 case MSG_STATE_CHANGED: 280 handleBatterySaverStateChanged( 281 msg.arg1 == ARG_SEND_BROADCAST, 282 msg.arg2); 283 break; 284 } 285 } 286 } 287 288 /** Enable or disable full battery saver. */ 289 @VisibleForTesting enableBatterySaver(boolean enable, int reason)290 public void enableBatterySaver(boolean enable, int reason) { 291 synchronized (mLock) { 292 if (getFullEnabledLocked() == enable) { 293 return; 294 } 295 setFullEnabledLocked(enable); 296 297 if (updatePolicyLevelLocked()) { 298 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); 299 } 300 } 301 } 302 updatePolicyLevelLocked()303 private boolean updatePolicyLevelLocked() { 304 if (getFullEnabledLocked()) { 305 return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_FULL); 306 } else if (getAdaptiveEnabledLocked()) { 307 return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_ADAPTIVE); 308 } else { 309 return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_OFF); 310 } 311 } 312 getPolicyLocked(@olicyLevel int policyLevel)313 BatterySaverPolicyConfig getPolicyLocked(@PolicyLevel int policyLevel) { 314 return mBatterySaverPolicy.getPolicyLocked(policyLevel).toConfig(); 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 getFullEnabledLocked() || (getAdaptiveEnabledLocked() 324 && 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 getFullEnabledLocked() || getAdaptiveEnabledLocked(); 335 } 336 } 337 isFullEnabled()338 boolean isFullEnabled() { 339 synchronized (mLock) { 340 return getFullEnabledLocked(); 341 } 342 } 343 setFullPolicyLocked(BatterySaverPolicyConfig config, int reason)344 boolean setFullPolicyLocked(BatterySaverPolicyConfig config, int reason) { 345 return setFullPolicyLocked(BatterySaverPolicy.Policy.fromConfig(config), reason); 346 } 347 setFullPolicyLocked(Policy policy, int reason)348 boolean setFullPolicyLocked(Policy policy, int reason) { 349 if (mBatterySaverPolicy.setFullPolicyLocked(policy)) { 350 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); 351 return true; 352 } 353 return false; 354 } 355 isAdaptiveEnabled()356 boolean isAdaptiveEnabled() { 357 synchronized (mLock) { 358 return getAdaptiveEnabledLocked(); 359 } 360 } 361 setAdaptivePolicyLocked(BatterySaverPolicyConfig config, int reason)362 boolean setAdaptivePolicyLocked(BatterySaverPolicyConfig config, int reason) { 363 return setAdaptivePolicyLocked(BatterySaverPolicy.Policy.fromConfig(config), reason); 364 } 365 setAdaptivePolicyLocked(Policy policy, int reason)366 boolean setAdaptivePolicyLocked(Policy policy, int reason) { 367 if (mBatterySaverPolicy.setAdaptivePolicyLocked(policy)) { 368 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); 369 return true; 370 } 371 return false; 372 } 373 resetAdaptivePolicyLocked(int reason)374 boolean resetAdaptivePolicyLocked(int reason) { 375 if (mBatterySaverPolicy.resetAdaptivePolicyLocked()) { 376 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); 377 return true; 378 } 379 return false; 380 } 381 setAdaptivePolicyEnabledLocked(boolean enabled, int reason)382 boolean setAdaptivePolicyEnabledLocked(boolean enabled, int reason) { 383 if (getAdaptiveEnabledLocked() == enabled) { 384 return false; 385 } 386 setAdaptiveEnabledLocked(enabled); 387 if (updatePolicyLevelLocked()) { 388 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); 389 return true; 390 } 391 return false; 392 } 393 394 /** @return whether device is in interactive state. */ isInteractive()395 public boolean isInteractive() { 396 synchronized (mLock) { 397 return mIsInteractive; 398 } 399 } 400 401 /** @return Battery saver policy. */ getBatterySaverPolicy()402 public BatterySaverPolicy getBatterySaverPolicy() { 403 return mBatterySaverPolicy; 404 } 405 406 /** 407 * @return true if launch boost should currently be disabled. 408 */ isLaunchBoostDisabled()409 public boolean isLaunchBoostDisabled() { 410 return isPolicyEnabled() && mBatterySaverPolicy.isLaunchBoostDisabled(); 411 } 412 413 /** 414 * Dispatch power save events to the listeners. 415 * 416 * This method is always called on the handler thread. 417 * 418 * This method is called only in the following cases: 419 * - When battery saver becomes activated. 420 * - When battery saver becomes deactivated. 421 * - When battery saver is on and the interactive state changes. 422 * - When battery saver is on and the battery saver policy changes. 423 * - When adaptive battery saver becomes activated. 424 * - When adaptive battery saver becomes deactivated. 425 * - When adaptive battery saver is active (and full is off) and the policy changes. 426 */ handleBatterySaverStateChanged(boolean sendBroadcast, int reason)427 void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) { 428 final LowPowerModeListener[] listeners; 429 430 final boolean enabled; 431 final boolean isInteractive = getPowerManager().isInteractive(); 432 433 synchronized (mLock) { 434 enabled = getFullEnabledLocked() || getAdaptiveEnabledLocked(); 435 436 EventLogTags.writeBatterySaverMode( 437 mFullPreviouslyEnabled ? 1 : 0, // Previously off or on. 438 mAdaptivePreviouslyEnabled ? 1 : 0, // Previously off or on. 439 getFullEnabledLocked() ? 1 : 0, // Now off or on. 440 getAdaptiveEnabledLocked() ? 1 : 0, // Now off or on. 441 isInteractive ? 1 : 0, // Device interactive state. 442 enabled ? mBatterySaverPolicy.toEventLogString() : "", 443 reason); 444 445 mFullPreviouslyEnabled = getFullEnabledLocked(); 446 mAdaptivePreviouslyEnabled = getAdaptiveEnabledLocked(); 447 448 listeners = mListeners.toArray(new LowPowerModeListener[0]); 449 450 mIsInteractive = isInteractive; 451 } 452 453 final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class); 454 if (pmi != null) { 455 pmi.setPowerMode(Mode.LOW_POWER, isEnabled()); 456 } 457 458 updateBatterySavingStats(); 459 460 if (sendBroadcast) { 461 462 if (DEBUG) { 463 Slog.i(TAG, "Sending broadcasts for mode: " + isEnabled()); 464 } 465 466 // Send the broadcasts and notify the listeners. We only do this when the battery saver 467 // mode changes, but not when only the screen state changes. 468 Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); 469 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 470 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 471 472 // Send the broadcast to a manifest-registered receiver that is specified in the config. 473 if (getPowerSaveModeChangedListenerPackage().isPresent()) { 474 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED) 475 .setPackage(getPowerSaveModeChangedListenerPackage().get()) 476 .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND 477 | Intent.FLAG_RECEIVER_FOREGROUND); 478 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 479 } 480 481 // Send internal version that requires signature permission. 482 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL); 483 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 484 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 485 Manifest.permission.DEVICE_POWER); 486 487 for (LowPowerModeListener listener : listeners) { 488 final PowerSaveState result = 489 mBatterySaverPolicy.getBatterySaverPolicy(listener.getServiceType()); 490 listener.onLowPowerModeChanged(result); 491 } 492 } 493 } 494 getPowerSaveModeChangedListenerPackage()495 private Optional<String> getPowerSaveModeChangedListenerPackage() { 496 if (mPowerSaveModeChangedListenerPackage == null) { 497 String configPowerSaveModeChangedListenerPackage = 498 mContext.getString(R.string.config_powerSaveModeChangedListenerPackage); 499 mPowerSaveModeChangedListenerPackage = 500 LocalServices 501 .getService(PackageManagerInternal.class) 502 .isSystemPackage(configPowerSaveModeChangedListenerPackage) 503 ? Optional.of(configPowerSaveModeChangedListenerPackage) 504 : Optional.empty(); 505 } 506 return mPowerSaveModeChangedListenerPackage; 507 } 508 updateBatterySavingStats()509 private void updateBatterySavingStats() { 510 final PowerManager pm = getPowerManager(); 511 if (pm == null) { 512 Slog.wtf(TAG, "PowerManager not initialized"); 513 return; 514 } 515 final boolean isInteractive = pm.isInteractive(); 516 final int dozeMode = 517 pm.isDeviceIdleMode() ? DozeState.DEEP 518 : pm.isLightDeviceIdleMode() ? DozeState.LIGHT 519 : DozeState.NOT_DOZING; 520 521 synchronized (mLock) { 522 mBatterySavingStats.transitionState( 523 getFullEnabledLocked() ? BatterySaverState.ON : 524 (getAdaptiveEnabledLocked() ? BatterySaverState.ADAPTIVE : 525 BatterySaverState.OFF), 526 isInteractive ? InteractiveState.INTERACTIVE : 527 InteractiveState.NON_INTERACTIVE, 528 dozeMode, 529 mIsPluggedIn ? PlugState.PLUGGED : PlugState.UNPLUGGED); 530 } 531 } 532 533 @GuardedBy("mLock") setFullEnabledLocked(boolean value)534 private void setFullEnabledLocked(boolean value) { 535 if (mFullEnabledRaw == value) { 536 return; 537 } 538 PowerManager.invalidatePowerSaveModeCaches(); 539 mFullEnabledRaw = value; 540 } 541 542 /** Non-blocking getter exists as a reminder not to directly modify the cached field */ getFullEnabledLocked()543 private boolean getFullEnabledLocked() { 544 return mFullEnabledRaw; 545 } 546 547 @GuardedBy("mLock") setAdaptiveEnabledLocked(boolean value)548 private void setAdaptiveEnabledLocked(boolean value) { 549 if (mAdaptiveEnabledRaw == value) { 550 return; 551 } 552 PowerManager.invalidatePowerSaveModeCaches(); 553 mAdaptiveEnabledRaw = value; 554 } 555 556 /** Non-blocking getter exists as a reminder not to directly modify the cached field */ getAdaptiveEnabledLocked()557 private boolean getAdaptiveEnabledLocked() { 558 return mAdaptiveEnabledRaw; 559 } 560 } 561