关机流程详解

### 关机流程

#### 背景

常见的关机方式有常按电源键,下拉菜单栏点关机或者重启,或者adb命令关机,本质这几个最终都会调用ShutdownThread.reboot或者shutdown方法来关机或者重启。

#### 具体流程

##### 常按电源关机流程

常按电源后会产生按键事件,InputReaderThread从EventHub读到此事件后会交给InputDispather.cpp再继续往上报

`frameworks/native/services/inputflinger/InputDispatcher.cpp`

```c++
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {

mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);

}
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
      : mPolicy(policy),
    //省略
```

这个mPolicy指的就是`com_android_server_input_InputManagerService.cpp`的NativeInputManager

`frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp`

```c++
class NativeInputManager : 
    public virtual InputDispatcherPolicyInterface{
        //省略
    }
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
 //...
        jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
        jint wmActions;
        if (keyEventObj) {
            wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
            if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
                wmActions = 0;
            }
            android_view_KeyEvent_recycle(env, keyEventObj);
            env->DeleteLocalRef(keyEventObj);
        } else {
            ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
            wmActions = 0;
        }
//...
}

int register_android_server_InputManager(JNIEnv* env) {
    //...
    jclass clazz;
    FIND_CLASS(clazz, "com/android/server/input/InputManagerService");
    gServiceClassInfo.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
     //...
}
 
```

可以看到调用了gServiceClassInfo.interceptKeyBeforeQueueing,其中gServiceClassInfo即是java层的InputManagerService,所以这里会调用到InputManagerService.interceptKeyBeforeQueueing,相当于从JNI层调用到java层。这里不止点电源键是这样的流程,所有的key事件,比如点home,back,或者手动触发的keycode事件,都会走这个流程。

`InputManagerService.java`

```java
// Native callback.
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}
```

`InputManagerCallback.java`

```java
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
}
```

`PhoneWindowManager.java`

```java
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
//...
    case KeyEvent.KEYCODE_POWER: {
        cancelPendingAccessibilityShortcutAction();
        result &= ~ACTION_PASS_TO_USER;
        isWakeKey = false; // wake-up will be handled separately
        if (down) {
            interceptPowerKeyDown(event, interactive);
        } else {
            interceptPowerKeyUp(event, interactive, canceled);
        }
        break;
    }
//...
}
```

按下电源后会有几种场景,比如熄屏,弹出是否关机或重启,直接关机等。所以interceptPowerKeyDown会去做这些逻辑。

```java
 private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
 //...
     if (hasLongPressOnPowerBehavior()) {
         if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
             powerLongPress();
         } else {
             Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
             msg.setAsynchronous(true);
             mHandler.sendMessageDelayed(msg,
                                         ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());

             if (hasVeryLongPressOnPowerBehavior()) {
                 Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
                 longMsg.setAsynchronous(true);
                 mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
             }
         }
     }
//...
}
  private class PolicyHandler extends Handler {
      //...
        case MSG_POWER_LONG_PRESS:
                    powerLongPress();
                    break;
      //...
  }
private void powerLongPress() {
        final int behavior = getResolvedLongPressOnPowerBehavior();
        switch (behavior) {
            case LONG_PRESS_POWER_NOTHING:
                break;
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                        "Power - Long Press - Global Actions");
                showGlobalActionsInternal();
                break;
//...
}
void showGlobalActionsInternal() {
        if (mGlobalActions == null) {
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
        }
        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
        // since it took two seconds of long press to bring this up,
        // poke the wake lock so they have some time to see the dialog.
        mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
```

发了个MSG_POWER_LONG_PRESS事件到hander处理,之后powerLongPress()->showGlobalActionsInternal()->mGlobalActions.showDialog()。

`LegacyGlobalActions`

```java
 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
        mKeyguardShowing = keyguardShowing;
        mDeviceProvisioned = isDeviceProvisioned;
        if (mDialog != null) {
            mDialog.dismiss();
            mDialog = null;
            // Show delayed, so that the dismiss of the previous dialog completes
            mHandler.sendEmptyMessage(MESSAGE_SHOW);
        } else {
            handleShow();
        }
    }
    private void handleShow() {
        awakenIfNecessary();
        mDialog = createDialog();
        prepareDialog();

        // If we only have 1 item and it's a simple press action, just do this action.
        if (mAdapter.getCount() == 1
                && mAdapter.getItem(0) instanceof SinglePressAction
                && !(mAdapter.getItem(0) instanceof LongPressAction)) {
            ((SinglePressAction) mAdapter.getItem(0)).onPress();
        } else {
            if (mDialog != null) {
                WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
                attrs.setTitle("LegacyGlobalActions");
                mDialog.getWindow().setAttributes(attrs);
                mDialog.show();
                mDialog.getWindow().getDecorView().setSystemUiVisibility(
                        View.STATUS_BAR_DISABLE_EXPAND);
            }
        }
    }
   private ActionsDialog createDialog() {
       //...
        if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
                mItems.add(new PowerAction(mContext, mWindowManagerFuncs));
            }
       //...
   }
```

showDialog最终都会调到handleShow,这里会根据actionKey创建相应的dialog,这里是电源的长按事件,会创建PowerAction,并执行其onPress方法

`PowerAction`

```java
public final class PowerAction extends SinglePressAction implements LongPressAction {
    @Override
    public void onPress() {
        // shutdown by making sure radio and power are handled accordingly.
        mWindowManagerFuncs.shutdown(false /* confirm */);
    }
}
```

执行wms的shutdown方法

`WindowManagerService`

```java
    @Override
    public void shutdown(boolean confirm) {
        // Pass in the UI context, since ShutdownThread requires it (to show UI).
        ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(),
                PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
    }
```

终于到主角ShutdownThread.shutdown方法。

##### 下拉菜单栏点关机或重启

这种一般是厂商自己定制ui,屏幕上点关机后弹出是否确定关机或者重启,点确定关机后发送广播

```java
    private void rebootDevice(){
        Intent localIntent = new Intent("android.intent.action.REBOOT");
        localIntent.putExtra("android.intent.extra.KEY_CONFIRM", false);
        localIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivity(localIntent);
    }
```

ShutdownActivity会来处理此action

```xml
<activity android:name="com.android.internal.app.ShutdownActivity"
    android:permission="android.permission.SHUTDOWN"
    android:theme="@style/Theme.NoDisplay"
    android:excludeFromRecents="true">
    <intent-filter>
        <action android:name="android.intent.action.REBOOT" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
```

`ShutdownActivity`

```java
public class ShutdownActivity extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//...
        Thread thr = new Thread("ShutdownActivity") {
            @Override
            public void run() {
                IPowerManager pm = IPowerManager.Stub.asInterface(
                        ServiceManager.getService(Context.POWER_SERVICE));
                try {
                    if (mReboot) {
                        pm.reboot(mConfirm, null, false);
                    } else {
                        pm.shutdown(mConfirm, reason, false);
                    }
                } catch (RemoteException e) {
                }
            }
        };
        thr.start();
        finish();
    }
}
```

根据是否重启调用了PowerManagerManager.reboot或者shutdown

`PowerManagerService`

```java
@Override // Binder call
public void shutdown(boolean confirm, String reason, boolean wait) {
    try {
        shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}
  private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
            @Nullable final String reason, boolean wait) {
      //...
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
                        ShutdownThread.rebootSafeMode(getUiContext(), confirm);
                    } else if (haltMode == HALT_MODE_REBOOT) {
                        ShutdownThread.reboot(getUiContext(), reason, confirm);
                    } else {
                        ShutdownThread.shutdown(getUiContext(), reason, confirm);
                    }
                }
            }
        };

        // ShutdownThread must run on a looper capable of displaying the UI.
        Message msg = Message.obtain(UiThread.getHandler(), runnable);
        msg.setAsynchronous(true);
        UiThread.getHandler().sendMessage(msg);
      //...
  }
```

看到最终还是ShutdownThread.shutdown或者reboot。

##### Framework层shutdown流程

`ShutdownThread`

```java
public static void shutdown(final Context context, String reason, boolean confirm) {
    mReboot = false;
    mRebootSafeMode = false;
    mReason = reason;
    shutdownInner(context, confirm);
}
  private static void shutdownInner(final Context context, boolean confirm) {
  //...
      if (confirm) {
          final CloseDialogReceiver closer = new CloseDialogReceiver(context);
          if (sConfirmDialog != null) {
              sConfirmDialog.dismiss();
          }
          sConfirmDialog = new AlertDialog.Builder(context)
              .setTitle(mRebootSafeMode
                        ? com.android.internal.R.string.reboot_safemode_title
                        : com.android.internal.R.string.power_off)
              .setMessage(resourceId)
              .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                  public void onClick(DialogInterface dialog, int which) {
                      beginShutdownSequence(context);
                  }
              })
              .setNegativeButton(com.android.internal.R.string.no, null)
              .create();
          closer.dialog = sConfirmDialog;
          sConfirmDialog.setOnDismissListener(closer);
          sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
          sConfirmDialog.show();
      } else {
          beginShutdownSequence(context);
      }
}
```

这里confirm一般为false,代表是否需要再确认。最终都会到beginShutdownSequence去真正执行关机

```java
  private static void beginShutdownSequence(Context context) {
        // ...
        //关机进度的弹框,不允许cancel
        sInstance.mProgressDialog = showShutdownDialog(context);
        sInstance.mContext = context;
        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
      //...
        // start the thread that initiates shutdown
        sInstance.mHandler = new Handler() {
        };
        sInstance.start();
    }
   private static final ShutdownThread sInstance = new ShutdownThread();
```

弹出关机进度的弹框,并启动新线程ShutdownThread,执行start后到ShutdownThread.run

```java
    public void run() {
 //...
        //发送关机广播
        // First send the high-level shut down broadcast.
        mActionDone = false;
        Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        mContext.sendOrderedBroadcastAsUser(intent,
                UserHandle.ALL, null, br, mHandler, 0, null, null);

 //...
        //关闭ams
        final IActivityManager am =
                IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
        if (am != null) {
            try {
                am.shutdown(MAX_BROADCAST_TIME);
            } catch (RemoteException e) {
            }
        }
 //...
        //关闭pms
        final PackageManagerService pm = (PackageManagerService)
            ServiceManager.getService("package");
        if (pm != null) {
            pm.shutdown();
        }
 //...
        // Shutdown radios.
        shutdownRadios(MAX_RADIO_WAIT_TIME);
   //...
        // Remaining work will be done by init, including vold shutdown
        rebootOrShutdown(mContext, mReboot, mReason);
    }
```

run里还是做了很多清理工作,比如关了ams和pms,最后执行rebootOrShutdown

```java
 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
      //...震动 记录关机或重启日志 
        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown(reason);
    }
    public static void lowLevelShutdown(String reason) {
        if (reason == null) {
            reason = "";
        }
        SystemProperties.set("sys.powerctl", "shutdown," + reason);
    }
```

只是往`sys.powerctl`系统属性里设置了shutdown,+reason,重启同理,传的是reboot,+reason。

##### Native层shutdown流程

当sys.powerctl系统属性改变时,会触发property_changed事件

`system/core/init/init.cpp`

```c++
void PropertyChanged(const std::string& name, const std::string& value) {
    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
    // if there are other pending events to process or if init is waiting on an exec service or
    // waiting on a property.
    // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
    // commands to be executed.
    if (name == "sys.powerctl") {
        trigger_shutdown(value);
    }

    if (property_triggers_enabled) {
        ActionManager::GetInstance().QueuePropertyChange(name, value);
        WakeMainInitThread();
    }

    prop_waiter_state.CheckAndResetWait(name, value);
}

 trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
static class ShutdownState {
  public:
    void TriggerShutdown(const std::string& command) {
        // We can't call HandlePowerctlMessage() directly in this function,
        // because it modifies the contents of the action queue, which can cause the action queue
        // to get into a bad state if this function is called from a command being executed by the
        // action queue.  Instead we set this flag and ensure that shutdown happens before the next
        // command is run in the main init loop.
        auto lock = std::lock_guard{shutdown_command_lock_};
        shutdown_command_ = command;
        do_shutdown_ = true;
        WakeMainInitThread();
    }

    std::optional<std::string> CheckShutdown() {
        auto lock = std::lock_guard{shutdown_command_lock_};
        if (do_shutdown_ && !IsShuttingDown()) {
            return shutdown_command_;
        }
        return {};
    }
} shutdown_state;
```

trigger_shutdown调用到shutdown_state.TriggerShutdown,这里注释写了没有直接调用HandlePowerctlMessage方法,因为这样可能会导致状态混乱,但是再下个命令执行前会执行这个方法。

`system/core/init/reboot.cpp`

```c++
void HandlePowerctlMessage(const std::string& command) {
    //...
    // Queue built-in shutdown_done
    auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
        DoReboot(cmd, command, reboot_target, run_fsck);
        return Result<void>{};
    };
 //...
}
```

最终调到DoReboot去真正关机

```c++
static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,
                     bool run_fsck) {
   // ...

    // optional shutdown step
    // 1. terminate all services except shutdown critical ones. wait for delay to finish
    if (shutdown_timeout > 0ms) {
        StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);
    }
    // 2 Send SIGKILL to ones that didn't terminate cleanly.
    StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */);
    SubcontextTerminate();
    // Reap subcontext pids.
    ReapAnyOutstandingChildren();

    // 3. send volume abort_fuse and volume shutdown to vold
    Service* vold_service = ServiceList::GetInstance().FindService("vold");
    if (vold_service != nullptr && vold_service->IsRunning()) {
        // Manually abort FUSE connections, since the FUSE daemon is already dead
        // at this point, and unmounting it might hang.
        CallVdc("volume", "abort_fuse");
        CallVdc("volume", "shutdown");
        vold_service->Stop();
    } else {
        LOG(INFO) << "vold not running, skipping vold shutdown";
    }
    // logcat stopped here
    StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);
    // 4. sync, try umount, and optionally run fsck for user shutdown
    {
        Timer sync_timer;
        LOG(INFO) << "sync() before umount...";
        sync();
        LOG(INFO) << "sync() before umount took" << sync_timer;
    }
    // 5. drop caches and disable zram backing device, if exist
    KillZramBackingDevice();

    LOG(INFO) << "Ready to unmount apexes. So far shutdown sequence took " << t;
    // 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);
    // Follow what linux shutdown is doing: one more sync with little bit delay
    {
        Timer sync_timer;
        LOG(INFO) << "sync() after umount...";
        sync();
        LOG(INFO) << "sync() after umount took" << sync_timer;
    }
    if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
    LogShutdownTime(stat, &t);

    // Send signal to terminate reboot monitor thread.
    reboot_monitor_run = false;
    sem_post(&reboot_semaphore);

    // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
    RebootSystem(cmd, reboot_target);
    abort();
}

```

注释写的还是比较清晰的,整理下:  

1.向所有services发送terminate信号, 并等待其退出

2.如果services没有主动退出, 则发送kill信号, 强制杀掉service

3.给vold发送shutdown命令, 主动卸载挂载的设备,例如SD卡

4.执行sync操作,同步文件系统

5.调用kernel reboot系统调用

`system/core/init/reboot_utils.cpp`

```c++
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
    switch (cmd) {
        case ANDROID_RB_POWEROFF:
            reboot(RB_POWER_OFF);
            break;
    }
//...
}
```

##### Kernel层处理reboot流程

没找到代码。

1. kernel_restart_prepare

​       这里面会调用device_shutdown, 运行每个设备驱动注册的shutdown回调, 把驱动给停掉

2. syscore_shutdown

​        调用注册的syscore的shutdown回调。 syscore作为低功耗流程的一部分,其涉及的文件主要有syscore_ops.h和syscore.c,这一级别的回调函数是在完全屏蔽中断的场景下进行的。

3. machine_restart

   不同cpu架构可能不同,向其他cpu 发送ipi中断, 让其他cpu 都停止。其他cpu 收到对应的终端后, 会进入wfe模式。

主要关闭所有驱动和cpu,完成真正的关机。

#### 总结

1. 不管用户是长按电源键还是点厂商的关机图标等,一般会先弹框让用户确认,确定后最终都会调到ShutdownThread.shutdown或者reboot方法

2. 启动新线程后先发送关机广播,震动,并关闭ams,pms等,再调用powermanagerservice的lowLevelShutdown,这里只是往sys.powerctl系统属性中写入shutdown或者reboot参数
3. jni层会监听这个属性的改变,一旦改变会调用PropertyChanged方法,最终调用DoReboot去关机,这里会把所有的服务关闭,卸载挂载的设备,比如sd卡等,最后调用RebootSystem到kernel层去真正关机
4. kernel层会调用kernel_restart_prepare和machine_restart等去关闭驱动和cpu等,完成真正的关机。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值