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.Handler; 27 import android.os.Looper; 28 import android.os.Message; 29 import android.os.PowerManager; 30 import android.os.PowerManagerInternal; 31 import android.os.PowerManagerInternal.LowPowerModeListener; 32 import android.os.PowerSaveState; 33 import android.os.UserHandle; 34 import android.util.ArrayMap; 35 import android.util.Slog; 36 37 import com.android.internal.annotations.GuardedBy; 38 import com.android.internal.util.ArrayUtils; 39 import com.android.internal.util.Preconditions; 40 import com.android.server.EventLogTags; 41 import com.android.server.LocalServices; 42 import com.android.server.power.BatterySaverPolicy; 43 import com.android.server.power.BatterySaverPolicy.BatterySaverPolicyListener; 44 import com.android.server.power.PowerManagerService; 45 import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState; 46 import com.android.server.power.batterysaver.BatterySavingStats.DozeState; 47 import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState; 48 49 import java.util.ArrayList; 50 51 /** 52 * Responsible for battery saver mode transition logic. 53 * 54 * IMPORTANT: This class shares the power manager lock, which is very low in the lock hierarchy. 55 * Do not call out with the lock held. (Settings provider is okay.) 56 */ 57 public class BatterySaverController implements BatterySaverPolicyListener { 58 static final String TAG = "BatterySaverController"; 59 60 static final boolean DEBUG = BatterySaverPolicy.DEBUG; 61 62 private final Object mLock; 63 private final Context mContext; 64 private final MyHandler mHandler; 65 private final FileUpdater mFileUpdater; 66 67 private PowerManager mPowerManager; 68 69 private final BatterySaverPolicy mBatterySaverPolicy; 70 71 private final BatterySavingStats mBatterySavingStats; 72 73 @GuardedBy("mLock") 74 private final ArrayList<LowPowerModeListener> mListeners = new ArrayList<>(); 75 76 @GuardedBy("mLock") 77 private boolean mEnabled; 78 79 @GuardedBy("mLock") 80 private boolean mIsPluggedIn; 81 82 /** 83 * Previously enabled or not; only for the event logging. Only use it from 84 * {@link #handleBatterySaverStateChanged}. 85 */ 86 private boolean mPreviouslyEnabled; 87 88 @GuardedBy("mLock") 89 private boolean mIsInteractive; 90 91 /** 92 * Read-only list of plugins. No need for synchronization. 93 */ 94 private final Plugin[] mPlugins; 95 96 public static final int REASON_AUTOMATIC_ON = 0; 97 public static final int REASON_AUTOMATIC_OFF = 1; 98 public static final int REASON_MANUAL_ON = 2; 99 public static final int REASON_MANUAL_OFF = 3; 100 public static final int REASON_STICKY_RESTORE = 4; 101 public static final int REASON_INTERACTIVE_CHANGED = 5; 102 public static final int REASON_POLICY_CHANGED = 6; 103 public static final int REASON_PLUGGED_IN = 7; 104 public static final int REASON_SETTING_CHANGED = 8; 105 106 /** 107 * Plugin interface. All methods are guaranteed to be called on the same (handler) thread. 108 */ 109 public interface Plugin { onSystemReady(BatterySaverController caller)110 void onSystemReady(BatterySaverController caller); 111 onBatterySaverChanged(BatterySaverController caller)112 void onBatterySaverChanged(BatterySaverController caller); 113 } 114 115 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 116 @Override 117 public void onReceive(Context context, Intent intent) { 118 if (DEBUG) { 119 Slog.d(TAG, "onReceive: " + intent); 120 } 121 switch (intent.getAction()) { 122 case Intent.ACTION_SCREEN_ON: 123 case Intent.ACTION_SCREEN_OFF: 124 if (!isEnabled()) { 125 updateBatterySavingStats(); 126 return; // No need to send it if not enabled. 127 } 128 // Don't send the broadcast, because we never did so in this case. 129 mHandler.postStateChanged(/*sendBroadcast=*/ false, 130 REASON_INTERACTIVE_CHANGED); 131 break; 132 case Intent.ACTION_BATTERY_CHANGED: 133 synchronized (mLock) { 134 mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0); 135 } 136 // Fall-through. 137 case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED: 138 case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED: 139 updateBatterySavingStats(); 140 break; 141 } 142 } 143 }; 144 145 /** 146 * Constructor. 147 */ BatterySaverController(Object lock, Context context, Looper looper, BatterySaverPolicy policy, BatterySavingStats batterySavingStats)148 public BatterySaverController(Object lock, Context context, Looper looper, 149 BatterySaverPolicy policy, BatterySavingStats batterySavingStats) { 150 mLock = lock; 151 mContext = context; 152 mHandler = new MyHandler(looper); 153 mBatterySaverPolicy = policy; 154 mBatterySaverPolicy.addListener(this); 155 mFileUpdater = new FileUpdater(context); 156 mBatterySavingStats = batterySavingStats; 157 158 // Initialize plugins. 159 final ArrayList<Plugin> plugins = new ArrayList<>(); 160 plugins.add(new BatterySaverLocationPlugin(mContext)); 161 162 mPlugins = plugins.toArray(new Plugin[plugins.size()]); 163 } 164 165 /** 166 * Add a listener. 167 */ addListener(LowPowerModeListener listener)168 public void addListener(LowPowerModeListener listener) { 169 synchronized (mLock) { 170 mListeners.add(listener); 171 } 172 } 173 174 /** 175 * Called by {@link PowerManagerService} on system ready, *with no lock held*. 176 */ systemReady()177 public void systemReady() { 178 final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); 179 filter.addAction(Intent.ACTION_SCREEN_OFF); 180 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 181 filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); 182 filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); 183 mContext.registerReceiver(mReceiver, filter); 184 185 mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class) 186 .isRuntimeRestarted()); 187 mHandler.postSystemReady(); 188 } 189 getPowerManager()190 private PowerManager getPowerManager() { 191 if (mPowerManager == null) { 192 mPowerManager = 193 Preconditions.checkNotNull(mContext.getSystemService(PowerManager.class)); 194 } 195 return mPowerManager; 196 } 197 198 @Override onBatterySaverPolicyChanged(BatterySaverPolicy policy)199 public void onBatterySaverPolicyChanged(BatterySaverPolicy policy) { 200 if (!isEnabled()) { 201 return; // No need to send it if not enabled. 202 } 203 mHandler.postStateChanged(/*sendBroadcast=*/ true, REASON_POLICY_CHANGED); 204 } 205 206 private class MyHandler extends Handler { 207 private static final int MSG_STATE_CHANGED = 1; 208 209 private static final int ARG_DONT_SEND_BROADCAST = 0; 210 private static final int ARG_SEND_BROADCAST = 1; 211 212 private static final int MSG_SYSTEM_READY = 2; 213 MyHandler(Looper looper)214 public MyHandler(Looper looper) { 215 super(looper); 216 } 217 postStateChanged(boolean sendBroadcast, int reason)218 public void postStateChanged(boolean sendBroadcast, int reason) { 219 obtainMessage(MSG_STATE_CHANGED, sendBroadcast ? 220 ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, reason).sendToTarget(); 221 } 222 postSystemReady()223 public void postSystemReady() { 224 obtainMessage(MSG_SYSTEM_READY, 0, 0).sendToTarget(); 225 } 226 227 @Override dispatchMessage(Message msg)228 public void dispatchMessage(Message msg) { 229 switch (msg.what) { 230 case MSG_STATE_CHANGED: 231 handleBatterySaverStateChanged( 232 msg.arg1 == ARG_SEND_BROADCAST, 233 msg.arg2); 234 break; 235 236 case MSG_SYSTEM_READY: 237 for (Plugin p : mPlugins) { 238 p.onSystemReady(BatterySaverController.this); 239 } 240 break; 241 } 242 } 243 } 244 245 /** 246 * Called by {@link PowerManagerService} to update the battery saver stete. 247 */ enableBatterySaver(boolean enable, int reason)248 public void enableBatterySaver(boolean enable, int reason) { 249 synchronized (mLock) { 250 if (mEnabled == enable) { 251 return; 252 } 253 mEnabled = enable; 254 255 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); 256 } 257 } 258 259 /** @return whether battery saver is enabled or not. */ isEnabled()260 public boolean isEnabled() { 261 synchronized (mLock) { 262 return mEnabled; 263 } 264 } 265 266 /** @return whether device is in interactive state. */ isInteractive()267 public boolean isInteractive() { 268 synchronized (mLock) { 269 return mIsInteractive; 270 } 271 } 272 273 /** @return Battery saver policy. */ getBatterySaverPolicy()274 public BatterySaverPolicy getBatterySaverPolicy() { 275 return mBatterySaverPolicy; 276 } 277 278 /** 279 * @return true if launch boost should currently be disabled. 280 */ isLaunchBoostDisabled()281 public boolean isLaunchBoostDisabled() { 282 return isEnabled() && mBatterySaverPolicy.isLaunchBoostDisabled(); 283 } 284 285 /** 286 * Dispatch power save events to the listeners. 287 * 288 * This method is always called on the handler thread. 289 * 290 * This method is called only in the following cases: 291 * - When battery saver becomes activated. 292 * - When battery saver becomes deactivated. 293 * - When battery saver is on the interactive state changes. 294 * - When battery saver is on the battery saver policy changes. 295 */ handleBatterySaverStateChanged(boolean sendBroadcast, int reason)296 void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) { 297 final LowPowerModeListener[] listeners; 298 299 final boolean enabled; 300 final boolean isInteractive = getPowerManager().isInteractive(); 301 final ArrayMap<String, String> fileValues; 302 303 synchronized (mLock) { 304 EventLogTags.writeBatterySaverMode( 305 mPreviouslyEnabled ? 1 : 0, // Previously off or on. 306 mEnabled ? 1 : 0, // Now off or on. 307 isInteractive ? 1 : 0, // Device interactive state. 308 mEnabled ? mBatterySaverPolicy.toEventLogString() : "", 309 reason); 310 mPreviouslyEnabled = mEnabled; 311 312 listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]); 313 314 enabled = mEnabled; 315 mIsInteractive = isInteractive; 316 317 if (enabled) { 318 fileValues = mBatterySaverPolicy.getFileValues(isInteractive); 319 } else { 320 fileValues = null; 321 } 322 } 323 324 final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class); 325 if (pmi != null) { 326 pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0); 327 } 328 329 updateBatterySavingStats(); 330 331 if (ArrayUtils.isEmpty(fileValues)) { 332 mFileUpdater.restoreDefault(); 333 } else { 334 mFileUpdater.writeFiles(fileValues); 335 } 336 337 for (Plugin p : mPlugins) { 338 p.onBatterySaverChanged(this); 339 } 340 341 if (sendBroadcast) { 342 343 if (DEBUG) { 344 Slog.i(TAG, "Sending broadcasts for mode: " + enabled); 345 } 346 347 // Send the broadcasts and notify the listeners. We only do this when the battery saver 348 // mode changes, but not when only the screen state changes. 349 Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING) 350 .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, enabled) 351 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 352 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 353 354 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); 355 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 356 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 357 358 // Send internal version that requires signature permission. 359 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL); 360 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 361 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 362 Manifest.permission.DEVICE_POWER); 363 364 for (LowPowerModeListener listener : listeners) { 365 final PowerSaveState result = 366 mBatterySaverPolicy.getBatterySaverPolicy( 367 listener.getServiceType(), enabled); 368 listener.onLowPowerModeChanged(result); 369 } 370 } 371 } 372 updateBatterySavingStats()373 private void updateBatterySavingStats() { 374 final PowerManager pm = getPowerManager(); 375 if (pm == null) { 376 Slog.wtf(TAG, "PowerManager not initialized"); 377 return; 378 } 379 final boolean isInteractive = pm.isInteractive(); 380 final int dozeMode = 381 pm.isDeviceIdleMode() ? DozeState.DEEP 382 : pm.isLightDeviceIdleMode() ? DozeState.LIGHT 383 : DozeState.NOT_DOZING; 384 385 synchronized (mLock) { 386 if (mIsPluggedIn) { 387 mBatterySavingStats.startCharging(); 388 return; 389 } 390 mBatterySavingStats.transitionState( 391 mEnabled ? BatterySaverState.ON : BatterySaverState.OFF, 392 isInteractive ? InteractiveState.INTERACTIVE : InteractiveState.NON_INTERACTIVE, 393 dozeMode); 394 } 395 } 396 } 397