編輯:關於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 流程整體比較簡單,到此分析完.
如何手動調低Android手機屏幕分辨率
1080P全高清屏幕雖然可為手機帶來更細膩的視界,但同時也會增加系統負載,拖慢游戲速度(和同配置720P手機相比)。那麼,如何才能提高1080P手機的游戲速
在android中使用SQLite數據庫
SQLite數據庫以其輕量、體積小等特點,使其在開發中運用的非常廣泛,在前面的博客中我也介紹過在Cocos2d-x中使用SQLite數據庫,這篇博客是介紹在Android
Android actionBar與Fragment結合使用Demo2
上一篇文章介紹了ActionBar的使用,這裡介紹ActionBar的另一種使用方法,達到的效果和以前的GroupActivity或TabHost是一樣的,可作為導航來使
Android中檢查、設置默認程序詳解
Android作為一個偉大的系統,自然提供了設置默認打開程序的實現.在這篇文章中,我會介紹如何在Android系統中設置默認的程序. 在設置默認程序之前,無非有兩種情況,