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     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      */
    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 
    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
    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 
    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 {
    242         void update(int batteryLevel, int bucket, long screenOffTime);
    243         void dismissLowBatteryWarning();
    244         void showLowBatteryWarning(boolean playSound);
    245         void dismissInvalidChargerWarning();
    246         void showInvalidChargerWarning();
    247         void updateLowBatteryWarning();
    248         boolean isInvalidChargerWarningShowing();
    249         void dump(PrintWriter pw);
    250         void userSwitched();
    251     }
    252 }
    253 
    254