private void shutdownIfNoPowerLocked() {
385 // shut down gracefully if our battery is critically low and we are not powered.
386 // wait until the system has booted before attempting to display the shutdown dialog.
387 if (shouldShutdownLocked()) {
388 mHandler.post(new Runnable() {
389 @Override
390 public void run() {
391 if (mActivityManagerInternal.isSystemReady()) {
392 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
393 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
394 intent.putExtra(Intent.EXTRA_REASON,
395 PowerManager.SHUTDOWN_LOW_BATTERY);
396 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
397 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
398 }
399 }
400 });
401 }
402 }
//判断电量
348 private boolean shouldSendBatteryLowLocked() {
349 final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
350 final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
351
352 /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
353 * - is just un-plugged (previously was plugged) and battery level is
354 * less than or equal to WARNING, or
355 * - is not plugged and battery level falls to WARNING boundary
356 * (becomes <= mLowBatteryWarningLevel).
357 */
358 return !plugged
359 && mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
360 && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel
361 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
362 }
如果当前电量小于警告电量(在config.xml中 <integer name="config_lowBatteryWarningLevel">15</integer>)则弹出电量低提示,或者电量为0(当然这个有误差也可能是5%时就自动关机)时自动关机。如果低电量的话就发送一个广播出去:
631 if (shouldSendBatteryLowLocked()) {
632 mSentLowBatteryBroadcast = true;
633 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
634 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
635 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
636 mHandler.post(new Runnable() {
637 @Override
638 public void run() {
639 mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
640 }
641 });
下面 这段代码是电量过低而自动关机:
384 private void shutdownIfNoPowerLocked() {
385 // shut down gracefully if our battery is critically low and we are not powered.
386 // wait until the system has booted before attempting to display the shutdown dialog.
387 if (shouldShutdownLocked()) {
388 mHandler.post(new Runnable() {
389 @Override
390 public void run() {
391 if (mActivityManagerInternal.isSystemReady()) {
392 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
393 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
394 intent.putExtra(Intent.EXTRA_REASON,
395 PowerManager.SHUTDOWN_LOW_BATTERY);
396 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
397 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
398 }
399 }
400 });
401 }
402 }
PowerUI
@Override
253 public void onReceive(Context context, Intent intent) {
254 String action = intent.getAction();
255 if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
256 ThreadUtils.postOnBackgroundThread(() -> {
257 if (mPowerManager.isPowerSaveMode()) {
258 mWarnings.dismissLowBatteryWarning();
259 }
260 });
261 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
262 mHasReceivedBattery = true;
263 final int oldBatteryLevel = mBatteryLevel;
264 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
265 final int oldBatteryStatus = mBatteryStatus;
266 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
267 BatteryManager.BATTERY_STATUS_UNKNOWN);
268 final int oldPlugType = mPlugType;
269 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
270 final int oldInvalidCharger = mInvalidCharger;
271 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
272 mLastBatteryStateSnapshot = mCurrentBatteryStateSnapshot;
273
274 final boolean plugged = mPlugType != 0;
275 final boolean oldPlugged = oldPlugType != 0;
276
277 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
278 int bucket = findBatteryLevelBucket(mBatteryLevel);
279
280 if (DEBUG) {
281 Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
282 + " .. " + mLowBatteryReminderLevels[0]
283 + " .. " + mLowBatteryReminderLevels[1]);
284 Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
285 Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
286 Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
287 Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
288 Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
289 Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
290 }
291
292 mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
293 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
294 Slog.d(TAG, "showing invalid charger warning");
295 mWarnings.showInvalidChargerWarning();
296 return;
297 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
298 mWarnings.dismissInvalidChargerWarning();
299 } else if (mWarnings.isInvalidChargerWarningShowing()) {
300 // if invalid charger is showing, don't show low battery
301 if (DEBUG) {
302 Slog.d(TAG, "Bad Charger");
303 }
304 return;
305 }
306
307 // Show the correct version of low battery warning if needed
308 if (mLastShowWarningTask != null) {
309 mLastShowWarningTask.cancel(true);
310 if (DEBUG) {
311 Slog.d(TAG, "cancelled task");
312 }
313 }
314 mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
315 maybeShowBatteryWarningV2(
316 plugged, bucket);
317 });
318
319 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
320 mScreenOffTime = SystemClock.elapsedRealtime();
321 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
322 mScreenOffTime = -1;
323 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
324 mWarnings.userSwitched();
325 } else {
326 Slog.w(TAG, "unknown intent: " + intent);
327 }
328 }
329 }
330
maybeShowBatteryWarningV2
mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
362 if (mCurrentBatteryStateSnapshot.isHybrid()) {
363 maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
364 } else {
365 maybeShowBatteryWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
366 }
367 }
368
476 protected void maybeShowBatteryWarning(
477 BatteryStateSnapshot currentSnapshot,
478 BatteryStateSnapshot lastSnapshot) {
479 final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
480 || lastSnapshot.getPlugged();
481
482 if (shouldShowLowBatteryWarning(currentSnapshot, lastSnapshot)) {
483 mWarnings.showLowBatteryWarning(playSound);
484 } else if (shouldDismissLowBatteryWarning(currentSnapshot, lastSnapshot)) {
485 mWarnings.dismissLowBatteryWarning();
486 } else {
487 mWarnings.updateLowBatteryWarning();
488 }
489 }
490
491 @VisibleForTesting
492 boolean shouldShowLowBatteryWarning(
493 BatteryStateSnapshot currentSnapshot,
494 BatteryStateSnapshot lastSnapshot) {
495 return !currentSnapshot.getPlugged()
496 && !currentSnapshot.isPowerSaver()
497 && (((currentSnapshot.getBucket() < lastSnapshot.getBucket()
498 || lastSnapshot.getPlugged())
499 && currentSnapshot.getBucket() < 0))
500 && currentSnapshot.getBatteryStatus() != BatteryManager.BATTERY_STATUS_UNKNOWN;
501 }
protected void showWarningNotification() {
255 final String percentage = NumberFormat.getPercentInstance()
256 .format((double) mCurrentBatterySnapshot.getBatteryLevel() / 100.0);
257
258 // get shared standard notification copy
259 String title = mContext.getString(R.string.battery_low_title);
260 String contentText;
261
262 // get correct content text if notification is hybrid or not
263 if (mCurrentBatterySnapshot.isHybrid()) {
264 contentText = getHybridContentString(percentage);
265 } else {
266 contentText = mContext.getString(R.string.battery_low_percent_format, percentage);
267 }
268
269 final Notification.Builder nb =
270 new Notification.Builder(mContext, NotificationChannels.BATTERY)
271 .setSmallIcon(R.drawable.ic_power_low)
272 // Bump the notification when the bucket dropped.
273 .setWhen(mWarningTriggerTimeMs)
274 .setShowWhen(false)
275 .setContentText(contentText)
276 .setContentTitle(title)
277 .setOnlyAlertOnce(true)
278 .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_WARNING))
279 .setStyle(new Notification.BigTextStyle().bigText(contentText))
280 .setVisibility(Notification.VISIBILITY_PUBLIC);
281 if (hasBatterySettings()) {
282 nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
283 }
284 // Make the notification red if the percentage goes below a certain amount or the time
285 // remaining estimate is disabled
286 if (!mCurrentBatterySnapshot.isHybrid() || mBucket < 0
287 || mCurrentBatterySnapshot.getTimeRemainingMillis()
288 < mCurrentBatterySnapshot.getSevereThresholdMillis()) {
289 nb.setColor(Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorError));
290 }
291
292 if (!mPowerMan.isPowerSaveMode()) {
293 nb.addAction(0,
294 mContext.getString(R.string.battery_saver_start_action),
295 pendingBroadcast(ACTION_START_SAVER));
296 }
297 nb.setOnlyAlertOnce(!mPlaySound);
298 mPlaySound = false;
299 SystemUI.overrideNotificationAppName(mContext, nb, false);
300 final Notification n = nb.build();
301 mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, UserHandle.ALL);
302 mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
303 }