PowerUI.java revision 3332ba54ae85df14d761447d86d2aa19d448ce11
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.net.Uri;
26import android.os.BatteryManager;
27import android.os.Handler;
28import android.os.PowerManager;
29import android.os.SystemClock;
30import android.os.UserHandle;
31import android.provider.Settings;
32import android.util.Slog;
33
34import com.android.systemui.SystemUI;
35
36import java.io.FileDescriptor;
37import java.io.PrintWriter;
38import java.util.Arrays;
39
40public class PowerUI extends SystemUI {
41    static final String TAG = "PowerUI";
42    static final boolean DEBUG = false;
43
44
45    private final Handler mHandler = new Handler();
46    private final SettingsObserver mObserver = new SettingsObserver(mHandler);
47    private final Receiver mReceiver = new Receiver();
48
49    private PowerManager mPowerManager;
50    private WarningsUI mWarnings;
51    private int mBatteryLevel = 100;
52    private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
53    private int mPlugType = 0;
54    private int mInvalidCharger = 0;
55
56    private int mLowBatteryAlertCloseLevel;
57    private final int[] mLowBatteryReminderLevels = new int[2];
58
59    private long mScreenOffTime = -1;
60
61    public void start() {
62        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
63        mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
64        mWarnings = new PowerNotificationWarnings(mContext);
65
66        ContentObserver obs = new ContentObserver(mHandler) {
67            @Override
68            public void onChange(boolean selfChange) {
69                updateBatteryWarningLevels();
70            }
71        };
72        final ContentResolver resolver = mContext.getContentResolver();
73        resolver.registerContentObserver(Settings.Global.getUriFor(
74                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
75                false, obs, UserHandle.USER_ALL);
76        updateBatteryWarningLevels();
77        mReceiver.init();
78        mObserver.init();
79    }
80
81    private void setSaverMode(boolean mode) {
82        mWarnings.showSaverMode(mode);
83    }
84
85    private void setSaverTrigger(int level) {
86        mWarnings.setSaverTrigger(level);
87    }
88
89    void updateBatteryWarningLevels() {
90        int critLevel = mContext.getResources().getInteger(
91                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
92
93        final ContentResolver resolver = mContext.getContentResolver();
94        int defWarnLevel = mContext.getResources().getInteger(
95                com.android.internal.R.integer.config_lowBatteryWarningLevel);
96        int warnLevel = Settings.Global.getInt(resolver,
97                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
98        if (warnLevel == 0) {
99            warnLevel = defWarnLevel;
100        }
101        if (warnLevel < critLevel) {
102            warnLevel = critLevel;
103        }
104
105        mLowBatteryReminderLevels[0] = warnLevel;
106        mLowBatteryReminderLevels[1] = critLevel;
107        mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
108                + mContext.getResources().getInteger(
109                        com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
110    }
111
112    /**
113     * Buckets the battery level.
114     *
115     * The code in this function is a little weird because I couldn't comprehend
116     * the bucket going up when the battery level was going down. --joeo
117     *
118     * 1 means that the battery is "ok"
119     * 0 means that the battery is between "ok" and what we should warn about.
120     * less than 0 means that the battery is low
121     */
122    private int findBatteryLevelBucket(int level) {
123        if (level >= mLowBatteryAlertCloseLevel) {
124            return 1;
125        }
126        if (level > mLowBatteryReminderLevels[0]) {
127            return 0;
128        }
129        final int N = mLowBatteryReminderLevels.length;
130        for (int i=N-1; i>=0; i--) {
131            if (level <= mLowBatteryReminderLevels[i]) {
132                return -1-i;
133            }
134        }
135        throw new RuntimeException("not possible!");
136    }
137
138    private final class Receiver extends BroadcastReceiver {
139
140        public void init() {
141            // Register for Intent broadcasts for...
142            IntentFilter filter = new IntentFilter();
143            filter.addAction(Intent.ACTION_BATTERY_CHANGED);
144            filter.addAction(Intent.ACTION_SCREEN_OFF);
145            filter.addAction(Intent.ACTION_SCREEN_ON);
146            filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
147            mContext.registerReceiver(this, filter, null, mHandler);
148            updateSaverMode();
149        }
150
151        private void updateSaverMode() {
152            setSaverMode(mPowerManager.isPowerSaveMode());
153        }
154
155        @Override
156        public void onReceive(Context context, Intent intent) {
157            String action = intent.getAction();
158            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
159                final int oldBatteryLevel = mBatteryLevel;
160                mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
161                final int oldBatteryStatus = mBatteryStatus;
162                mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
163                        BatteryManager.BATTERY_STATUS_UNKNOWN);
164                final int oldPlugType = mPlugType;
165                mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
166                final int oldInvalidCharger = mInvalidCharger;
167                mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
168
169                final boolean plugged = mPlugType != 0;
170                final boolean oldPlugged = oldPlugType != 0;
171
172                int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
173                int bucket = findBatteryLevelBucket(mBatteryLevel);
174
175                if (DEBUG) {
176                    Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
177                            + " .. " + mLowBatteryReminderLevels[0]
178                            + " .. " + mLowBatteryReminderLevels[1]);
179                    Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
180                    Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
181                    Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
182                    Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
183                    Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
184                    Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
185                }
186
187                mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
188                if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
189                    Slog.d(TAG, "showing invalid charger warning");
190                    mWarnings.showInvalidChargerWarning();
191                    return;
192                } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
193                    mWarnings.dismissInvalidChargerWarning();
194                } else if (mWarnings.isInvalidChargerWarningShowing()) {
195                    // if invalid charger is showing, don't show low battery
196                    return;
197                }
198
199                if (!plugged
200                        && (bucket < oldBucket || oldPlugged)
201                        && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
202                        && bucket < 0) {
203                    // only play SFX when the dialog comes up or the bucket changes
204                    final boolean playSound = bucket != oldBucket || oldPlugged;
205                    mWarnings.showLowBatteryWarning(playSound);
206                } else if (plugged || (bucket > oldBucket && bucket > 0)) {
207                    mWarnings.dismissLowBatteryWarning();
208                } else {
209                    mWarnings.updateLowBatteryWarning();
210                }
211            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
212                mScreenOffTime = SystemClock.elapsedRealtime();
213            } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
214                mScreenOffTime = -1;
215            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
216                updateSaverMode();
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 setSaverTrigger(int level);
255        void showSaverMode(boolean mode);
256        void dismissLowBatteryWarning();
257        void showLowBatteryWarning(boolean playSound);
258        void dismissInvalidChargerWarning();
259        void showInvalidChargerWarning();
260        void updateLowBatteryWarning();
261        boolean isInvalidChargerWarningShowing();
262        void dump(PrintWriter pw);
263    }
264
265    private final class SettingsObserver extends ContentObserver {
266        private final Uri LOW_POWER_MODE_TRIGGER_LEVEL_URI =
267                Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL);
268
269        public SettingsObserver(Handler handler) {
270            super(handler);
271        }
272
273        public void init() {
274            onChange(true, LOW_POWER_MODE_TRIGGER_LEVEL_URI);
275            final ContentResolver cr = mContext.getContentResolver();
276            cr.registerContentObserver(LOW_POWER_MODE_TRIGGER_LEVEL_URI, false, this);
277        }
278
279        @Override
280        public void onChange(boolean selfChange, Uri uri) {
281            if (LOW_POWER_MODE_TRIGGER_LEVEL_URI.equals(uri)) {
282                final int level = Settings.Global.getInt(mContext.getContentResolver(),
283                        Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
284                setSaverTrigger(level);
285            }
286        }
287    }
288}
289
290