• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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