Home | History | Annotate | Download | only in batterysaver
      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 {
    110         void onSystemReady(BatterySaverController caller);
    111 
    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      */
    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      */
    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      */
    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 
    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
    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 
    214         public MyHandler(Looper looper) {
    215             super(looper);
    216         }
    217 
    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 
    223         public void postSystemReady() {
    224             obtainMessage(MSG_SYSTEM_READY, 0, 0).sendToTarget();
    225         }
    226 
    227         @Override
    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      */
    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. */
    260     public boolean isEnabled() {
    261         synchronized (mLock) {
    262             return mEnabled;
    263         }
    264     }
    265 
    266     /** @return whether device is in interactive state. */
    267     public boolean isInteractive() {
    268         synchronized (mLock) {
    269             return mIsInteractive;
    270         }
    271     }
    272 
    273     /** @return Battery saver policy. */
    274     public BatterySaverPolicy getBatterySaverPolicy() {
    275         return mBatterySaverPolicy;
    276     }
    277 
    278     /**
    279      * @return true if launch boost should currently be disabled.
    280      */
    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      */
    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 
    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