admin管理员组

文章数量:1597416

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来解决。

本文标签: 时间长过程中黑屏系统android