• 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 
updateBatteryWarningLevels()79     void updateBatteryWarningLevels() {
80         int critLevel = mContext.getResources().getInteger(
81                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
82 
83         final ContentResolver resolver = mContext.getContentResolver();
84         int defWarnLevel = mContext.getResources().getInteger(
85                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
86         int warnLevel = Settings.Global.getInt(resolver,
87                 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
88         if (warnLevel == 0) {
89             warnLevel = defWarnLevel;
90         }
91         if (warnLevel < critLevel) {
92             warnLevel = critLevel;
93         }
94 
95         mLowBatteryReminderLevels[0] = warnLevel;
96         mLowBatteryReminderLevels[1] = critLevel;
97         mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
98                 + mContext.getResources().getInteger(
99                         com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
100     }
101 
102     /**
103      * Buckets the battery level.
104      *
105      * The code in this function is a little weird because I couldn't comprehend
106      * the bucket going up when the battery level was going down. --joeo
107      *
108      * 1 means that the battery is "ok"
109      * 0 means that the battery is between "ok" and what we should warn about.
110      * less than 0 means that the battery is low
111      */
findBatteryLevelBucket(int level)112     private int findBatteryLevelBucket(int level) {
113         if (level >= mLowBatteryAlertCloseLevel) {
114             return 1;
115         }
116         if (level > mLowBatteryReminderLevels[0]) {
117             return 0;
118         }
119         final int N = mLowBatteryReminderLevels.length;
120         for (int i=N-1; i>=0; i--) {
121             if (level <= mLowBatteryReminderLevels[i]) {
122                 return -1-i;
123             }
124         }
125         throw new RuntimeException("not possible!");
126     }
127 
128     private final class Receiver extends BroadcastReceiver {
129 
init()130         public void init() {
131             // Register for Intent broadcasts for...
132             IntentFilter filter = new IntentFilter();
133             filter.addAction(Intent.ACTION_BATTERY_CHANGED);
134             filter.addAction(Intent.ACTION_SCREEN_OFF);
135             filter.addAction(Intent.ACTION_SCREEN_ON);
136             filter.addAction(Intent.ACTION_USER_SWITCHED);
137             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
138             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
139             mContext.registerReceiver(this, filter, null, mHandler);
140         }
141 
142         @Override
onReceive(Context context, Intent intent)143         public void onReceive(Context context, Intent intent) {
144             String action = intent.getAction();
145             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
146                 final int oldBatteryLevel = mBatteryLevel;
147                 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
148                 final int oldBatteryStatus = mBatteryStatus;
149                 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
150                         BatteryManager.BATTERY_STATUS_UNKNOWN);
151                 final int oldPlugType = mPlugType;
152                 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
153                 final int oldInvalidCharger = mInvalidCharger;
154                 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
155 
156                 final boolean plugged = mPlugType != 0;
157                 final boolean oldPlugged = oldPlugType != 0;
158 
159                 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
160                 int bucket = findBatteryLevelBucket(mBatteryLevel);
161 
162                 if (DEBUG) {
163                     Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
164                             + " .. " + mLowBatteryReminderLevels[0]
165                             + " .. " + mLowBatteryReminderLevels[1]);
166                     Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
167                     Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
168                     Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
169                     Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
170                     Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
171                     Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
172                 }
173 
174                 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
175                 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
176                     Slog.d(TAG, "showing invalid charger warning");
177                     mWarnings.showInvalidChargerWarning();
178                     return;
179                 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
180                     mWarnings.dismissInvalidChargerWarning();
181                 } else if (mWarnings.isInvalidChargerWarningShowing()) {
182                     // if invalid charger is showing, don't show low battery
183                     return;
184                 }
185 
186                 boolean isPowerSaver = mPowerManager.isPowerSaveMode();
187                 if (!plugged
188                         && !isPowerSaver
189                         && (bucket < oldBucket || oldPlugged)
190                         && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
191                         && bucket < 0) {
192                     // only play SFX when the dialog comes up or the bucket changes
193                     final boolean playSound = bucket != oldBucket || oldPlugged;
194                     mWarnings.showLowBatteryWarning(playSound);
195                 } else if (isPowerSaver || plugged || (bucket > oldBucket && bucket > 0)) {
196                     mWarnings.dismissLowBatteryWarning();
197                 } else {
198                     mWarnings.updateLowBatteryWarning();
199                 }
200             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
201                 mScreenOffTime = SystemClock.elapsedRealtime();
202             } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
203                 mScreenOffTime = -1;
204             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
205                 mWarnings.userSwitched();
206             } else {
207                 Slog.w(TAG, "unknown intent: " + intent);
208             }
209         }
210     };
211 
dump(FileDescriptor fd, PrintWriter pw, String[] args)212     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
213         pw.print("mLowBatteryAlertCloseLevel=");
214         pw.println(mLowBatteryAlertCloseLevel);
215         pw.print("mLowBatteryReminderLevels=");
216         pw.println(Arrays.toString(mLowBatteryReminderLevels));
217         pw.print("mBatteryLevel=");
218         pw.println(Integer.toString(mBatteryLevel));
219         pw.print("mBatteryStatus=");
220         pw.println(Integer.toString(mBatteryStatus));
221         pw.print("mPlugType=");
222         pw.println(Integer.toString(mPlugType));
223         pw.print("mInvalidCharger=");
224         pw.println(Integer.toString(mInvalidCharger));
225         pw.print("mScreenOffTime=");
226         pw.print(mScreenOffTime);
227         if (mScreenOffTime >= 0) {
228             pw.print(" (");
229             pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
230             pw.print(" ago)");
231         }
232         pw.println();
233         pw.print("soundTimeout=");
234         pw.println(Settings.Global.getInt(mContext.getContentResolver(),
235                 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
236         pw.print("bucket: ");
237         pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
238         mWarnings.dump(pw);
239     }
240 
241     public interface WarningsUI {
update(int batteryLevel, int bucket, long screenOffTime)242         void update(int batteryLevel, int bucket, long screenOffTime);
dismissLowBatteryWarning()243         void dismissLowBatteryWarning();
showLowBatteryWarning(boolean playSound)244         void showLowBatteryWarning(boolean playSound);
dismissInvalidChargerWarning()245         void dismissInvalidChargerWarning();
showInvalidChargerWarning()246         void showInvalidChargerWarning();
updateLowBatteryWarning()247         void updateLowBatteryWarning();
isInvalidChargerWarningShowing()248         boolean isInvalidChargerWarningShowing();
dump(PrintWriter pw)249         void dump(PrintWriter pw);
userSwitched()250         void userSwitched();
251     }
252 }
253 
254