PowerUI.java revision ecbc5e828abaf2b10dd7a746ecff9bcfae2b0f7f
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
17package com.android.systemui.power;
18
19import android.content.BroadcastReceiver;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.database.ContentObserver;
25import android.os.BatteryManager;
26import android.os.Handler;
27import android.os.PowerManager;
28import android.os.SystemClock;
29import android.os.UserHandle;
30import android.provider.Settings;
31import android.util.Log;
32import android.util.Slog;
33
34import com.android.systemui.SystemUI;
35import com.android.systemui.statusbar.phone.PhoneStatusBar;
36
37import java.io.FileDescriptor;
38import java.io.PrintWriter;
39import java.util.Arrays;
40
41public 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
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
79    private void setSaverMode(boolean mode) {
80        mWarnings.showSaverMode(mode);
81    }
82
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     */
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
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
147        private void updateSaverMode() {
148            setSaverMode(mPowerManager.isPowerSaveMode());
149        }
150
151        @Override
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
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 {
253        void update(int batteryLevel, int bucket, long screenOffTime);
254        void showSaverMode(boolean mode);
255        void dismissLowBatteryWarning();
256        void showLowBatteryWarning(boolean playSound);
257        void dismissInvalidChargerWarning();
258        void showInvalidChargerWarning();
259        void updateLowBatteryWarning();
260        boolean isInvalidChargerWarningShowing();
261        void dump(PrintWriter pw);
262        void userSwitched();
263    }
264}
265
266