• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.systemui.power;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.database.ContentObserver;
25 import android.os.BatteryManager;
26 import android.os.Handler;
27 import android.os.PowerManager;
28 import android.os.SystemClock;
29 import android.os.UserHandle;
30 import android.provider.Settings;
31 import android.util.Log;
32 import android.util.Slog;
33 
34 import com.android.systemui.SystemUI;
35 import com.android.systemui.statusbar.phone.PhoneStatusBar;
36 
37 import java.io.FileDescriptor;
38 import java.io.PrintWriter;
39 import java.util.Arrays;
40 
41 public class PowerUI extends SystemUI {
42     static final String TAG = "PowerUI";
43     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
44 
45     private final Handler mHandler = new Handler();
46     private final Receiver mReceiver = new Receiver();
47 
48     private PowerManager mPowerManager;
49     private WarningsUI mWarnings;
50     private int mBatteryLevel = 100;
51     private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
52     private int mPlugType = 0;
53     private int mInvalidCharger = 0;
54 
55     private int mLowBatteryAlertCloseLevel;
56     private final int[] mLowBatteryReminderLevels = new int[2];
57 
58     private long mScreenOffTime = -1;
59 
start()60     public void start() {
61         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
62         mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
63         mWarnings = new PowerNotificationWarnings(mContext, getComponent(PhoneStatusBar.class));
64 
65         ContentObserver obs = new ContentObserver(mHandler) {
66             @Override
67             public void onChange(boolean selfChange) {
68                 updateBatteryWarningLevels();
69             }
70         };
71         final ContentResolver resolver = mContext.getContentResolver();
72         resolver.registerContentObserver(Settings.Global.getUriFor(
73                 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
74                 false, obs, UserHandle.USER_ALL);
75         updateBatteryWarningLevels();
76         mReceiver.init();
77     }
78 
setSaverMode(boolean mode)79     private void setSaverMode(boolean mode) {
80         mWarnings.showSaverMode(mode);
81     }
82 
updateBatteryWarningLevels()83     void updateBatteryWarningLevels() {
84         int critLevel = mContext.getResources().getInteger(
85                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
86 
87         final ContentResolver resolver = mContext.getContentResolver();
88         int defWarnLevel = mContext.getResources().getInteger(
89                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
90         int warnLevel = Settings.Global.getInt(resolver,
91                 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
92         if (warnLevel == 0) {
93             warnLevel = defWarnLevel;
94         }
95         if (warnLevel < critLevel) {
96             warnLevel = critLevel;
97         }
98 
99         mLowBatteryReminderLevels[0] = warnLevel;
100         mLowBatteryReminderLevels[1] = critLevel;
101         mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
102                 + mContext.getResources().getInteger(
103                         com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
104     }
105 
106     /**
107      * Buckets the battery level.
108      *
109      * The code in this function is a little weird because I couldn't comprehend
110      * the bucket going up when the battery level was going down. --joeo
111      *
112      * 1 means that the battery is "ok"
113      * 0 means that the battery is between "ok" and what we should warn about.
114      * less than 0 means that the battery is low
115      */
findBatteryLevelBucket(int level)116     private int findBatteryLevelBucket(int level) {
117         if (level >= mLowBatteryAlertCloseLevel) {
118             return 1;
119         }
120         if (level > mLowBatteryReminderLevels[0]) {
121             return 0;
122         }
123         final int N = mLowBatteryReminderLevels.length;
124         for (int i=N-1; i>=0; i--) {
125             if (level <= mLowBatteryReminderLevels[i]) {
126                 return -1-i;
127             }
128         }
129         throw new RuntimeException("not possible!");
130     }
131 
132     private final class Receiver extends BroadcastReceiver {
133 
init()134         public void init() {
135             // Register for Intent broadcasts for...
136             IntentFilter filter = new IntentFilter();
137             filter.addAction(Intent.ACTION_BATTERY_CHANGED);
138             filter.addAction(Intent.ACTION_SCREEN_OFF);
139             filter.addAction(Intent.ACTION_SCREEN_ON);
140             filter.addAction(Intent.ACTION_USER_SWITCHED);
141             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
142             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
143             mContext.registerReceiver(this, filter, null, mHandler);
144             updateSaverMode();
145         }
146 
updateSaverMode()147         private void updateSaverMode() {
148             setSaverMode(mPowerManager.isPowerSaveMode());
149         }
150 
151         @Override
onReceive(Context context, Intent intent)152         public void onReceive(Context context, Intent intent) {
153             String action = intent.getAction();
154             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
155                 final int oldBatteryLevel = mBatteryLevel;
156                 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
157                 final int oldBatteryStatus = mBatteryStatus;
158                 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
159                         BatteryManager.BATTERY_STATUS_UNKNOWN);
160                 final int oldPlugType = mPlugType;
161                 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
162                 final int oldInvalidCharger = mInvalidCharger;
163                 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
164 
165                 final boolean plugged = mPlugType != 0;
166                 final boolean oldPlugged = oldPlugType != 0;
167 
168                 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
169                 int bucket = findBatteryLevelBucket(mBatteryLevel);
170 
171                 if (DEBUG) {
172                     Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
173                             + " .. " + mLowBatteryReminderLevels[0]
174                             + " .. " + mLowBatteryReminderLevels[1]);
175                     Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
176                     Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
177                     Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
178                     Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
179                     Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
180                     Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
181                 }
182 
183                 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
184                 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
185                     Slog.d(TAG, "showing invalid charger warning");
186                     mWarnings.showInvalidChargerWarning();
187                     return;
188                 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
189                     mWarnings.dismissInvalidChargerWarning();
190                 } else if (mWarnings.isInvalidChargerWarningShowing()) {
191                     // if invalid charger is showing, don't show low battery
192                     return;
193                 }
194 
195                 if (!plugged
196                         && (bucket < oldBucket || oldPlugged)
197                         && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
198                         && bucket < 0) {
199                     // only play SFX when the dialog comes up or the bucket changes
200                     final boolean playSound = bucket != oldBucket || oldPlugged;
201                     mWarnings.showLowBatteryWarning(playSound);
202                 } else if (plugged || (bucket > oldBucket && bucket > 0)) {
203                     mWarnings.dismissLowBatteryWarning();
204                 } else {
205                     mWarnings.updateLowBatteryWarning();
206                 }
207             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
208                 mScreenOffTime = SystemClock.elapsedRealtime();
209             } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
210                 mScreenOffTime = -1;
211             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
212                 mWarnings.userSwitched();
213             } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
214                 updateSaverMode();
215             } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGING.equals(action)) {
216                 setSaverMode(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
217             } else {
218                 Slog.w(TAG, "unknown intent: " + intent);
219             }
220         }
221     };
222 
dump(FileDescriptor fd, PrintWriter pw, String[] args)223     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
224         pw.print("mLowBatteryAlertCloseLevel=");
225         pw.println(mLowBatteryAlertCloseLevel);
226         pw.print("mLowBatteryReminderLevels=");
227         pw.println(Arrays.toString(mLowBatteryReminderLevels));
228         pw.print("mBatteryLevel=");
229         pw.println(Integer.toString(mBatteryLevel));
230         pw.print("mBatteryStatus=");
231         pw.println(Integer.toString(mBatteryStatus));
232         pw.print("mPlugType=");
233         pw.println(Integer.toString(mPlugType));
234         pw.print("mInvalidCharger=");
235         pw.println(Integer.toString(mInvalidCharger));
236         pw.print("mScreenOffTime=");
237         pw.print(mScreenOffTime);
238         if (mScreenOffTime >= 0) {
239             pw.print(" (");
240             pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
241             pw.print(" ago)");
242         }
243         pw.println();
244         pw.print("soundTimeout=");
245         pw.println(Settings.Global.getInt(mContext.getContentResolver(),
246                 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
247         pw.print("bucket: ");
248         pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
249         mWarnings.dump(pw);
250     }
251 
252     public interface WarningsUI {
update(int batteryLevel, int bucket, long screenOffTime)253         void update(int batteryLevel, int bucket, long screenOffTime);
showSaverMode(boolean mode)254         void showSaverMode(boolean mode);
dismissLowBatteryWarning()255         void dismissLowBatteryWarning();
showLowBatteryWarning(boolean playSound)256         void showLowBatteryWarning(boolean playSound);
dismissInvalidChargerWarning()257         void dismissInvalidChargerWarning();
showInvalidChargerWarning()258         void showInvalidChargerWarning();
updateLowBatteryWarning()259         void updateLowBatteryWarning();
isInvalidChargerWarningShowing()260         boolean isInvalidChargerWarningShowing();
dump(PrintWriter pw)261         void dump(PrintWriter pw);
userSwitched()262         void userSwitched();
263     }
264 }
265 
266