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 BatterySaverUtils()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 Parameters(Context context)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 */ setPowerSaveMode(Context context, boolean enable, boolean needFirstTimeWarning)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 */ maybeShowBatterySaverConfirmation(Context context, Bundle extras)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 showAutoBatterySaverSuggestion(Context context, Bundle extras)176 private static void showAutoBatterySaverSuggestion(Context context, Bundle extras) { 177 context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION, extras)); 178 } 179 getSystemUiBroadcast(String action, Bundle extras)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 setBatterySaverConfirmationAcknowledged(Context context)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 */ suppressAutoBatterySaver(Context context)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 */ setAutoBatterySaverTriggerLevel(Context context, int level)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 */ ensureAutoBatterySaver(Context context, int level)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 */ revertScheduleToNoneIfNeeded(Context context)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