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