Android 的recovery模式分析

来源:互联网 发布:app下载网站源码 编辑:程序博客网 时间:2024/05/22 08:21
  • Recovery Binary:

      Recovery Binary 是 Android 进入 Recovery 模式所运行的程序,实现了 Recovery 模式下的功能。它由目录 bootable/recovery 下的源代码编译生成。头文件 bootable/recovery/recovery_ui.h 定义了 Recovery UI 的接口,bootable/recovery/default_recovery_ui.c 是其默认实现,每个设备可以有自己不同的实现,然后通过变量 TARGET_RECOVERY_UI_LIB 来指定,否则使用默认实现。

    # bootable/recovery/Android.mk

    ifeq ($(TARGET_RECOVERY_UI_LIB),)
      LOCAL_SRC_FILES +=default_recovery_ui.c
    else
      LOCAL_STATIC_LIBRARIES +=$(TARGET_RECOVERY_UI_LIB)
    endif
    • Recovery Image:

      Recovery Image 的生成规则在文件 build/core/Makefile 中定义,具体分析如下:

    # build/core/Makefile

    # -----------------------------------------------------------------
    # Recovery image

    # If neither TARGET_NO_KERNEL nor TARGET_NO_RECOVERY are true
    ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY) $(BUILD_TINY_ANDROID)))

    INSTALLED_RECOVERYIMAGE_TARGET:= $(PRODUCT_OUT)/recovery.img

    recovery_initrc :=$(call include-path-for, recovery)/etc/init.rc
    recovery_kernel :=$(INSTALLED_KERNEL_TARGET)# same as a non-recovery system
    recovery_ramdisk :=$(PRODUCT_OUT)/ramdisk-recovery.img
    recovery_build_prop :=$(INSTALLED_BUILD_PROP_TARGET)
    recovery_binary :=$(call intermediates-dir-for,EXECUTABLES,recovery)/recovery
    recovery_resources_common :=$(call include-path-for, recovery)/res
    recovery_resources_private :=$(strip$(wildcard$(TARGET_DEVICE_DIR)/recovery/res))
    recovery_resource_deps :=$(shell find$(recovery_resources_common)\
      $(recovery_resources_private)-type f)
    recovery_fstab :=$(strip$(wildcard$(TARGET_DEVICE_DIR)/recovery.fstab))
    recovery_mmc_fstab :=$(strip$(wildcard$(TARGET_DEVICE_DIR)/recovery_mmc.fstab))

    ifeq ($(recovery_resources_private),)
      $(info No private recovery resourcesfor TARGET_DEVICE$(TARGET_DEVICE))
    endif

    ifeq ($(recovery_fstab),)
      $(info No recovery.fstabfor TARGET_DEVICE$(TARGET_DEVICE))
    endif

    INTERNAL_RECOVERYIMAGE_ARGS :=\
        $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET))\
        --kernel $(recovery_kernel)\
        --ramdisk $(recovery_ramdisk)

    # Assumes this has already been stripped
    ifdef BOARD_KERNEL_CMDLINE
      INTERNAL_RECOVERYIMAGE_ARGS +=--cmdline "$(BOARD_KERNEL_CMDLINE)"
    endif
    ifdef BOARD_KERNEL_BASE
      INTERNAL_RECOVERYIMAGE_ARGS +=--base $(BOARD_KERNEL_BASE)
    endif
    BOARD_KERNEL_PAGESIZE :=$(strip$(BOARD_KERNEL_PAGESIZE))
    ifdef BOARD_KERNEL_PAGESIZE
      INTERNAL_RECOVERYIMAGE_ARGS +=--pagesize $(BOARD_KERNEL_PAGESIZE)
    endif

    INSTALLED_BOOTIMAGE_TARGET :=$(PRODUCT_OUT)/boot.img
    kernel: $(INSTALLED_BOOTIMAGE_TARGET)
    .PHONY: kernel

    # Keys authorized to sign OTA packages this build will accept.  The
    # build always uses test-keys for this; release packaging tools will
    # substitute other keys for this one.
    OTA_PUBLIC_KEYS :=$(SRC_TARGET_DIR)/product/security/testkey.x509.pem

    # Generate a file containing the keys that will be read by the
    # recovery binary.
    RECOVERY_INSTALL_OTA_KEYS :=\
        $(call intermediates-dir-for,PACKAGING,ota_keys)/keys
    DUMPKEY_JAR :=$(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar
    $(RECOVERY_INSTALL_OTA_KEYS): PRIVATE_OTA_PUBLIC_KEYS :=$(OTA_PUBLIC_KEYS)
    $(RECOVERY_INSTALL_OTA_KEYS):$(OTA_PUBLIC_KEYS)$(DUMPKEY_JAR)
        @echo "DumpPublicKey: $@ <= $(PRIVATE_OTA_PUBLIC_KEYS)"
        @rm -rf $@
        @mkdir -p $(dir $@)
        java -jar $(DUMPKEY_JAR)$(PRIVATE_OTA_PUBLIC_KEYS)> $@

    $(INSTALLED_RECOVERYIMAGE_TARGET):$(MKBOOTFS)$(MKBOOTIMG)$(MINIGZIP)\
            $(INSTALLED_RAMDISK_TARGET)\
            $(INSTALLED_BOOTIMAGE_TARGET)\
            $(recovery_binary)\
            $(recovery_initrc)$(recovery_kernel)\
            $(INSTALLED_2NDBOOTLOADER_TARGET)\
            $(recovery_build_prop)$(recovery_resource_deps)\
            $(recovery_fstab)\
            $(RECOVERY_INSTALL_OTA_KEYS)

        /* 以正常系统的根文件系统为基础构建 Recovery 的根文件系统 */
        @echo ----- Making recovery image ------
        rm -rf $(TARGET_RECOVERY_OUT)
        mkdir -p $(TARGET_RECOVERY_OUT)
        mkdir -p $(TARGET_RECOVERY_ROOT_OUT)
        mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/etc
        mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/tmp
        echo Copying baseline ramdisk...
        cp -R $(TARGET_ROOT_OUT)$(TARGET_RECOVERY_OUT)

        /* 删除所有的 Init 脚本,使用 Recovery 特定的 Init 脚本 */
        rm $(TARGET_RECOVERY_ROOT_OUT)/init*.rc
        echo Modifying ramdisk contents...
        cp -f $(recovery_initrc)$(TARGET_RECOVERY_ROOT_OUT)/

        /* 添加 Recovery Binary */
        cp -f $(recovery_binary)$(TARGET_RECOVERY_ROOT_OUT)/sbin/

        /* 添加通用的和设备特定的 Recovery 资源 */
        cp -rf $(recovery_resources_common)$(TARGET_RECOVERY_ROOT_OUT)/
        $(foreach item,$(recovery_resources_private),\
          cp -rf $(item) $(TARGET_RECOVERY_ROOT_OUT)/)

        /* 添加设备特定的文件系统表 */
        $(foreach item,$(recovery_fstab),\
          cp -f $(item)$(TARGET_RECOVERY_ROOT_OUT)/etc/recovery.fstab)
        $(foreach item,$(recovery_mmc_fstab),\
          cp -f $(item)$(TARGET_RECOVERY_ROOT_OUT)/etc/recovery_mmc.fstab)

        /* 内嵌验证签名的公钥 */
        cp $(RECOVERY_INSTALL_OTA_KEYS)$(TARGET_RECOVERY_ROOT_OUT)/res/keys

        /* 生成 Recovery 模式的默认属性文件 */
        cat $(INSTALLED_DEFAULT_PROP_TARGET)$(recovery_build_prop)\
                > $(TARGET_RECOVERY_ROOT_OUT)/default.prop

        /* 生成 Recovery 的根文件系统 ramdisk-recovery.img */
        $(MKBOOTFS)$(TARGET_RECOVERY_ROOT_OUT) |$(MINIGZIP)> $(recovery_ramdisk)

        /* 把正常系统的内核跟 ramdisk-recovery.img 打包生成 Recovery Image */
        $(MKBOOTIMG)$(INTERNAL_RECOVERYIMAGE_ARGS)--output $@
        @echo ----- Made recovery image --------$@

        /* 验证生成的 Recovery Image 有没有超出 Recovery 分区的大小 */
        $(hide)$(call assert-max-image-size,$@,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE),raw)

    else
    INSTALLED_RECOVERYIMAGE_TARGET :=
    endif

    .PHONY: recoveryimage
    recoveryimage: $(INSTALLED_RECOVERYIMAGE_TARGET)

    • Recovery Init Script:

      从上面的分析可以看出 recovery.img 和 boot.img 的区别不大,主要是 init 脚本不一样,recovery 的 init 脚本相对简单,系统起来后只运行 ueventd、recovery、adbd 三个服务。

    # bootable/recovery/etc/init.rc

    on early-init
        start ueventd

    on init
        export PATH /sbin
        export ANDROID_ROOT /system
        export ANDROID_DATA /data
        export EXTERNAL_STORAGE /sdcard

        symlink /system/etc /etc

        mkdir /sdcard
        mkdir /system
        mkdir /data
        mkdir /cache
      
        mount /tmp /tmp tmpfs

    on boot
        ifup lo
        hostname localhost
        domainname localdomain

        class_start default

    service ueventd /sbin/ueventd
        critical

    service recovery /sbin/recovery

    service adbd /sbin/adbd recovery
        disabled

    on property:persist.service.adb.enable=1
        start adbd

    on property:persist.service.adb.enable=0
        stop adbd

    • Android <----> Recovery Binary <----> Bootloader:

      有时候 Android 需要不同的模式互相协助来完成一项任务,这样不同模式之间就要有一种机制来交换信息。Recovery Binary 和 Bootloader 之间是通过 misc 分区来传递信息的,如果是 MTD 设备,则使用 misc 分区的第二个页面,如果是块设备,则使用 misc 分区的第一块,交换的信息通过如下结构体封装。Recovery Binary 和 Android 之间是通过 cache 分区下的如下几个固定文件来传递信息的。

    /* Recovery Binary <----> Bootloader */

    struct bootloader_message{
        char command[32];
        char status[32];
        char recovery[1024];
    };

    /* Recovery Binary <----> Android */

    /cache/recovery/command
    /cache/recovery/intent
    /cache/recovery/log
    /cache/recovery/last_log

    • Updater Binary:

      Updater Binary 是 OTA package 的安装程序,被打包到 OTA package 中一起发布。Updater Binary 的源代码位于目录 bootable/recovery/updater 中。每个设备都可以为 Updater Binary 添加自己特定的扩展,然后通过变量TARGET_RECOVERY_UPDATER_LIBS 和 TARGET_RECOVERY_UPDATER_EXTRA_LIBS 来指定。

    # bootable/recovery/updater/Android.mk

    LOCAL_STATIC_LIBRARIES+= $(TARGET_RECOVERY_UPDATER_LIBS) \
                              $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
    LOCAL_STATIC_LIBRARIES +=libapplypatch libedify libmtdutils libminzip libz
    LOCAL_STATIC_LIBRARIES +=libmincrypt libbz
    LOCAL_STATIC_LIBRARIES +=libcutils libstdc++ libc
    LOCAL_C_INCLUDES +=$(LOCAL_PATH)/..

    # Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function
    # named "Register_<libname>()".  Here we emit a little C function that
    # gets #included by updater.c.  It calls all those registration
    # functions.

    # Devices can also add libraries to TARGET_RECOVERY_UPDATER_EXTRA_LIBS.
    # These libs are also linked in with updater, but we don't try to call
    # any sort of registration function for these.  Use this variable for
    # any subsidiary static libraries required for your registered
    # extension libs.

    inc :=$(call intermediates-dir-for,PACKAGING,updater_extensions)/register.inc

    # During the first pass of reading the makefiles, we dump the list of
    # extension libs to a temp file, then copy that to the ".list" file if
    # it is different than the existing .list (if any).  The register.inc
    # file then uses the .list as a prerequisite, so it is only rebuilt
    # (and updater.o recompiled) when the list of extension libs changes.

    junk :=$(shell mkdir -p$(dir$(inc));\
                echo $(TARGET_RECOVERY_UPDATER_LIBS)> $(inc).temp;\
                diff -q $(inc).temp$(inc).list ||cp -f $(inc).temp$(inc).list)

    $(inc): libs :=$(TARGET_RECOVERY_UPDATER_LIBS)
    $(inc): $(inc).list
        $(hide)mkdir -p $(dir$@)
        $(hide)echo "" > $@
        $(hide)$(foreach lib,$(libs),echo"extern void Register_$(lib)(void);">> $@)
        $(hide)echo "void RegisterDeviceExtensions() {" >> $@
        $(hide)$(foreach lib,$(libs),echo"  Register_$(lib)();">> $@)
        $(hide)echo "}" >> $@

    $(call intermediates-dir-for,EXECUTABLES,updater)/updater.o :$(inc)
    LOCAL_C_INCLUDES +=$(dir$(inc))

  • 原创粉丝点击