Home | History | Annotate | Download | only in fuelgauge
      1 /*
      2  * Copyright (C) 2018 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 
     17 package com.android.settingslib.fuelgauge;
     18 
     19 import android.content.ContentResolver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.os.Bundle;
     23 import android.os.PowerManager;
     24 import android.provider.Settings.Global;
     25 import android.provider.Settings.Secure;
     26 import android.text.TextUtils;
     27 import android.util.KeyValueListParser;
     28 import android.util.Log;
     29 import android.util.Slog;
     30 
     31 /**
     32  * Utilities related to battery saver.
     33  */
     34 public class BatterySaverUtils {
     35 
     36     private static final String TAG = "BatterySaverUtils";
     37     /**
     38      * When set to "true" the notification will be a generic confirm message instead of asking the
     39      * user if they want to turn on battery saver. If set to false the dialog will specifically
     40      * talk about battery saver without giving the option of turning it on. The only button visible
     41      * will be a generic confirmation button to acknowledge the dialog.
     42      */
     43     public static final String EXTRA_CONFIRM_TEXT_ONLY = "extra_confirm_only";
     44     /**
     45      * Ignored if {@link #EXTRA_CONFIRM_TEXT_ONLY} is "false". Can be set to any of the values in
     46      * {@link PowerManager.AutoPowerSaveModeTriggers}. If set the dialog will set the power
     47      * save mode trigger to the specified value after the user acknowledges the trigger.
     48      */
     49     public static final String EXTRA_POWER_SAVE_MODE_TRIGGER = "extra_power_save_mode_trigger";
     50     /**
     51      * Ignored if {@link #EXTRA_CONFIRM_TEXT_ONLY} is "false". can be set to any value between
     52      * 0-100 that will be used if {@link #EXTRA_POWER_SAVE_MODE_TRIGGER} is
     53      * {@link PowerManager#POWER_SAVE_MODE_TRIGGER_PERCENTAGE}.
     54      */
     55     public static final String EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL =
     56             "extra_power_save_mode_trigger_level";
     57 
     58     private BatterySaverUtils() {
     59     }
     60 
     61     private static final boolean DEBUG = false;
     62 
     63     private static final String SYSUI_PACKAGE = "com.android.systemui";
     64 
     65     /** Broadcast action for SystemUI to show the battery saver confirmation dialog. */
     66     public static final String ACTION_SHOW_START_SAVER_CONFIRMATION = "PNW.startSaverConfirmation";
     67 
     68     /**
     69      * Broadcast action for SystemUI to show the notification that suggests turning on
     70      * automatic battery saver.
     71      */
     72     public static final String ACTION_SHOW_AUTO_SAVER_SUGGESTION
     73             = "PNW.autoSaverSuggestion";
     74 
     75     private static class Parameters {
     76         private final Context mContext;
     77 
     78         /**
     79          * We show the auto battery saver suggestion notification when the user manually enables
     80          * battery saver for the START_NTH time through the END_NTH time.
     81          * (We won't show it for END_NTH + 1 time and after.)
     82          */
     83         private static final int AUTO_SAVER_SUGGESTION_START_NTH = 4;
     84         private static final int AUTO_SAVER_SUGGESTION_END_NTH = 8;
     85 
     86         public final int startNth;
     87         public final int endNth;
     88 
     89         public Parameters(Context context) {
     90             mContext = context;
     91 
     92             final String newValue = Global.getString(mContext.getContentResolver(),
     93                     Global.LOW_POWER_MODE_SUGGESTION_PARAMS);
     94             final KeyValueListParser parser = new KeyValueListParser(',');
     95             try {
     96                 parser.setString(newValue);
     97             } catch (IllegalArgumentException e) {
     98                 Slog.wtf(TAG, "Bad constants: " + newValue);
     99             }
    100             startNth = parser.getInt("start_nth", AUTO_SAVER_SUGGESTION_START_NTH);
    101             endNth = parser.getInt("end_nth", AUTO_SAVER_SUGGESTION_END_NTH);
    102         }
    103     }
    104 
    105     /**
    106      * Enable / disable battery saver by user request.
    107      * - If it's the first time and needFirstTimeWarning, show the first time dialog.
    108      * - If it's 4th time through 8th time, show the schedule suggestion notification.
    109      *
    110      * @param enable true to disable battery saver.
    111      *
    112      * @return true if the request succeeded.
    113      */
    114     public static synchronized boolean setPowerSaveMode(Context context,
    115             boolean enable, boolean needFirstTimeWarning) {
    116         if (DEBUG) {
    117             Log.d(TAG, "Battery saver turning " + (enable ? "ON" : "OFF"));
    118         }
    119         final ContentResolver cr = context.getContentResolver();
    120 
    121         final Bundle confirmationExtras = new Bundle(1);
    122         confirmationExtras.putBoolean(EXTRA_CONFIRM_TEXT_ONLY, false);
    123         if (enable && needFirstTimeWarning
    124                 && maybeShowBatterySaverConfirmation(context, confirmationExtras)) {
    125             return false;
    126         }
    127         if (enable && !needFirstTimeWarning) {
    128             setBatterySaverConfirmationAcknowledged(context);
    129         }
    130 
    131         if (context.getSystemService(PowerManager.class).setPowerSaveModeEnabled(enable)) {
    132             if (enable) {
    133                 final int count =
    134                         Secure.getInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 0) + 1;
    135                 Secure.putInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, count);
    136 
    137                 final Parameters parameters = new Parameters(context);
    138 
    139                 if ((count >= parameters.startNth)
    140                         && (count <= parameters.endNth)
    141                         && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0
    142                         && Secure.getInt(cr,
    143                         Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) {
    144                     showAutoBatterySaverSuggestion(context, confirmationExtras);
    145                 }
    146             }
    147 
    148             return true;
    149         }
    150         return false;
    151     }
    152 
    153     /**
    154      * Shows the battery saver confirmation warning if it hasn't been acknowledged by the user in
    155      * the past before. Various extras can be provided that will change the behavior of this
    156      * notification as well as the ui for it.
    157      * @param context A valid context
    158      * @param extras Any extras to include in the intent to trigger this confirmation that will
    159      * help the system disambiguate what to show/do
    160      *
    161      * @return True if it showed the notification because it has not been previously acknowledged.
    162      * @see #EXTRA_CONFIRM_TEXT_ONLY
    163      * @see #EXTRA_POWER_SAVE_MODE_TRIGGER
    164      * @see #EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL
    165      */
    166     public static boolean maybeShowBatterySaverConfirmation(Context context, Bundle extras) {
    167         if (Secure.getInt(context.getContentResolver(),
    168                 Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 0) != 0) {
    169             return false; // Already shown.
    170         }
    171         context.sendBroadcast(
    172                 getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION, extras));
    173         return true;
    174     }
    175 
    176     private static void showAutoBatterySaverSuggestion(Context context, Bundle extras) {
    177         context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION, extras));
    178     }
    179 
    180     private static Intent getSystemUiBroadcast(String action, Bundle extras) {
    181         final Intent i = new Intent(action);
    182         i.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    183         i.setPackage(SYSUI_PACKAGE);
    184         i.putExtras(extras);
    185         return i;
    186     }
    187 
    188     private static void setBatterySaverConfirmationAcknowledged(Context context) {
    189         Secure.putInt(context.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
    190     }
    191 
    192     /**
    193      * Don't show the automatic battery suggestion notification in the future.
    194      */
    195     public static void suppressAutoBatterySaver(Context context) {
    196         Secure.putInt(context.getContentResolver(),
    197                 Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 1);
    198     }
    199 
    200     /**
    201      * Set the automatic battery saver trigger level to {@code level}.
    202      */
    203     public static void setAutoBatterySaverTriggerLevel(Context context, int level) {
    204         if (level > 0) {
    205             suppressAutoBatterySaver(context);
    206         }
    207         Global.putInt(context.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, level);
    208     }
    209 
    210     /**
    211      * Set the automatic battery saver trigger level to {@code level}, but only when
    212      * automatic battery saver isn't enabled yet.
    213      */
    214     public static void ensureAutoBatterySaver(Context context, int level) {
    215         if (Global.getInt(context.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)
    216                 == 0) {
    217             setAutoBatterySaverTriggerLevel(context, level);
    218         }
    219     }
    220 
    221     /**
    222      * Reverts battery saver schedule mode to none if we are in a bad state where routine mode
    223      * is selected but no app is configured to actually provide the signal.
    224      * @param context a valid context
    225      */
    226     public static void revertScheduleToNoneIfNeeded(Context context) {
    227         ContentResolver resolver = context.getContentResolver();
    228         final int currentMode = Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
    229                 PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
    230         boolean providerConfigured = !TextUtils.isEmpty(context.getString(
    231                 com.android.internal.R.string.config_batterySaverScheduleProvider));
    232         if (currentMode == PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC && !providerConfigured) {
    233             Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
    234             Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
    235                     PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
    236         }
    237     }
    238 }
    239