android 關機流程詳細分析

前面的博客有提到過android事件上報流程,InputReaderThread 從EventHub讀到按鍵事件後,交給InputDispatcher 往上上報,我們從這裏跟蹤一下長按power鍵關鍵流程,

frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
…
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
…
}

這裏的mPolicy是NativeInputManager對象.

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

void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) {
…
  jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
886          jint wmActions;
887          if (keyEventObj) {
888              wmActions = env->CallIntMethod(mServiceObj,
889                      gServiceClassInfo.interceptKeyBeforeQueueing,
890                      keyEventObj, policyFlags);
891              if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
892                  wmActions = 0;
893              }
894              android_view_KeyEvent_recycle(env, keyEventObj);
895              env->DeleteLocalRef(keyEventObj);
896          } else {
897              ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
898              wmActions = 0;
899          }
…
}
這裏會call到java層,call 到InputManagerService裏面。

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

// Native callback.
1871      private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
1872          return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);

這個mWindowManagerCallbacks 是通過下面的接口設置的。

inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
繼續追查下去,就是一個InputMonitor 實例。

frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

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

這裏的mService就是WindowManagerService。

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

final WindowManagerPolicy mPolicy = new PhoneWindowManager();
所以這個mPolicy就是PhoneWindowManager對象。
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {

@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
   case KeyEvent.KEYCODE_POWER: {
5815                  result &= ~ACTION_PASS_TO_USER;
5816                  isWakeKey = false; // wake-up will be handled separately
5817                  if (down) {
5818                      interceptPowerKeyDown(event, interactive);
5819                  } else {
5820                      interceptPowerKeyUp(event, interactive, canceled);
5821                  }
5822                  break;
5823              }
}
繼續走到interceptPowerKeyDown()函數裏面。

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
...
if (interactive) {
1087                  // When interactive, we're already awake.
1088                  // Wait for a long press or for the button to be released to decide what to do.
1089                  if (hasLongPressOnPowerBehavior()) {
1090                      Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
1091                      msg.setAsynchronous(true);
1092                      mHandler.sendMessageDelayed(msg,
1093                              ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
1094                  }
1095              }
}
這裏面判斷是長按power鍵後會發一個MSG_POWER_LONG_PRESS消息出來,我們看一下它的處理。

private class PolicyHandler extends Handler {
...
746          @Override
747          public void handleMessage(Message msg) {
748              switch (msg.what) {
  case MSG_POWER_LONG_PRESS:
790                      powerLongPress();
791                      break;
}
}

  private void powerLongPress() {
1255          final int behavior = getResolvedLongPressOnPowerBehavior();
1256          switch (behavior) {
1257          case LONG_PRESS_POWER_NOTHING:
1258              break;
1259          case LONG_PRESS_POWER_GLOBAL_ACTIONS:
1260              mPowerKeyHandled = true;
1261              if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
1262                  performAuditoryFeedbackForAccessibilityIfNeed();
1263              }
1264              showGlobalActionsInternal();
1265              break;
1273          }
1274      }
接下來會走到showGlobalActionsInternal()函數裏面。

  void showGlobalActionsInternal() {
1389          sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
1390          if (mGlobalActions == null) {
1391              mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
1392          }
1393          final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
1394          mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
1395          if (keyguardShowing) {
1396              // since it took two seconds of long press to bring this up,
1397              // poke the wake lock so they have some time to see the dialog.
1398              mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
1399          }
1400      }

showDialog(keyguardShowing, isDeviceProvisioned());會彈出選擇界面。

frameworks/base/services/core/java/com/android/server/policy/GlobalActions.java

  public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
172          mKeyguardShowing = keyguardShowing;
173          mDeviceProvisioned = isDeviceProvisioned;
174          if (mDialog != null) {
175              mDialog.dismiss();
176              mDialog = null;
177              // Show delayed, so that the dismiss of the previous dialog completes
178              mHandler.sendEmptyMessage(MESSAGE_SHOW);
179          } else {
180              handleShow();
181          }
182      }

private void handleShow() {
197          awakenIfNecessary();
198          mDialog = createDialog();
199          prepareDialog();
200  
201          // If we only have 1 item and it's a simple press action, just do this action.
202          if (mAdapter.getCount() == 1
203                  && mAdapter.getItem(0) instanceof SinglePressAction
204                  && !(mAdapter.getItem(0) instanceof LongPressAction)) {
205              ((SinglePressAction) mAdapter.getItem(0)).onPress();
206          } else {
207              WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
208              attrs.setTitle("GlobalActions");
209              mDialog.getWindow().setAttributes(attrs);
210              mDialog.show();
211              mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
212          }
213      }
createDialog()裏面很重要的一點是創建了多項選項按鈕,並綁定了對應的處理函數。
private GlobalActionsDialog createDialog() {
  if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
281                  mItems.add(new PowerAction());
282              } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
283                  mItems.add(mAirplaneModeOn);
284              } 

}
關機對應的是PowerAction 。

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

如果點擊關機按鈕,就會走到onPress(),調用WindowManagerservice的shutdown接口。

 // Called by window manager policy.  Not exposed externally.
5823      @Override
5824      public void shutdown(boolean confirm) {
5825          ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
5826      }
frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java

    public static void shutdown(final Context context, String reason, boolean confirm) {
133          mReboot = false;
134          mRebootSafeMode = false;
135          mReason = reason;
136          shutdownInner(context, confirm);
137      }
 static void shutdownInner(final Context context, boolean confirm) {
...
159          if (confirm) {
160              final CloseDialogReceiver closer = new CloseDialogReceiver(context);
161              if (sConfirmDialog != null) {
162                  sConfirmDialog.dismiss();
163              }
164              sConfirmDialog = new AlertDialog.Builder(context)
165                      .setTitle(mRebootSafeMode
166                              ? com.android.internal.R.string.reboot_safemode_title
167                              : com.android.internal.R.string.power_off)
168                      .setMessage(resourceId)
169                      .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
170                          public void onClick(DialogInterface dialog, int which) {
171                              beginShutdownSequence(context);
172                          }
173                      })
174                      .setNegativeButton(com.android.internal.R.string.no, null)
175                      .create();
176              closer.dialog = sConfirmDialog;
177              sConfirmDialog.setOnDismissListener(closer);
178              sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
179              sConfirmDialog.show();
180          } else {
181              beginShutdownSequence(context);
182          }
183      }
如果confirm爲true,則彈出確認對話框,總之最終會走beginShutdownSequence(context) 進行接下來的關機流程。

 private static void beginShutdownSequence(Context context) {
...
276          if (PowerManager.REBOOT_RECOVERY_UPDATE.equals(mReason)) {
...
301          } else {
302              pd.setTitle(context.getText(com.android.internal.R.string.power_off));
303              pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
304              pd.setIndeterminate(true);
305          }
306          pd.setCancelable(false);
307          pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
308  
309          pd.show();
310  ...
341          // start the thread that initiates shutdown
342          sInstance.mHandler = new Handler() {
343          };
344          sInstance.start();
345      }
pd.show(); 顯示關機的進度界面, sInstance.start(); 會啓動另外一個線程,走接下來的關機流程。

private static final ShutdownThread sInstance = new ShutdownThread();

358      public void run() {
359         ...
384          Log.i(TAG, "Sending shutdown broadcast...");
385  
386          // First send the high-level shut down broadcast.
387          mActionDone = false;
388          Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
389          intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
390          mContext.sendOrderedBroadcastAsUser(intent,
391                  UserHandle.ALL, null, br, mHandler, 0, null, null);
392  
393        ...
497          rebootOrShutdown(mContext, mReboot, mReason);
498      }
ShutdownThread啓動之後,就會跑它的run()函數,做了很多工作,發送關機廣播,做一些狀態檢查和清理工作。

    public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
644          if (reboot) {
645              Log.i(TAG, "Rebooting, reason: " + reason);
646              PowerManagerService.lowLevelReboot(reason);
647              Log.e(TAG, "Reboot failed, will attempt shutdown instead");
648              reason = null;
649          } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
...
664          }
665  
666          // Shutdown power
667          Log.i(TAG, "Performing low-level shutdown...");
668          PowerManagerService.lowLevelShutdown(reason);
669      }

最後又跑到PowerManagerService 的lowLevelShutdown()。

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

    public static void lowLevelShutdown(String reason) {
2787          if (reason == null) {
2788              reason = "";
2789          }
2790          SystemProperties.set("sys.powerctl", "shutdown," + reason);
2791      }

很多人疑惑SystemProperties.set("sys.powerctl", "shutdown,"+ reason);又跑到了哪裏?



首先sys.powerctl 在init.rc中有配置,它是一類特殊的property,可以認爲是command。

它對應的處理函數定義在/system/core/init/builtins.cpp中。

     {"mount",                   {3,     kMax, do_mount}},
          {"umount",                  {1,     1,    do_umount}},
          {"powerctl",                {1,     1,    do_powerctl}},
          {"restart",                 {1,     1,    do_restart}},

所以接着會調到do_powerctl(),注意傳下來的參數有shutdown,所以cmd是ANDROID_RB_POWEROFF,reboot_target是上面傳的reason字符串。

 static int do_powerctl(const std::vector<std::string>& args) {
696      const char* command = args[1].c_str();
697      int len = 0;
698      unsigned int cmd = 0;
699      const char *reboot_target = "";
700      void (*callback_on_ro_remount)(const struct mntent*) = NULL;
701  
702      if (strncmp(command, "shutdown", 8) == 0) {
703          cmd = ANDROID_RB_POWEROFF;
704          len = 8;
705      } else if (strncmp(command, "reboot", 6) == 0) {
706          cmd = ANDROID_RB_RESTART2;
707          len = 6;
708      }
...
763      return android_reboot_with_callback(cmd, 0, reboot_target,
764                                          callback_on_ro_remount);
765  }

函數和cmd值在/system/core/include/cutils/android_reboot.h 中有聲明

int android_reboot_with_callback(
213      int cmd, int flags __unused, const char *arg,
214      void (*cb_on_remount)(const struct mntent*))
215  {
216      int ret;
217      remount_ro(cb_on_remount);
218      switch (cmd) {
219          case ANDROID_RB_RESTART:
220              ret = reboot(RB_AUTOBOOT);
221              break;
222  
223          case ANDROID_RB_POWEROFF:
224              ret = reboot(RB_POWER_OFF);
225              break;
226  
227          case ANDROID_RB_RESTART2:
228              ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
229                             LINUX_REBOOT_CMD_RESTART2, arg);
230              break;
231  
232          default:
233              ret = -1;
234      }
235  
236      return ret;
237  }

所以最後又調用到了reboot()函數,傳入的參數是RB_POWER_OFF。

reboot()定義在/bionic/libc/bionic/reboot.cpp

#include <unistd.h>
30  #include <sys/reboot.h>
31  
32  extern "C" int __reboot(int, int, int, void*);
33  
34  int reboot(int mode) {
35    return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);
36  }

又調用到了__reboot(),在__reboot.s中用匯編實現,不同的cpu架構在不同目錄下,比如32爲arm平臺。

bionic/libc/arch-arm/syscalls/__reboot.S

3  #include <private/bionic_asm.h>
4  
5  ENTRY(__reboot)
6      mov     ip, r7
7      .cfi_register r7, ip
8      ldr     r7, =__NR_reboot
9      swi     #0
10      mov     r7, ip
11      .cfi_restore r7
12      cmn     r0, #(MAX_ERRNO + 1)
13      bxls    lr
14      neg     r0, r0
15      b       __set_errno_internal
16  END(__reboot)

這裏又將__reboot的實現映射到了__NR_reboot。

bionic/libc/kernel/uapi/asm-generic/unistd.h

/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
219  #define __NR_rt_sigreturn 139
220  #define __NR_setpriority 140
221  #define __NR_getpriority 141
222  #define __NR_reboot 142

__NR_reboot對應的系統調用聲明在Unistd.h中

linux-4.10/include/uapi/asm-generic/unistd.h

 #define __NR_rt_sigreturn 139
419  __SC_COMP(__NR_rt_sigreturn, sys_rt_sigreturn, compat_sys_rt_sigreturn)
420  
421  /* kernel/sys.c */
422  #define __NR_setpriority 140
423  __SYSCALL(__NR_setpriority, sys_setpriority)
424  #define __NR_getpriority 141
425  __SYSCALL(__NR_getpriority, sys_getpriority)
426  #define __NR_reboot 142
427  __SYSCALL(__NR_reboot, sys_reboot)

對應的函數是sys_reboot(),聲明在linux-4.10\include\linux\syscalls.h


					struct timespec __user *interval);
311  asmlinkage long sys_setpriority(int which, int who, int niceval);
312  asmlinkage long sys_getpriority(int which, int who);
313  
314  asmlinkage long sys_shutdown(int, int);
315  asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd,
316  				void __user *arg);

sys_reboot()的定義在linux-4.10.3\kernel\reboot.c。

280  SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
281  		void __user *, arg)
282  {
283  ...
314  	mutex_lock(&reboot_mutex);
315  	switch (cmd) {
316  	case LINUX_REBOOT_CMD_RESTART:
317  		kernel_restart(NULL);
318  		break;
319  
320  	case LINUX_REBOOT_CMD_CAD_ON:
321  		C_A_D = 1;
322  		break;
323  
324  	case LINUX_REBOOT_CMD_CAD_OFF:
325  		C_A_D = 0;
326  		break;
327  
328  	case LINUX_REBOOT_CMD_HALT:
329  		kernel_halt();
330  		do_exit(0);
331  		panic("cannot halt");
332  
333  	case LINUX_REBOOT_CMD_POWER_OFF:
334  		kernel_power_off();
335  		do_exit(0);
336  		break;
         ...
361  	default:
362  		ret = -EINVAL;
363  		break;
364  	}
365  	mutex_unlock(&reboot_mutex);
366  	return ret;
367  }
mode從上面傳下來的值是RB_POWER_OFF。
bionic/libc/include/sys/reboot.h

/* use glibc names as well */
37  
38  #define RB_AUTOBOOT     LINUX_REBOOT_CMD_RESTART
39  #define RB_HALT_SYSTEM  LINUX_REBOOT_CMD_HALT
40  #define RB_ENABLE_CAD   LINUX_REBOOT_CMD_CAD_ON
41  #define RB_DISABLE_CAD  LINUX_REBOOT_CMD_CAD_OFF
42  #define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF
所以接下來走到kernel_power_off(); 
 /**
253   *	kernel_power_off - power_off the system
254   *
255   *	Shutdown everything and perform a clean system power_off.
256   */
257  void kernel_power_off(void)
258  {
259  	kernel_shutdown_prepare(SYSTEM_POWER_OFF);
260  	if (pm_power_off_prepare)
261  		pm_power_off_prepare();
262  	migrate_to_reboot_cpu();
263  	syscore_shutdown();
264  	pr_emerg("Power down\n");
265  	kmsg_dump(KMSG_DUMP_POWEROFF);
266  	machine_power_off();
267  }
268  EXPORT_SYMBOL_GPL(kernel_power_off);

走到這裏就不再繼續往下看了。



發佈了62 篇原創文章 · 獲贊 37 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章