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 start()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 updateBatteryWarningLevels()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 */ findBatteryLevelBucket(int level)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 init()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 onReceive(Context context, Intent intent)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 dump(FileDescriptor fd, PrintWriter pw, String[] args)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 { update(int batteryLevel, int bucket, long screenOffTime)242 void update(int batteryLevel, int bucket, long screenOffTime); dismissLowBatteryWarning()243 void dismissLowBatteryWarning(); showLowBatteryWarning(boolean playSound)244 void showLowBatteryWarning(boolean playSound); dismissInvalidChargerWarning()245 void dismissInvalidChargerWarning(); showInvalidChargerWarning()246 void showInvalidChargerWarning(); updateLowBatteryWarning()247 void updateLowBatteryWarning(); isInvalidChargerWarningShowing()248 boolean isInvalidChargerWarningShowing(); dump(PrintWriter pw)249 void dump(PrintWriter pw); userSwitched()250 void userSwitched(); 251 } 252 } 253 254