[點晴永久免費OA]Android 開機流程介紹
一、目的 從2014年Android4.0開始接觸機器人,開發(fā)過App應用軟件,研究過Framework層框架結構、也梳理過Native層的系統(tǒng)流程,但是對于Hal層,以及底下的kernel方向,知之甚少。 二、環(huán)境
三、相關概念3.1 Android平臺架構如下是Google官網提供的平臺架構圖,很直觀地攘括了Android的層級,關于各個層級的結構,有想了解可以參考:https://developer.android.google.cn/guide/platform?hl=zh-cn ps: Android系統(tǒng)為什么要有Hal層? 3.2 Android啟動架構如下是從某大佬的文章里摘取的圖片,很詳細地描述了Android系統(tǒng)啟動過程:Boot Loader引導開機 -> Kernel -> Native -> Framework -> App 3.3 zImagezImage是一般情況下默認的壓縮內核映像文件,壓縮vmlinux,加上一段解壓啟動代碼得到,只能從0X0地址運行。 3.4 RAMDISK RAMDISK(initrd) 是一個小的分區(qū)像,在引導時內核以只讀方式掛載它。它只保護/int和一些置文件,它用于初始化和掛載其它的文件系統(tǒng)鏡像。 3.5 RC文件rc文件,是用Android Init Language編寫的特殊文件。用這種語法編寫的文件,統(tǒng)一用".rc"后綴。所有rc文件,不會被編譯/鏈接。它是配置文件,不是程序,是一種用于android init的配置文件。真正加載rc文件,并進行解析,做事情的是 Init進程。 四、詳細設計4.1 Boot Rom當長按電源開機的時候,引導芯片開始從固化在ROM的預設代碼開始執(zhí)行,然后將加載引導程序到RAM中。 4.2 BootLoaderBootLoader又稱為引導程序,它在運行操作系統(tǒng)之前運行的一段程序,是運行的第一個程序。主要的功能有檢查RAM、初始化一些硬件外設等功能,它最終的目的是啟動操作系統(tǒng)。 4.3 KernelKernel初始化可以分成三部分:zImage解壓縮、kernel的匯編啟動階段、Kernel的C啟動階段 4.3.1 zImage解壓縮階段
4.3.2 kernel的匯編啟動階段idle進程的啟動是用匯編語言編寫(感興趣可以去研究下),對應的啟動如下: s @bsp\kernel\kernel5.4\arch\arm\kernel\head-common.S ldmia r4, {r0, r1, r2, r3} str r9, [r0] @ Save processor ID str r7, [r1] @ Save machine type str r8, [r2] @ Save atags pointer cmp r3, #0 strne r10, [r3] @ Save control register values mov lr, #0 b start_kernel //啟動start_kernel函數 其中,語句b start_kernel,b 是跳轉的意思,即跳轉到start_kernel函數,對應的實現在bsp/kernel/kernel5.4/init/main.c,至此idle進程被啟動。 4.3.3 Kernel的C啟動階段start_kernel()函數是內核初始化C語言部分的主體。這個函數完成系統(tǒng)底層基本機制,包括處理器、存儲管理系統(tǒng)、進程管理系統(tǒng)、中斷機制、定時機制等的初始化工作。 c @bsp\kernel\kernel5.4\init\main.c asmlinkage __visible void __init start_kernel(void){ char *command_line; char *after_dashes; set_task_stack_end_magic(&init_task); smp_setup_processor_id();//打印了驅動加載的第一行l(wèi)og ... //初始化一系列系統(tǒng)底層機制 pr_notice("%s", linux_banner);//打印內核版本信息 ... pr_notice("Kernel command line: %s\n", saved_command_line);//打印從uboot傳遞過來的command_line字符串 ... /* Do the rest non-__init'ed, we're now alive */ arch_call_rest_init();//創(chuàng)建init進程、kthread進程、idle進程 prevent_tail_call_optimization();} 4.3.3.1 kernel啟動log(1)kernel內核啟動階段,smp_setup_processor_id()函數會打印的第一條log(文中l(wèi)og可以在 [目錄5.1開機log] 下載查看),如下: none [ 0.000000] c0 Booting Linux on physical CPU 0x0 (2)接著會打印內核的一些信息,版本,作者,編譯器版本,日期等,如下: none [ 0.000000]c0 Linux version 5.4.147+ (lzq@cz-PowerEdge-R730) (Android (7284624, based on r416183b) clang version 12.0.5 (https://android.googlesource.com/toolchain/llvm-project c935d99d7cf2016289302412d708641d52d2f7ee), LLD 12.0.5 (/buildbot/src/android/llvm-toolchain/out/llvm-project/lld c935d99d7cf2016289302412d708641d52d2f7ee)) #5 SMP PREEMPT Thu Sep 28 15:17:40 CST 2023 (3)打印出從uboot傳遞過來的command_line字符串,在setup_arch函數中獲得的(proc/cmdline) none [ 0.000000]c0 Kernel command line: earlycon=sprd_serial_ex,0x508d0000,115200n8 console=ttySE0,921600n8 loglevel=7 init=/init root=/dev/ram0 rw vmalloc=360M printk.devkmsg=on androidboot.boot_devices=soc/soc:ap-ahb/20600000.sdio initcall_debug=1 swiotlb=1 androidboot.selinux=permissive androidboot.hardware=sl8541e_1h10_32b androidboot.dtbo_idx=0 lcd_id=ID40396 lcd_name=lcd_st7123_truly_mipi_hd lcd_base=9e000000 lcd_size=1440x720 logo_bpix=24 androidboot.ddrsize=1024M androidboot.ddrsize.range=[1024,2048) sysdump_magic=80001000 sysdump_re_flag=1 androidboot.wdten=0 androidboot.dswdten=disable modem=shutdown ltemode=lcsfb rfboard.id=-1 rfhw.id=0 crystal=2 32k.less=1 androidboot.pmic.chipid=2721 modemboot.method=emmcboot cpcmdline=end androidboot.verifiedbootstate=orange androidboot.flash.locked=0 androidboot.serialno=LE210210001326000028 buildvariant=userdebug androidboot.vbmeta.device=PARTUUID=1.0 androidboot.vbmeta.avb_version=1.1 androidboot.vbmeta.device_state=unlocked androidboot. 4.3.3.2 init進程&kthreadd進程創(chuàng)建了Linux系統(tǒng)中兩個重要的進程init和kthreadd,并且將當前進程設置為idle進程: c @bsp\kernel\kernel5.4\init\main.cvoid __init __weak arch_call_rest_init(void){ rest_init();}noinline void __ref rest_init(void){ ... pid = kernel_thread(kernel_init, NULL, CLONE_FS);//創(chuàng)建init進程 ... pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);//創(chuàng)建kthreadd進程 ... /* Call into cpu_idle with preempt disabled */ cpu_startup_entry(CPUHP_ONLINE);//設置當前進程為idle進程} Linux下有三個特殊的進程,idle(swapper)進程(PID = 0),init進程(PID = 1)和看threadd(PID = 2): (1)idle(swapper)進程由系統(tǒng)自動創(chuàng)建,運行在內核態(tài)。idle進程其pid=0,其前身是系統(tǒng)創(chuàng)建的第一個進程,也是唯一一個沒有通過fork或者kernel_thread產生的進程。完成加載系統(tǒng)后,演變?yōu)檫M程調度、交換,常常被稱為交換進程。 4.3.3.3 idle進程啟動這個函數是Linux內核為非引導CPU初始化和進入空閑循環(huán)的入口函數,負責在系統(tǒng)沒有任務需要執(zhí)行時,讓CPU進入空閑狀態(tài)。 c @bsp\kernel\kernel5.4\kernel\sched\idle.cvoid cpu_startup_entry(enum cpuhp_state state){ arch_cpu_idle_prepare(); cpuhp_online_idle(state); while (1) do_idle();} 4.4 Init進程由上一節(jié)可知,Init進程由0號idle進程啟動,kernel_init()為其入口函數,該函數主要通過三種方式啟動init程序。 c static int __ref kernel_init(void *unused){ ... if (ramdisk_execute_command) { ret = run_init_process(ramdisk_execute_command);//Step 1. 根據ramdisk_execute_command的值來啟動init程序 if (!ret) return 0; pr_err("Failed to execute %s (error %d)\n", ramdisk_execute_command, ret); } if (execute_command) { ret = run_init_process(execute_command);//Step 2. 根據execute_command的值來啟動init程序 if (!ret) return 0; panic("Requested init %s failed (error %d).", execute_command, ret); } if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) //Step 3.根據系統(tǒng)默認位置來啟動init程序 return 0; panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/admin-guide/init.rst for guidance.");//Step 4.異常重啟} (1)ramdisk_execute_command是一個全局的char指針變量,值為“/init”,也就是根目錄下的init程序。該值通過uboot傳遞過來,具體可參考上一節(jié)的command_line。(如未配置該值,即會將該值默認設置為"/init",該項目是從該處啟動init進程) log [ 1.000614]c1 Run /init as init process 4.4.1 init程序編譯 我們知道init程序是引用的根目錄下的init,即/init,但是其對應的軟鏈接指向:system/bin/init,相關信息如下: 編譯makefile文件如下: none @system\core\init\Android.bp phony { name: "init", required: [ "init_second_stage", ], } cc_binary { name: "init_second_stage", recovery_available: true, stem: "init",//最終生成的二進制文件名 defaults: ["init_defaults"], static_libs: ["libinit"], ... srcs: ["main.cpp"], ... } 4.4.2 init程序入口函數init程序的入口函數,根據不同的入參,響應init不同階段、處理不同業(yè)務邏輯。 c @system\core\init\main.cppint main(int argc, char** argv) { ... if (!strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv);//初始化uevent事件 } if (argc > 1) { ... if (!strcmp(argv[1], "selinux_setup")) { android::mboot::mdb("SELinux Setup ..."); return SetupSelinux(argv);//selinux權限 } if (!strcmp(argv[1], "second_stage")) { return SecondStageMain(argc, argv);//第二階段 } } return FirstStageMain(argc, argv);//第一階段} 由于在kernel階段啟動init程序時,未配置參數,故首先會引用FirstStageMain函數。另外,根據log可以看到,其引用順序如下: none [ 1.004958]c1 init: init first stage started! [ 2.547025]c0 init: Opening SELinux policy [ 3.226672]c0 init: init second stage started! [ 3.715657]c1 ueventd: ueventd started!
4.4.3 FirstStageMain第一階段,該階段所掛載的文件系統(tǒng)都屬于ramdisk,運行在虛擬內存上。 c @system\core\init\first_stage_mount.cppint FirstStageMain(int argc, char** argv) { ... //Step 1. 創(chuàng)建&掛載最基本的文件系統(tǒng) CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));//將/dev設置為tmpfs并掛載,設置0755權限,tmpfs是在內存上建立的文件系統(tǒng)(Filesystem) CHECKCALL(mkdir("/dev/pts", 0755)); CHECKCALL(mkdir("/dev/socket", 0755)); CHECKCALL(mkdir("/dev/dm-user", 0755)); ... //Step 2. 初始化log系統(tǒng)并打印 SetStdioToDevNull(argv); InitKernelLogging(argv); ... LOG(INFO) << "init first stage started!"; ... //Step 3. 加載內核驅動模塊 if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console, module_count)) { ... } ... //Step 4.掛載分區(qū)(system、vendor、product等分區(qū)) if (!DoFirstStageMount(!created_devices)) { LOG(FATAL) << "Failed to mount required partitions early ..."; } ... //Step 5.初始化Android的安全框架Android Verified Boot SetInitAvbVersionInRecovery(); ... //Step 6.執(zhí)行下一個階段 const char* path = "/system/bin/init"; const char* args[] = {path, "selinux_setup", nullptr}; ... execv(path, const_cast<char**>(args));//exec系列函數可以把當前進程替換為一個新進程,且新進程與原進程有相同的PID,即重新回到main.cpp ...} Step 1. 創(chuàng)建&掛載最基本的文件系統(tǒng),創(chuàng)建了如下五類文件系統(tǒng):
相關掛載情況如下: Step 2. 結合dev/kmsg節(jié)點,初始化log系統(tǒng),并在第一階段開始時,打印第一條log none [ 1.004958]c1 init: init first stage started! Step 3. 加載內核驅動模塊,第一階段加載的內核模塊如下: none [ 1.012653]c1 init: Loaded kernel module /lib/modules/sprd_wdt.ko [ 1.020197]c0 init: Loaded kernel module /lib/modules/sc2721-regulator.ko [ 1.022427]c0 init: Loaded kernel module /lib/modules/nvmem-sc27xx-efuse.ko [ 1.026126]c0 init: Loaded kernel module /lib/modules/spool.ko [ 1.033668]c0 init: Loaded kernel module /lib/modules/sipx.ko [ 1.047553]c0 init: Loaded kernel module /lib/modules/seth.ko [ 1.048526]c0 init: Loaded kernel module /lib/modules/usb_f_vser.ko ... Step 4. 掛載分區(qū)。創(chuàng)建/first_stage_ramdisk并掛載,然后將根目錄切換到/first_stage_ramdisk,并掛載system、vendor 、product等系統(tǒng)分區(qū),掛載信息如上。 4.4.4 SetupSelinux該階段主要是初始化Selinux權限相關業(yè)務,同時在業(yè)務流程最后一步時,重新執(zhí)行system/bin/init程序,再次啟動下一個階段SecondStageMain。 c @system\core\init\selinux.cppint SetupSelinux(char** argv) { ... LOG(INFO) << "Opening SELinux policy"; ... const char* path = "/system/bin/init"; const char* args[] = {path, "second_stage", nullptr}; execv(path, const_cast<char**>(args)); ... return 1;} 該階段log打印如下: none [ 2.547025]c0 init: Opening SELinux policy 4.4.5 SecondStageMain第二階段,涉及到文件系統(tǒng)掛載、屬性服務等系統(tǒng)相關業(yè)務,其中最主要的一點去解析rc文件(創(chuàng)建目錄,修改權限,掛載分區(qū),啟動服務進程等),以期讓開機流程進入下一階段。 c @system\core\init\init.cppint SecondStageMain(int argc, char** argv) { //Step 1.初始化log SetStdioToDevNull(argv); InitKernelLogging(argv); LOG(INFO) << "init second stage started!"; ... //Step 2.屬性服務初始化,讀取默認屬性配置 PropertyInit(); ... //Step 3.掛載其他文件系統(tǒng),如/apex MountExtraFilesystems(); ... //Step 4.啟動屬性服務 StartPropertyService(&property_fd); ... //Step 5.加載開機rc文件 ActionManager& am = ActionManager::GetInstance(); ServiceList& sm = ServiceList::GetInstance(); LoadBootScripts(am, sm); ... //Step 6.設置進程優(yōu)先級,主進程不能退出 setpriority(PRIO_PROCESS, 0, 0); while (true) { ... } return 0;} Step 1. 初始化log,該階段log打印如下: none [ 3.226672]c0 init: init second stage started! Step 2. 屬性服務初始化,讀取默認屬性配置。獲取system/build.prop、vendor/build.prop、/odm/build.prop、/product/build.prop等其他build.prop屬性,并加載到properties map結構中,然后通過MMAP映射到全局內存中,供所有進程調用; none [ 3.481952]c0 init: Parsing file /system/etc/init/hw/init.rc... [ 3.489693]c0 init: Parsing file /init.environ.rc... [ 3.490110]c0 init: Parsing file /system/etc/init/hw/init.usb.rc... [ 3.491867]c0 init: Parsing file /init.sl8541e_1h10_32b.rc... [ 3.493283]c0 init: Parsing file /vendor/etc/init/hw/init.sl8541e_1h10_32b.rc... [ 3.494372]c0 init: Parsing file /vendor/etc/init/hw/init.sl8541e_1h10_32b.usb.rc... [ 3.509664]c0 init: Parsing file /vendor/etc/init/hw/init.ram.rc... ... 解析init.rc會把一條條命令映射到內存中,然后依次啟動,啟動順序如下: none on on early-init:在初始化早期階段觸發(fā) on init:在初始化階段觸發(fā) on late-init:在初始化晚期階段觸發(fā) on boot/charger:當系統(tǒng)啟動/充電時觸發(fā) on property:當屬性值滿足條件時觸發(fā) Step 6. 設置進程優(yōu)先級,主進程不能銷毀和退出。 4.5 Zygote進程4.5.1 Zygote進程啟動腳本(1)rc文件編譯 c @system\core\rootdir\Android.bp prebuilt_etc { name: "init.rc", src: "init.rc", sub_dir: "init/hw", required: [ "fsverity_init", "platform-bootclasspath", ],} (2)init.rc c @system\core\rootdir\init.rc...import /system/etc/init/hw/init.${ro.zygote}.rc//導入zygote的rc文件,ro.zygote屬性可根據系統(tǒng)讀取...# Mount filesystems and start core system services.on late-init ... # Now we can start zygote for devices with file based encryption trigger zygote-start //在開機初始化晚期階段,觸發(fā)zygote啟動 ...# It is recommended to put unnecessary data/ initialization from post-fs-data# to start-zygote in device's init.rc to unblock zygote start.on zygote-start && property:ro.crypto.state=unencrypted wait_for_prop odsign.verification.done 1 # A/B update verifier that marks a successful boot. exec_start update_verifier_nonencrypted start statsd start netd start zygote start zygote_secondary (3)init.zygote32.rc r @system\core\rootdir\init.zygote32.rc service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main priority -20 user root group root readproc reserved_disk socket zygote stream 660 root system socket usap_pool_primary stream 660 root system onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse onrestart write /sys/power/state on onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart netd onrestart restart wificond writepid /dev/cpuset/foreground/tasks critical window=${zygote.critical_window.minute:-off} target=zygote-fatal (4)zygote啟動log none [ 7.916749]c3 init: starting service 'zygote'... 4.5.2 Zygote進程(1)zygote編譯文件 none @frameworks\base\cmds\app_process\Android.bp cc_binary { name: "app_process", srcs: ["app_main.cpp"], ... } (2)app_process啟動 c int main(int argc, char* const argv[]){ ... while (i < argc) {//讀取rc文件傳進來的參數 const char* arg = argv[i++]; if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = ZYGOTE_NICE_NAME; } else if (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; } ... } ... if (zygote) { runtime.start("com.android.internal.os.ZygoteInit", args, zygote);//啟動ZygoteInit } ...} (3)AndroidRuntime c void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote){ ALOGD(">>>>>> START %s uid %d <<<<<<\n", className != NULL ? className : "(unknown)", getuid()); ... //通過反射啟動函數 char* slashClassName = toSlashClassName(className != NULL ? className : ""); jclass startClass = env->FindClass(slashClassName); if (startClass == NULL) { ALOGE("JavaVM unable to locate class '%s'\n", slashClassName); /* keep going */ } else { jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V"); if (startMeth == NULL) { ALOGE("JavaVM unable to find main() in '%s'\n", className); /* keep going */ } else { env->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0 if (env->ExceptionCheck()) threadExitUncaughtException(env);#endif } } ...} 當前流程會打印如下log: none 01-01 08:00:08.538 387 387 D AndroidRuntime: >>>>>> START com.android.internal.os.ZygoteInit uid 0 <<<<<< 4.5.3 Zygote進程(java)(1)ZygoteInit java @frameworks\base\core\java\com\android\internal\os\ZygoteInit.javapublic static void main(String[] argv) { ... try { //Step 1. 預加載資源文件 if (!enableLazyPreload) { bootTimingsTraceLog.traceBegin("ZygotePreload"); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); preload(bootTimingsTraceLog); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); bootTimingsTraceLog.traceEnd(); // ZygotePreload } ... //Step 2.注冊Zygote的socket監(jiān)聽接口 zygoteServer = new ZygoteServer(isPrimaryZygote); //Step 3.創(chuàng)建system_server進程 if (startSystemServer) { Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); if (r != null) { r.run(); return; } } //Step 4.主線程loop消息循環(huán) caller = zygoteServer.runSelectLoop(abiList); } ...} Step 1. 提前加載類,加載系統(tǒng)資源(如一些公共的庫、SDK等),這樣當程序被fork處理后,應用的進程內已經包含了這些系統(tǒng)資源,大大節(jié)省了應用的啟動時間。 (2)forkSystemServer java @frameworks\base\core\java\com\android\internal\os\ZygoteInit.javaprivate static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) { ... /* Hardcoded command line to start the system server */ String[] args = { "--setuid=1000", "--setgid=1000", "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023," + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011", "--capabilities=" + capabilities + "," + capabilities, "--nice-name=system_server", "--runtime-args", "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT, "com.android.server.SystemServer", };//啟動SystemServer相關參數 ... try { ... /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids, parsedArgs.mRuntimeFlags, null, parsedArgs.mPermittedCapabilities, parsedArgs.mEffectiveCapabilities);//fork出SystemServer進程 } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } /* For child process */ if (pid == 0) { ... return handleSystemServerProcess(parsedArgs);//通過反射調用SystemServer進程的main函數 } return null;} (3)handleSystemServerProcess java @frameworks\base\core\java\com\android\internal\os\RuntimeInit.javaprotected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<?> cl; try { cl = Class.forName(className, true, classLoader);//反射調用類:com.android.server.SystemServer } ... Method m; try { m = cl.getMethod("main", new Class[] { String[].class });//反射調用方法:main } ... return new MethodAndArgsCaller(m, argv);} 4.6 SystemServer進程Android系統(tǒng)在啟動的時候,在啟動兩個重要的進程,一個是Zygote進程,另一個是由zygote進程fork出來的system_server進程。SystemSever負責啟動系統(tǒng)的各項服務,Android系統(tǒng)中Java世界的核心Service都在這里啟動。 4.6.1 main函數由上一章節(jié)我們知道,SystemServer的入口函數是main方法,如下: java @frameworks\base\services\java\com\android\server\SystemServer.javapublic static void main(String[] args) { new SystemServer().run();}private void run() { ... Slog.i(TAG, "Entered the Android system server!"); ... // Start services. try { t.traceBegin("StartServices"); startBootstrapServices(t);//引導服務 startCoreServices(t);//核心服務 startOtherServices(t);//其他服務 } ... // Loop forever. Looper.loop();//主線程循環(huán)隊列 throw new RuntimeException("Main thread loop unexpectedly exited");} SystemServer進程成功啟動,會打印如下log: none 01-01 08:00:21.430 1001 1001 I SystemServer: Entered the Android system server! 4.6.2 SystemServer中啟動服務在一系列的java服務中,可以分為三類:系統(tǒng)boot級別服務、核心服務、其他服務,其對應如下:
4.7 Home進程一般情況下,Android原生的軟體會包含兩個home進程,一個是Settings進程的Fallbackhome,一個是Launcher進程。 4.7.1 Home進程的編譯(1)FallbackHome編譯 none @packages\apps\Settings\Android.bp android_app { name: "Settings", defaults: ["platform_app_defaults"], platform_apis: true, certificate: "platform", system_ext_specific: true, privileged: true, ... } (2)Launcher進程編譯 none @packages\apps\Launcher3\Android.mk include $(CLEAR_VARS) ... LOCAL_PACKAGE_NAME := Launcher3QuickStepGo LOCAL_PRIVILEGED_MODULE := true LOCAL_SYSTEM_EXT_MODULE := true LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep ... 4.7.2 Launcher進程啟動FallbackHome是系統(tǒng)由未解密到解密過程的一個過度界面,只要用戶把系統(tǒng)解鎖過一次后,FallbackHome收到解鎖廣播就會退出,而WMS檢測到當前Acitivity棧是空的,進而啟動真正的Launcher。由于FallbackHome沒有界面,所以可能會出現一個問題,home進程切換時會出現空白界面,接下來才是顯示Launcher的一個圖標界面。 java @packages\apps\Settings\src\com\android\settings\FallbackHome.javaprotected void onCreate(Bundle savedInstanceState) { ... registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));//注冊ACTION_USER_UNLOCKED廣播 maybeFinish();}private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { maybeFinish();//接收ACTION_USER_UNLOCKED廣播 }};private void maybeFinish() { if (getSystemService(UserManager.class).isUserUnlocked()) { final Intent homeIntent = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME); final ResolveInfo homeInfo = getPackageManager().resolveActivity(homeIntent, 0);//查詢home包名信息,此處一般是返回Launcher的信息 if (Objects.equals(getPackageName(), homeInfo.activityInfo.packageName)) { Log.d(TAG, "User unlocked but no home; let's hope someone enables one soon?"); mHandler.sendEmptyMessageDelayed(0, 500);//間隔500ms輪詢 } else { Log.d(TAG, "User unlocked and real home found; let's go!"); getSystemService(PowerManager.class).userActivity( SystemClock.uptimeMillis(), false); finish();//結束當前Activity,啟動Launcher應用 } }} 4.7.3 FallbackHome進程啟動(1) 啟動home進程 java @frameworks\base\services\core\java\com\android\server\wm\RootWindowContainer.javavoid startHomeOnEmptyDisplays(String reason) { forAllTaskDisplayAreas(taskDisplayArea -> { if (taskDisplayArea.topRunningActivity() == null) { startHomeOnTaskDisplayArea(mCurrentUser, reason, taskDisplayArea, false /* allowInstrumenting */, false /* fromHomeKey */); } });} (2) ACTION_USER_UNLOCKED廣播發(fā)送 java @frameworks\base\services\core\java\com\android\server\am\UserController.javavoid finishUserUnlocked(final UserState uss) { ... if (!mInjector.getUserManager().isPreCreated(userId)) { // Dispatch unlocked to external apps final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED); unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); unlockedIntent.addFlags( Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); mInjector.broadcastIntent(unlockedIntent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), userId); } ...} 五、相關資源5.1 開機log串口+logcat:https://download.csdn.net/download/u013320490/88800008 六、小結 本文章涉獵Android多個層級,旨在梳理整體流程,對Android設備的啟動有一個感性的認識,能夠達到一定邏輯自洽。 七、參考資料
作者:林奮斗同學,轉自博客園 https://www.cnblogs.com/zhiqinlin/p/18001113 該文章在 2024/2/1 16:39:13 編輯過 |
關鍵字查詢
相關文章
正在查詢... |