編輯:關於Android編程
Shutdown 跟 reboot流程很類似,所以這裡以reboot分析:
reboot的類型:
1、手動長按power鍵選擇reboot;
2、adb reboot;
3、手動長按power鍵11s觸發reboot;
4、BUG_ON(1),觸發kernel panic流程reboot;
上面1、2的本質上代碼跑的是一樣的,下面主要分析第1類正常的關鍵源碼流程。
關機流程總圖:
一、首先看Android 層.
1、長按power鍵選擇reboot必定走以下接口:
下面開始進入reboot 前的准備工作,大致分為發起有序shutdown廣播、執行Activity、安裝包管理、無線相關、掛載服務等組件的shutdown工作.
ShutdownThread.java public void run() { //SHUTDOWN有序廣播結果接受 BroadcastReceiver br = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { actionDone(); } }; ... //發送SHUTDOWN有序廣播,注意是同步的,如果被阻塞將會block住main thread. Intent intent = new Intent(Intent.ACTION_SHUTDOWN); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, null, br, mHandler, 0, null, null); //等待有序廣播全部處理完成,也就是等上面的br.onReveive回調. synchronized (mActionDoneSync) { while (!mActionDone) { ... try { mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC)); } catch (InterruptedException e) { } } } ... //ActivityManagerService執行shutdown操作,寫一些相關狀態(比如battery)記錄到文件. final IActivityManager am = ActivityManagerNative.asInterface(ServiceManager.checkService("activity")); if (am != null) { try { am.shutdown(MAX_BROADCAST_TIME); } catch (RemoteException e) { } } ... //安裝包管理服務執行shutdonw,將當前的packageName寫入data/system目錄文件中. final PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package"); if (pm != null) { pm.shutdown(); } ... //無線相關執行shutdown,比如nfc、電話服務相關等. shutdownRadios(MAX_RADIO_WAIT_TIME); ... //掛載服務卸載完成回調 IMountShutdownObserver observer = new IMountShutdownObserver.Stub() { public void onShutDownComplete(int statusCode) throws RemoteException { actionDone(); } }; //執行文件系統掛載服務卸載 synchronized (mActionDoneSync) { try { final IMountService mount = IMountService.Stub.asInterface( ServiceManager.checkService("mount")); mount.shutdown(observer); } catch (Exception e) { Log.e(TAG, "Exception during MountService shutdown", e); } // 等待卸載完成,也就是等上面的 observer.onShutDownComplete執行完 while (!mActionDone) { ... try { mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC)); } catch (InterruptedException e) { } } } ... //准備工作完成,進入正式reboot流程 rebootOrShutdown(mContext, mReboot, mRebootReason); }
繼續分析准備工作後的reboot流程,主要要干的事情就是把shutdown或者reboot的command從java層傳到native層的reboot接口.
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { //如果是重啟的話就執行lowLevelReboot,否則就執行lowLevelShutdown接口 if (reboot) { PowerManagerService.lowLevelReboot(reason); } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) { //如果是關機命令,則會振動500ms提示 Vibrator vibrator = new SystemVibrator(context); try { vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES); } catch (Exception e) { } //等500ms待vib完成再進入shutdown. try { Thread.sleep(SHUTDOWN_VIBRATE_MS); } catch (InterruptedException unused) { } } PowerManagerService.lowLevelShutdown(); } public static void lowLevelReboot(String reason) { ... //使用屬性服務傳入cmd觸發reboot的 Action SystemProperties.set("sys.powerctl", "reboot," + reason); //等待20s,也就是說20s內需要關機完成 try { Thread.sleep(20 * 1000L); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } //下面這條log很關機,如果這條log打出來了,就說明關機失敗了,需要找原因了... Slog.wtf(TAG, "Unexpected return from lowLevelReboot!"); } //如果執行的是shutdown則走執行下面的cmd public static void lowLevelShutdown() { SystemProperties.set("sys.powerctl", "shutdown"); }
Java層關機流程分析到此結束,進入native層,我們知道,屬性服務貫穿整個Android系統可以很方便的觸發各種Action、啟動服務等,那麼這裡的SystemProperties.set("sys.powerctl", "reboot," + reason)到底干了什麼事情呢?這個需要從init.rc找答案(屬性服務觸發實現機制暫不討論)。搜索sys.powerctl關鍵字:
./rootdir/init.rc:544:on property:sys.powerctl=* ./rootdir/init.rc:545: powerctl ${sys.powerctl}
這是一個on 的action,意思是當sys.powerctl的值改變時,執行powerctl命令,參數就是${sys.powerctl},此處就是上面的reboot,那麼具體是什麼呢?搜索powerctl會發現:
./init/keywords.h:17:int do_powerctl(int nargs, char **args); ./init/keywords.h:79: KEYWORD(powerctl, COMMAND, 1, do_powerctl)
很顯然其實就是代表的do_powerctl函數!簡單來說就是執行SystemProperties.set("sys.powerctl", "reboot," + reason) 函數的時候其實就是會最終下面的函數:
int do_powerctl(int nargs, char **args) { char command[PROP_VALUE_MAX]; ... res = expand_props(command, args[1], sizeof(command)); ... if (strncmp(command, "shutdown", 8) == 0) { cmd = ANDROID_RB_POWEROFF; len = 8; } else if (strncmp(command, "reboot", 6) == 0) { cmd = ANDROID_RB_RESTART2; len = 6; } else { ERROR("powerctl: unrecognized command '%s'\n", command); return -EINVAL; } ... //很簡單,就是解析出要下發哪一個cmd,這裡顯然就是ANDROID_RB_RESTART2了,接著 //調用android層最後一個函數接口 return android_reboot(cmd, 0, reboot_target); } int android_reboot(int cmd, int flags UNUSED, const char *arg) { int ret; // 將緩沖區數據寫回磁盤,保證數據同步. sync(); //把filesystem置為read only,不允許proc再往裡面寫東西. remount_ro(); //下面就是reboot的system call進入內核空間了: switch (cmd) { case ANDROID_RB_RESTART: ret = reboot(RB_AUTOBOOT); break; case ANDROID_RB_POWEROFF: ret = reboot(RB_POWER_OFF); break; case ANDROID_RB_RESTART2: ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, arg); break; default: ret = -1; } return ret; }
二、Android層關機流程分析完成,進入內核層分析,執行系統調用後進入kernel層系統調用入口:(系統調用是用戶程序請求內核服務的標准形式,這裡我們不去關注其具體實現)
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { ... //忽略前頭一堆各種檢查細節,關注reboot流程主線. //互斥鎖,保證當前就一個CPU在執行此路徑. mutex_lock(&reboot_mutex); switch (cmd) { case LINUX_REBOOT_CMD_RESTART: kernel_restart(NULL); break; ... case LINUX_REBOOT_CMD_HALT: kernel_halt(); do_exit(0); panic("cannot halt"); case LINUX_REBOOT_CMD_POWER_OFF: kernel_power_off(); do_exit(0); break; case LINUX_REBOOT_CMD_RESTART2: ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1); if (ret < 0) { ret = -EFAULT; break; } buffer[sizeof(buffer) - 1] = '\0'; //進入內核restart入口函數 kernel_restart(buffer); break; ... mutex_unlock(&reboot_mutex); return ret; }
kernel_restart 函數要干的事情主要分為幾部分:
void kernel_restart(char *cmd) { //kernel關機准備工作. kernel_restart_prepare(cmd); //掛起其他cpu的工作,只留下當前cpu干活 migrate_to_reboot_cpu(); //核心設備執行shutdown,比如PM,irq,usb等. syscore_shutdown(); if (!cmd) pr_emerg("Restarting system\n"); else pr_emerg("Restarting system with command '%s'\n", cmd); kmsg_dump(KMSG_DUMP_RESTART); //執行各個體系結構相關的關機、restart操作實現 machine_restart(cmd); }
kernel_restart_prepare 分析,主要干了兩件事情:發通知給感興趣的dev + 執行dev卸載
void kernel_restart_prepare(char *cmd) { //發cmd給通知鏈中對SYS_RESTART感興趣的設備,執行nofifier回調. blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); system_state = SYSTEM_RESTART; //用戶模式 disable ? usermodehelper_disable(); //設備卸載 device_shutdown(); }
這裡需要重點分析下device_shutdown函數,如果該函數stuck,會導致無法關機.
void device_shutdown(void) { struct device *dev, *parent; //自旋鎖,關搶斷. spin_lock(&devices_kset->list_lock); /* * Walk the devices list backward, shutting down each in turn. * Beware that device unplug events may also start pulling * devices offline, even as the system is shutting down. */ while (!list_empty(&devices_kset->list)) { //從device鏈表使用“內核中經典大法-從實例找容器方式” 遍歷各個dev dev = list_entry(devices_kset->list.prev, struct device, kobj.entry); /* * hold reference count of device's parent to * prevent it from being freed because parent's * lock is to be held */ //激活parent dev和dev,這get,put名字起的容易讓人誤解,汗.. parent = get_device(dev->parent); get_device(dev); /* * Make sure the device is off the kset list, in the * event that dev->*->shutdown() doesn't remove it. */ //把dev從kobj.entry容器中刪除 list_del_init(&dev->kobj.entry); spin_unlock(&devices_kset->list_lock); /* hold lock to avoid race with probe/release */ if (parent) device_lock(parent); device_lock(dev); //阻止任何的runtime相關的dev掛起 /* Don't allow any more runtime suspends */ pm_runtime_get_noresume(dev); //這個pm runtime相關函數很復雜,暫時沒看懂要干什麼,汗.. pm_runtime_barrier(dev); //執行各個對關機感興趣dev的shutdown回調函數 if (dev->bus && dev->bus->shutdown) { if (initcall_debug) dev_info(dev, "shutdown\n"); dev->bus->shutdown(dev); } else if (dev->driver && dev->driver->shutdown) { if (initcall_debug) dev_info(dev, "shutdown\n"); dev->driver->shutdown(dev); } device_unlock(dev); if (parent) device_unlock(parent); //告訴dev,你現在可以掛起了. put_device(dev); put_device(parent); spin_lock(&devices_kset->list_lock); } spin_unlock(&devices_kset->list_lock); }
下面進入執行真正的關機操作:
void machine_restart(char *cmd) { //關閉中斷 local_irq_disable(); //停掉別的cpu,只留下當前執行的cpu(smp:多對稱處理器結構) smp_send_stop(); /* Flush the console to make sure all the relevant messages make it * out to the console drivers */ arm_machine_flush_console(); //arm_pm_restart 是函數指針,指向各個體系結構和芯片廠商具體的restart入口,傳參給pmic執行restart或者shutdwon的動作。 //比如高通8937項目對於的就是:do_msm_restart,mtk 6580對應的就是:psci_sys_reset //(還需要打log確認),如果沒有定義則跑默認的kernel restart接口. if (arm_pm_restart) arm_pm_restart(reboot_mode, cmd); else do_kernel_restart(cmd); //等1s時間,若1s後打印出下面的log就說明shutdwon失敗了,正常情況就已經斷電關機了. mdelay(1000); /* Whoops - the platform was unable to reboot. Tell the user! */ printk("Reboot failed -- System halted\n"); local_irq_disable(); //如果跑到這裡就說明關機失敗了. while (1); }
Android reboot 流程整體比較簡單,到此分析完.
1080P全高清屏幕雖然可為手機帶來更細膩的視界,但同時也會增加系統負載,拖慢游戲速度(和同配置720P手機相比)。那麼,如何才能提高1080P手機的游戲速
SQLite數據庫以其輕量、體積小等特點,使其在開發中運用的非常廣泛,在前面的博客中我也介紹過在Cocos2d-x中使用SQLite數據庫,這篇博客是介紹在Android
上一篇文章介紹了ActionBar的使用,這裡介紹ActionBar的另一種使用方法,達到的效果和以前的GroupActivity或TabHost是一樣的,可作為導航來使
Android作為一個偉大的系統,自然提供了設置默認打開程序的實現.在這篇文章中,我會介紹如何在Android系統中設置默認的程序. 在設置默認程序之前,無非有兩種情況,