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