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