Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 6.0 Reboot 流程源代碼分析

Android 6.0 Reboot 流程源代碼分析

編輯:關於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 流程整體比較簡單,到此分析完.


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved