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