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(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
    141             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
    142             mContext.registerReceiver(this, filter, null, mHandler);
    143             updateSaverMode();
    144         }
    145 
    146         private void updateSaverMode() {
    147             setSaverMode(mPowerManager.isPowerSaveMode());
    148         }
    149 
    150         @Override
    151         public void onReceive(Context context, Intent intent) {
    152             String action = intent.getAction();
    153             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
    154                 final int oldBatteryLevel = mBatteryLevel;
    155                 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
    156                 final int oldBatteryStatus = mBatteryStatus;
    157                 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
    158                         BatteryManager.BATTERY_STATUS_UNKNOWN);
    159                 final int oldPlugType = mPlugType;
    160                 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
    161                 final int oldInvalidCharger = mInvalidCharger;
    162                 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
    163 
    164                 final boolean plugged = mPlugType != 0;
    165                 final boolean oldPlugged = oldPlugType != 0;
    166 
    167                 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
    168                 int bucket = findBatteryLevelBucket(mBatteryLevel);
    169 
    170                 if (DEBUG) {
    171                     Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
    172                             + " .. " + mLowBatteryReminderLevels[0]
    173                             + " .. " + mLowBatteryReminderLevels[1]);
    174                     Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
    175                     Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
    176                     Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
    177                     Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
    178                     Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
    179                     Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
    180                 }
    181 
    182                 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
    183                 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
    184                     Slog.d(TAG, "showing invalid charger warning");
    185                     mWarnings.showInvalidChargerWarning();
    186                     return;
    187                 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
    188                     mWarnings.dismissInvalidChargerWarning();
    189                 } else if (mWarnings.isInvalidChargerWarningShowing()) {
    190                     // if invalid charger is showing, don't show low battery
    191                     return;
    192                 }
    193 
    194                 if (!plugged
    195                         && (bucket < oldBucket || oldPlugged)
    196                         && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
    197                         && bucket < 0) {
    198                     // only play SFX when the dialog comes up or the bucket changes
    199                     final boolean playSound = bucket != oldBucket || oldPlugged;
    200                     mWarnings.showLowBatteryWarning(playSound);
    201                 } else if (plugged || (bucket > oldBucket && bucket > 0)) {
    202                     mWarnings.dismissLowBatteryWarning();
    203                 } else {
    204                     mWarnings.updateLowBatteryWarning();
    205                 }
    206             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
    207                 mScreenOffTime = SystemClock.elapsedRealtime();
    208             } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
    209                 mScreenOffTime = -1;
    210             } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
    211                 updateSaverMode();
    212             } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGING.equals(action)) {
    213                 setSaverMode(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
    214             } else {
    215                 Slog.w(TAG, "unknown intent: " + intent);
    216             }
    217         }
    218     };
    219 
    220     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    221         pw.print("mLowBatteryAlertCloseLevel=");
    222         pw.println(mLowBatteryAlertCloseLevel);
    223         pw.print("mLowBatteryReminderLevels=");
    224         pw.println(Arrays.toString(mLowBatteryReminderLevels));
    225         pw.print("mBatteryLevel=");
    226         pw.println(Integer.toString(mBatteryLevel));
    227         pw.print("mBatteryStatus=");
    228         pw.println(Integer.toString(mBatteryStatus));
    229         pw.print("mPlugType=");
    230         pw.println(Integer.toString(mPlugType));
    231         pw.print("mInvalidCharger=");
    232         pw.println(Integer.toString(mInvalidCharger));
    233         pw.print("mScreenOffTime=");
    234         pw.print(mScreenOffTime);
    235         if (mScreenOffTime >= 0) {
    236             pw.print(" (");
    237             pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
    238             pw.print(" ago)");
    239         }
    240         pw.println();
    241         pw.print("soundTimeout=");
    242         pw.println(Settings.Global.getInt(mContext.getContentResolver(),
    243                 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
    244         pw.print("bucket: ");
    245         pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
    246         mWarnings.dump(pw);
    247     }
    248 
    249     public interface WarningsUI {
    250         void update(int batteryLevel, int bucket, long screenOffTime);
    251         void showSaverMode(boolean mode);
    252         void dismissLowBatteryWarning();
    253         void showLowBatteryWarning(boolean playSound);
    254         void dismissInvalidChargerWarning();
    255         void showInvalidChargerWarning();
    256         void updateLowBatteryWarning();
    257         boolean isInvalidChargerWarningShowing();
    258         void dump(PrintWriter pw);
    259     }
    260 }
    261 
    262