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