Home | History | Annotate | Download | only in power
      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 
     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