1. 项目背景
在项目的开发过程中,往往会遇到这样的问题:Android系统关机慢,关机过程中黑屏时间长。
2. 代码流程
(1) PowerManagerService.java
/**
* Reboots the device.
*
* @param confirm If true, shows a reboot confirmation dialog.
* @param reason The reason for the reboot, or null if none.
* @param wait If true, this call waits for the reboot to complete and does not return.
*/
@Override // Binder call
public void reboot(boolean confirm, @Nullable String reason, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
if (PowerManager.REBOOT_RECOVERY.equals(reason)
|| PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
}
ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
3736 private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
3737 @Nullable final String reason, boolean wait) {
3738 if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
3739 if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
3740 throw new UnsupportedOperationException(
3741 "Attempted userspace reboot on a device that doesn't support it");
3742 }
3743 UserspaceRebootLogger.noteUserspaceRebootWasRequested();
3744 }
3745 if (mHandler == null || !mSystemReady) {
3746 if (RescueParty.isAttemptingFactoryReset()) {
3747 // If we're stuck in a really low-level reboot loop, and a
3748 // rescue party is trying to prompt the user for a factory data
3749 // reset, we must GET TO DA CHOPPA!
3750 // No check point from ShutdownCheckPoints will be dumped at this state.
3751 PowerManagerService.lowLevelReboot(reason);
3752 } else {
3753 throw new IllegalStateException("Too early to call shutdown() or reboot()");
3754 }
3755 }
3756
3757 Runnable runnable = new Runnable() {
3758 @Override
3759 public void run() {
3760 synchronized (this) {
3761 if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
3762 ShutdownThread.rebootSafeMode(getUiContext(), confirm);
3763 } else if (haltMode == HALT_MODE_REBOOT) {
3764 ShutdownThread.reboot(getUiContext(), reason, confirm);
3765 } else {
3766 ShutdownThread.shutdown(getUiContext(), reason, confirm);
3767 }
3768 }
3769 }
3770 };
3771
3772 // ShutdownThread must run on a looper capable of displaying the UI.
3773 Message msg = Message.obtain(UiThread.getHandler(), runnable);
3774 msg.setAsynchronous(true);
3775 UiThread.getHandler().sendMessage(msg);
3776
3777 // PowerManager.reboot() is documented not to return so just wait for the inevitable.
3778 if (wait) {
3779 synchronized (runnable) {
3780 while (true) {
3781 try {
3782 runnable.wait();
3783 } catch (InterruptedException e) {
3784 }
3785 }
3786 }
3787 }
3788 }
230 /**
231 * Request a clean shutdown, waiting for subsystems to clean up their
232 * state etc. Must be called from a Looper thread in which its UI
233 * is shown.
234 *
235 * @param context Context used to display the shutdown progress dialog. This must be a context
236 * suitable for displaying UI (aka Themable).
237 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
238 * @param confirm true if user confirmation is needed before shutting down.
239 */
240 public static void reboot(final Context context, String reason, boolean confirm) {
241 mReboot = true;
242 mRebootSafeMode = false;
243 mRebootHasProgressBar = false;
244 mReason = reason;
245 shutdownInner(context, confirm);
246 }
private static void shutdownInner(final Context context, boolean confirm)
private static void beginShutdownSequence(Context context)
/**
424 * Makes sure we handle the shutdown gracefully.
425 * Shuts off power regardless of radio state if the allotted time has passed.
426 */
427 public void run() {
428 TimingsTraceLog shutdownTimingLog = newTimingsLog();
429 shutdownTimingLog.traceBegin("SystemServerShutdown");
430 metricShutdownStart();
431 metricStarted(METRIC_SYSTEM_SERVER);
432
433 // Start dumping check points for this shutdown in a separate thread.
434 Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread(
435 new File(CHECK_POINTS_FILE_BASENAME));
436 dumpCheckPointsThread.start();
437
438 BroadcastReceiver br = new BroadcastReceiver() {
439 @Override public void onReceive(Context context, Intent intent) {
440 // We don't allow apps to cancel this, so ignore the result.
441 actionDone();
442 }
443 };
444
445 /*
446 * Write a system property in case the system_server reboots before we
447 * get to the actual hardware restart. If that happens, we'll retry at
448 * the beginning of the SystemServer startup.
449 */
450 {
451 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
452 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
453 }
454
455 /*
456 * If we are rebooting into safe mode, write a system property
457 * indicating so.
458 */
459 if (mRebootSafeMode) {
460 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
461 }
462
463 shutdownTimingLog.traceBegin("DumpPreRebootInfo");
464 try {
465 Slog.i(TAG, "Logging pre-reboot information...");
466 PreRebootLogger.log(mContext);
467 } catch (Exception e) {
468 Slog.e(TAG, "Failed to log pre-reboot information", e);
469 }
470 shutdownTimingLog.traceEnd(); // DumpPreRebootInfo
471
472 metricStarted(METRIC_SEND_BROADCAST);
473 shutdownTimingLog.traceBegin("SendShutdownBroadcast");
474 Log.i(TAG, "Sending shutdown broadcast...");
475
476 // First send the high-level shut down broadcast.
477 mActionDone = false;
478 Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
479 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
480 mContext.sendOrderedBroadcastAsUser(intent,
481 UserHandle.ALL, null, br, mHandler, 0, null, null);
482
483 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
484 synchronized (mActionDoneSync) {
485 while (!mActionDone) {
486 long delay = endTime - SystemClock.elapsedRealtime();
487 if (delay <= 0) {
488 Log.w(TAG, "Shutdown broadcast timed out");
489 break;
490 } else if (mRebootHasProgressBar) {
491 int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
492 BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
493 sInstance.setRebootProgress(status, null);
494 }
495 try {
496 mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
497 } catch (InterruptedException e) {
498 }
499 }
500 }
501 if (mRebootHasProgressBar) {
502 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
503 }
504 shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
505 metricEnded(METRIC_SEND_BROADCAST);
506
507 Log.i(TAG, "Shutting down activity manager...");
508 shutdownTimingLog.traceBegin("ShutdownActivityManager");
509 metricStarted(METRIC_AM);
510
511 final IActivityManager am =
512 IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
513 if (am != null) {
514 try {
515 am.shutdown(MAX_BROADCAST_TIME);
516 } catch (RemoteException e) {
517 }
518 }
519 if (mRebootHasProgressBar) {
520 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
521 }
522 shutdownTimingLog.traceEnd();// ShutdownActivityManager
523 metricEnded(METRIC_AM);
524
525 Log.i(TAG, "Shutting down package manager...");
526 shutdownTimingLog.traceBegin("ShutdownPackageManager");
527 metricStarted(METRIC_PM);
528
529 final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
530 if (pm != null) {
531 pm.shutdown();
532 }
533 if (mRebootHasProgressBar) {
534 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
535 }
536 shutdownTimingLog.traceEnd(); // ShutdownPackageManager
537 metricEnded(METRIC_PM);
538
539 // Shutdown radios.
540 shutdownTimingLog.traceBegin("ShutdownRadios");
541 metricStarted(METRIC_RADIOS);
542 shutdownRadios(MAX_RADIO_WAIT_TIME);
543 if (mRebootHasProgressBar) {
544 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
545 }
546 shutdownTimingLog.traceEnd(); // ShutdownRadios
547 metricEnded(METRIC_RADIOS);
548
549 if (mRebootHasProgressBar) {
550 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
551
552 // If it's to reboot to install an update and uncrypt hasn't been
553 // done yet, trigger it now.
554 uncrypt();
555 }
556
557 // Wait for the check points dump thread to finish, or kill it if not finished in time.
558 shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait");
559 try {
560 dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME);
561 } catch (InterruptedException ex) {
562 }
563 shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait
564
565 shutdownTimingLog.traceEnd(); // SystemServerShutdown
566 metricEnded(METRIC_SYSTEM_SERVER);
567 saveMetrics(mReboot, mReason);
568 // Remaining work will be done by init, including vold shutdown
569 rebootOrShutdown(mContext, mReboot, mReason);
570 }
672 /**
673 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
674 * or {@link #shutdown(Context, String, boolean)} instead.
675 *
676 * @param context Context used to vibrate or null without vibration
677 * @param reboot true to reboot or false to shutdown
678 * @param reason reason for reboot/shutdown
679 */
680 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
681 if (reboot) {
682 Log.i(TAG, "Rebooting, reason: " + reason);
683 PowerManagerService.lowLevelReboot(reason);
684 Log.e(TAG, "Reboot failed, will attempt shutdown instead");
685 reason = null;
686 } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
687 // vibrate before shutting down
688 Vibrator vibrator = new SystemVibrator(context);
689 try {
690 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
691 } catch (Exception e) {
692 // Failure to vibrate shouldn't interrupt shutdown. Just log it.
693 Log.w(TAG, "Failed to vibrate during shutdown.", e);
694 }
695
696 // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
697 try {
698 Thread.sleep(SHUTDOWN_VIBRATE_MS);
699 } catch (InterruptedException unused) {
700 }
701 }
702 // Shutdown power
703 Log.i(TAG, "Performing low-level shutdown...");
704 PowerManagerService.lowLevelShutdown(reason);
705 }
发生关机广播之后,调用AMS的shutdown,然后调用PMS的shutdown,接着是shutdownRadios,最后又调回PowerManagerService.lowLevelShutdown。
4242 /**
4243 * Low-level function turn the device off immediately, without trying
4244 * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
4245 *
4246 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
4247 */
4248 public static void lowLevelShutdown(String reason) {
4249 if (reason == null) {
4250 reason = "";
4251 }
4252 SystemProperties.set("sys.powerctl", "shutdown," + reason);
4253 }
通过sys.powerctl 会调用Reboot.cpp的DoReboot方法。
//* Reboot / shutdown the system.
582 // cmd ANDROID_RB_* as defined in android_reboot.h
583 // reason Reason string like "reboot", "shutdown,userrequested"
584 // reboot_target Reboot target string like "bootloader". Otherwise, it should be an empty string.
585 // run_fsck Whether to run fsck after umount is done.
586 //
587 static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,
588 bool run_fsck) {
706 // optional shutdown step
707 // 1. terminate all services except shutdown critical ones. wait for delay to finish
711 // Send SIGKILL to ones that didn't terminate cleanly.
714 // Reap subcontext pids.
716
717 // 3. send volume abort_fuse and volume shutdown to vold
728 // logcat stopped here
729 StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);
730 // 4. sync, try umount, and optionally run fsck for user shutdown
737 // 5. drop caches and disable zram backing device, if exist
738 KillZramBackingDevice();
741 // 6. unmount active apexes, otherwise they might prevent clean unmount of /data.
747 // Follow what linux shutdown is doing: one more sync with little bit delay
756
757 // Send signal to terminate reboot monitor thread.
758 reboot_monitor_run = false;
759 sem_post(&reboot_semaphore);
761 // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
772 RebootSystem(cmd, reboot_target);
773 abort();
774 }
可以通过不杀logcat,看看耗时在哪里,通过注释下面的地方:
// logcat stopped here
StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);
最后通过上面的方法,发现是卡在了第6步的unmount 分区:
// 6. unmount active apexes, otherwise they might prevent clean unmount of /data.
if (auto ret = UnmountAllApexes(); !ret.ok()) {
LOG(ERROR) << ret.error();
}
UmountStat stat =
TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
3. 修改方案
umount分区的地方有timeout控制,可以通过减少这个timeout的时间做优化。最终通过修改shutdown_timeout来解决。