android recovery mode debug notes on msm8909 platform

1. Print message in recovery mode

# adb pull /tmp/recovery.log ; in recovery mode

# adb pull /cache/recovery/last_log ; in normal mode after recovery done

; last_kmsg: kernel log when recoverying; last_log: recovery log about minui
# grover:/cache/recovery # ls
last_install last_kmsg last_locale last_log
# grover:/cache/recovery # cat last_locale
zh_CN

grover:/cache/recovery # cat last_install
/data/grover-ota-eng.buildfarm.zip
1
time_total: 86
retry: 0
bytes_written_system: 590950400
bytes_stashed_system: 0

2. display interface description

recovery main函数中主菜单的显示,就是通过以下的接口将文字图片显示出来,应用层调用minui库实现了图形的描绘以及固定大小的文字显示,minui库调用了Pixelflinger库来进行渲染
a)首先新建了一个Device类的对象, Device类封装了一些操作,包括UI的操作
b)调用Device类的GetUI()返回一个DefaultUI对象,recovery中涉及到三个UI类,三个类之间为继承关系,分别为DefaultUI、ScreenRecoveryUI、RecoveryUI
c)调用DefaultUI类的Init(), DefaultUI类没有Init()方法,因此将调用它的父类ScreenRecoveryUI的Init()
d)同理,调用ScreenRecoveryUI类的SetLocale()来标识几个比较特别的区域
e)同理,调用ScreenRecoveryUI类的SetBackground()设置初始状态的背景图
f)显示recovery的主界面,即一个选择菜单

int gr_init(void);             /* 初始化图形显示,主要是打开设备、分配内存、初始化一些参数 */  
int gr_init(void)
{
    gr_init_font();
    gr_backend = open_adf();
    if (gr_backend) {
        gr_draw = gr_backend->init(gr_backend);
        if (!gr_draw) {
            gr_backend->exit(gr_backend);
        }
    }
    if (!gr_draw) {
        gr_backend = open_drm();
        gr_draw = gr_backend->init(gr_backend);
    }
    if (!gr_draw) {
        gr_backend = open_fbdev();
        gr_draw = gr_backend->init(gr_backend);
        if (gr_draw == NULL) {
            return -1;
        }
    }
    overscan_offset_x = gr_draw->width * overscan_percent / 100;
    overscan_offset_y = gr_draw->height * overscan_percent / 100;
    gr_flip();
    gr_flip();
    return 0;
}

void gr_exit(void);            /* 注销图形显示,关闭设备并释放内存 */  

int gr_fb_width(void);         /* 获取屏幕的宽度 */  
int gr_fb_height(void);        /* 获取屏幕的高度 */  
gr_pixel *gr_fb_data(void);    /* 获取显示数据缓存的地址 */          
void gr_flip() /* 刷新显示内容 */
{
    gr_draw = gr_backend->flip(gr_backend);
}
static GRSurface* fbdev_flip(minui_backend* backend __unused)
{
    if (double_buffered) {
        // Change gr_draw to point to the buffer currently displayed,
        // then flip the driver so we're displaying the other buffer
        // instead.
        gr_draw = gr_framebuffer + displayed_buffer;
        // pan_     
    set_displayed_framebuffer(1-displayed_buffer);
   } else {
        // Copy from the in-memory surface to the framebuffer.
        memcpy(gr_framebuffer[0].data, gr_draw->data,
               gr_draw->height * gr_draw->row_bytes);
    }
    return gr_draw;
}
static void set_displayed_framebuffer(unsigned n)
{
    if (n > 1 || !double_buffered) return;

    vi.yres_virtual = gr_framebuffer[0].height * 2;
    vi.yoffset = n * gr_framebuffer[0].height;
    vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
    if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
        perror("active fb swap failed");
    }
    displayed_buffer = n;
}

static GRSurface* fbdev_init(minui_backend* backend)
{
    int fd = open("/dev/graphics/fb0", O_RDWR);
    if (fd == -1) {
        perror("cannot open fb0");
        return NULL;
    }
    fb_fix_screeninfo fi;
    if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
        perror("failed to get fb0 info");
        close(fd);
        return NULL;
    }
    if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
        perror("failed to get fb0 info");
        close(fd);
        return NULL;
    }
    ...
        void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (bits == MAP_FAILED) {
        perror("failed to mmap framebuffer");
        close(fd);
        return NULL;
    }

    memset(bits, 0, fi.smem_len);

    gr_framebuffer[0].width = vi.xres;
    gr_framebuffer[0].height = vi.yres;
    gr_framebuffer[0].row_bytes = fi.line_length;
    gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
    gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits);
    memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);

    /* check if we can use double buffering */
    if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
        double_buffered = true;
        printf("Framebuffer is double buffered, line_length=%d, smem_len=%d\n",
                fi.line_length, fi.smem_len);
        memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface));
        gr_framebuffer[1].data = gr_framebuffer[0].data +
            gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;

        gr_draw = gr_framebuffer+1;

    } else {
        double_buffered = false;

        // Without double-buffering, we allocate RAM for a buffer to
        // draw in, and then "flipping" the buffer consists of a
        // memcpy from the buffer we allocated to the framebuffer.

        gr_draw = (GRSurface*) malloc(sizeof(GRSurface));
        memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface));
        gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes);
        if (!gr_draw->data) {
            perror("failed to allocate in-memory surface");
            return NULL;
        }
    }
    // blank operation needs to place here, but the native code places blank operation at the last line!!
    // If set at the last line, we will see broken picture.
    fbdev_blank(backend, true);
    fbdev_blank(backend, false);

    memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
    fb_fd = fd;
    set_displayed_framebuffer(0);
    printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
    #if 0
    // Don't call blank operation here, because it will cause that panel shows broken picture.
    fbdev_blank(backend, true);
    fbdev_blank(backend, false);
    #endif
    return gr_draw;
}

// Logs about ioctl from set_displayed_framebuffer with para(FBIOPUT_VSCREENINFO):
[   46.077704] [<c0b08ac0>] (mdss_fb_pan_display.part.22) from [<c02a4f18>] (fb_pan_display+0xf8/0x138)
[   46.077717] [<c02a4f18>] (fb_pan_display) from [<c02a5320>] (fb_set_var+0x288/0x330)
[   46.077727] [<c02a5320>] (fb_set_var) from [<c02a55bc>] (do_fb_ioctl+0x194/0x5dc)
[   46.077739] [<c02a55bc>] (do_fb_ioctl) from [<c0138768>] (do_vfs_ioctl+0x480/0x57c)
[   46.077750] [<c0138768>] (do_vfs_ioctl) from [<c01388b0>] (SyS_ioctl+0x4c/0x74)
[   46.077760] [<c01388b0>] (SyS_ioctl) from [<c000e060>] (ret_fast_syscall+0x0/0x38)

// Logs about pixel commit to panel:
[   14.922314] [<c02b7888>] (mdp3_ctrl_pan_display) froms [<c03291e4>] (__mdss_fb_display_thread+0x2e8/0x444)
[   14.922390] [<c03291e4>] (__mdss_fb_display_thread) from [<c0042e9c>] (kthread+0xcc/0xe0)
[   14.922401] [<c0042e9c>] (kthread) from [<c000e100>] (ret_from_fork+0x14/0x34)

static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
{
    ...
    /* offset is double or tripple buffer offset */
    offset = fbi->var.xoffset * bpp + fbi->var.yoffset * fbi->fix.line_length;

    if (mfd->fbi->screen_base) {
        ...
        if (mdp3_ctrl_get_intf_type(mfd) == MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
            rc = mdss_spi_panel_kickoff(mdp3_session->panel,
                    fbi->screen_base + offset, fbi->fix.smem_len,
                    fbi->fix.line_length);
        } else {
            rc = mdp3_session->dma->update(mdp3_session->dma, (void *)(int)(mfd->iova + offset), mdp3_session->intf, mdp3_session->first_commit, NULL);
        }
    ...
}
void gr_fb_blank(bool blank);  /* panel power on/off */  
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);  /* 设置字体颜色 */  
void gr_fill(int x, int y, int w, int h);  /* 填充矩形区域,参数分别代表起始坐标、矩形区域大小 */
void gr_fill(int x1, int y1, int x2, int y2)
{
    x1 += overscan_offset_x;
    y1 += overscan_offset_y;
    x2 += overscan_offset_x;
    y2 += overscan_offset_y;
    if (outside(x1, y1) || outside(x2-1, y2-1)) return;
    unsigned char* p = gr_draw->data + y1 * gr_draw->row_bytes + x1 * gr_draw->pixel_bytes;
    if (gr_current_a == 255) {
        int x, y;
        for (y = y1; y < y2; ++y) {
            unsigned char* px = p;
            for (x = x1; x < x2; ++x) {
                *px++ = gr_current_r;
                *px++ = gr_current_g;
                *px++ = gr_current_b;
                px++;
            }
            p += gr_draw->row_bytes;
        }
    } else if (gr_current_a > 0) {
        int x, y;
        for (y = y1; y < y2; ++y) {
            unsigned char* px = p;
            for (x = x1; x < x2; ++x) {
                *px = (*px * (255-gr_current_a) + gr_current_r * gr_current_a) / 255;
                ++px;
                *px = (*px * (255-gr_current_a) + gr_current_g * gr_current_a) / 255;
                ++px;
                *px = (*px * (255-gr_current_a) + gr_current_b * gr_current_a) / 255;
                ++px;
                ++px;
            }
            p += gr_draw->row_bytes;
        }
    }
}
int gr_text(int x, int y, const char *s);  /* 显示字符串 */  
int gr_measure(const char *s);             /* 获取字符串在默认字库中占用的像素长度 */  
void gr_font_size(int *x, int *y);         /* 获取当前字库一个字符所占的长宽 */  

void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);  /* 填充由source指定的图片 */
void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
    if (source == NULL) return;
    if (gr_draw->pixel_bytes != source->pixel_bytes) {
        printf("gr_blit: source has wrong format; bpp src(%d), drawed(%d)\n",
                source->pixel_bytes, gr_draw->pixel_bytes);
        return;
    }
    dx += overscan_offset_x;
    dy += overscan_offset_y;
    if (outside(dx, dy) || outside(dx+w-1, dy+h-1)) return;
    unsigned char* src_p = source->data + sy*source->row_bytes + sx*source->pixel_bytes;
    unsigned char* dst_p = gr_draw->data + dy*gr_draw->row_bytes + dx*gr_draw->pixel_bytes;
    int i;
    for (i = 0; i < h; ++i) {
        memcpy(dst_p, src_p, w * source->pixel_bytes);
        src_p += source->row_bytes;
        dst_p += gr_draw->row_bytes;
    }
}

unsigned int gr_get_width(gr_surface surface);   /* 获取图片宽度 */  
unsigned int gr_get_height(gr_surface surface);  /* 获取图片高度 */  
/* 根据图片创建显示资源数据,name为图片在mk文件指定的相对路径 */  
int res_create_surface(const char* name, gr_surface* pSurface);  
void res_free_surface(gr_surface surface); /* 释放资源数据 */

/* update_screen_locked和update_progress_locked是recovery的UI部分的关键函数 */
update_screen_locked /* 包含两个操作,一是更新screen, 二是切换前后buffer */
update_progress_locked /* 用来更新进度条,因为显示的画面会一直在更新,所以这两个函数会在不同的地方被反复调用 */

setLocale /* ScreenRecoveryUI类的SetLocale, 该函数根据locale判断所用的字体是否属于阿拉伯语系,阿拉伯语的书写习惯是从右到左,如果是阿拉伯语系的话,就设置一个标志,后面根据这个标志决定从右到左显示文字或进度条; SetLocale的参数locale赋值逻辑是这样的,先从command文件中读取, command文件中设置locale的命令如"--locale=zh_CN“,如果没有传入locale,初始化过程中会尝试从/cache/recovery/last_locale中读取locale, 如果该文件也没有,则locale不会被赋值,就默认用English. */

LoadBitmap() /* 将png生成surface, 每个png图片对应一个surface, 所有surface存放在一个数组中 */
LoadLocalizedBitmap() /* 将区域文字所在的图片中的text信息根据当前的locale提取出来,生成对应的surface, 所以surface也存放在一个数组中 */

progress_thread /* pthread_create(&progress_t, NULL, progress_thread, NULL) 创建一个线程,该线程的任务是一个死循环,在该循环中不停地检测currentIcon以及progressBarType来决定是不是要更新进度条 */

draw_background_locked /* 先将整个渲染buffer填充为黑色,然后计算背景surface的长宽,文字surface的长宽,再结合fb的长宽计算出背景surface以及文字surface显示的坐标,有长宽和坐标就可以调用Pixelflinger的接口在渲染buffer上进行渲染 */

draw_progress_locked /* 原理与update_screen_locked函数类似,最终是将进度条的surface输出到渲染buffer */

3. recovery image compilation

Make recovery image, macro PRODUCT_AAPT_PREF_CONFIG is used to select the png type.

How to define PRODUCT_AAPT_PREF_CONFIG?

$ grep -nrisE PRODUCT_AAPT_PREF_CONFIG *
generic/armv7-a-neon/mini_common.mk:21:PRODUCT_AAPT_PREF_CONFIG := hdpi
google/atv/products/atv_generic.mk:33:PRODUCT_AAPT_PREF_CONFIG := xhdpi
zeusis/grover/grover.mk:12:PRODUCT_AAPT_PREF_CONFIG := mdpi
robbie@bf-rm-19:~/work/smartphone_work/grover/bootable/recovery$ ls
adb_install.cpp     device.cpp                install.h            recovery-persist.cpp  roots.h          verifier.cpp
adb_install.h       device.h                  interlace-frames.py  recovery-persist.rc   screen_ui.cpp    verifier.h
Android.mk          edify                     minadbd              recovery-refresh.cpp  screen_ui.h      wear_touch.cpp
applypatch          error_code.h              minui                recovery-refresh.rc   tests            wear_touch.h
asn1_decoder.cpp    etc                       minzip               res-560dpi            tools            wear_ui.cpp
asn1_decoder.h      fonts                     mtdutils             res-hdpi              ui.cpp           wear_ui.h
bootloader.h        fuse_sdcard_provider.cpp  NOTICE               res-mdpi              ui.h
bootloader_message  fuse_sdcard_provider.h    otafault             res-xhdpi-backup      uncrypt
CleanSpec.mk        fuse_sideload.cpp         print_sha1.h         res-xxhdpi            unique_fd.h
common.h            fuse_sideload.h           README.md            res-xxxhdpi           updater
default_device.cpp  install.cpp               recovery.cpp         roots.cpp             update_verifier
# bootable/recovery/android.mk
include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
    adb_install.cpp \
    asn1_decoder.cpp \
    device.cpp \
    fuse_sdcard_provider.cpp \
    install.cpp \
    recovery.cpp \
    roots.cpp \
    screen_ui.cpp \
    ui.cpp \
    verifier.cpp \
    wear_ui.cpp \
    wear_touch.cpp

LOCAL_MODULE := recovery
LOCAL_FORCE_STATIC_EXECUTABLE := true
ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
ifeq ($(HOST_OS),linux)
LOCAL_REQUIRED_MODULES := mkfs.f2fs
endif
endif
RECOVERY_API_VERSION := 3
RECOVERY_FSTAB_VERSION := 2
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CLANG := true

LOCAL_C_INCLUDES += \
    system/vold \
    system/extras/ext4_utils \
    system/core/adb

LOCAL_STATIC_LIBRARIES := \
    libbatterymonitor \
    libbootloader_message \
    libext4_utils_static \
    libsparse_static \
    libminzip \
    libz \
    libmtdutils \
    libminadbd \
    libfusesideload \
    libminui \
    libpng \
    libfs_mgr \
    libcrypto_static \
    libbase \
    libcutils \
    libutils \
    liblog \
    libselinux \
    libm \

LOCAL_HAL_STATIC_LIBRARIES := libhealthd

include \
    $(LOCAL_PATH)/applypatch/Android.mk \
    $(LOCAL_PATH)/bootloader_message/Android.mk \
    $(LOCAL_PATH)/edify/Android.mk \
    $(LOCAL_PATH)/minui/Android.mk \
    $(LOCAL_PATH)/minzip/Android.mk \
    $(LOCAL_PATH)/minadbd/Android.mk \
    $(LOCAL_PATH)/mtdutils/Android.mk \
    $(LOCAL_PATH)/otafault/Android.mk \
    $(LOCAL_PATH)/tests/Android.mk \
    $(LOCAL_PATH)/tools/Android.mk \
    $(LOCAL_PATH)/uncrypt/Android.mk \
    $(LOCAL_PATH)/updater/Android.mk \
    $(LOCAL_PATH)/update_verifier/Android.mk

# bootable/recovery/minui/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
    events.cpp \
    graphics.cpp \
    graphics_adf.cpp \
    graphics_drm.cpp \
    graphics_fbdev.cpp \
    resources.cpp

LOCAL_WHOLE_STATIC_LIBRARIES += libadf
LOCAL_WHOLE_STATIC_LIBRARIES += libdrm
LOCAL_STATIC_LIBRARIES += libpng
LOCAL_MODULE := libminui
LOCAL_CLANG := true
TARGET_RECOVERY_FORMAT=$(TARGET_RECOVERY_PIXEL_FORMAT)

ifndef TARGET_RECOVERY_FORMAT
  LOCAL_CFLAGS += -DRECOVERY_RGB565
endif
ifneq ($(TARGET_RECOVERY_OVERSCAN_PERCENT),)
  LOCAL_CFLAGS += -DOVERSCAN_PERCENT=$(TARGET_RECOVERY_OVERSCAN_PERCENT)
else
  LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0
endif
include $(BUILD_STATIC_LIBRARY)

# Used by OEMs for factory test images.
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_MODULE := libminui
LOCAL_WHOLE_STATIC_LIBRARIES += libminui
LOCAL_SHARED_LIBRARIES := libpng
include $(BUILD_SHARED_LIBRARY)
# vim build/core/Makefile

# Recovery image exists if we are building recovery, or building recovery as boot.
ifneq (,$(INSTALLED_RECOVERYIMAGE_TARGET)$(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)))

INTERNAL_RECOVERYIMAGE_FILES := $(filter $(TARGET_RECOVERY_OUT)/%, \
    $(ALL_DEFAULT_INSTALLED_MODULES))

recovery_initrc := $(call include-path-for, recovery)/etc/init.rc
recovery_sepolicy := $(call intermediates-dir-for,ETC,sepolicy.recovery)/sepolicy.recovery
recovery_kernel := $(INSTALLED_KERNEL_TARGET) # same as a non-recovery system
recovery_ramdisk := $(PRODUCT_OUT)/ramdisk-recovery.img
recovery_build_prop := $(intermediate_system_build_prop)
recovery_resources_common := $(call include-path-for, recovery)/res

# Set recovery_density to the density bucket of the device.
recovery_density := unknown
ifneq (,$(PRODUCT_AAPT_PREF_CONFIG))
# If PRODUCT_AAPT_PREF_CONFIG includes a dpi bucket, then use that value.
recovery_density := $(filter %dpi,$(PRODUCT_AAPT_PREF_CONFIG))
else
# Otherwise, use the default medium density.
recovery_densities := mdpi
endif

ifneq (,$(wildcard $(recovery_resources_common)-$(recovery_density)))
recovery_resources_common := $(recovery_resources_common)-$(recovery_density)
else
recovery_resources_common := $(recovery_resources_common)-xhdpi
endif

# Select the 18x32 font on high-density devices (xhdpi and up); and
# the 12x22 font on other devices.  Note that the font selected here
# can be overridden for a particular device by putting a font.png in
# its private recovery resources.

ifneq (,$(filter xxxhdpi 560dpi xxhdpi 400dpi xhdpi,$(recovery_density)))
recovery_font := $(call include-path-for, recovery)/fonts/18x32.png
else
recovery_font := $(call include-path-for, recovery)/fonts/12x22.png
endif

# cp -rf bootable/recovery/res-xhdpi/* out/target/product/grover/recovery/root/res ) && (cp -f bootable/recovery/fonts/12x22.png out/target/product/grover/recovery/root/res/images/font.png ) && (cp -f device/zeusis/grover/recovery.fstab out/target/product/grover/recovery/root/etc/recovery.fstab

4. Get android and cellphone information

grover:/ # getprop ro.build.display.id
getprop ro.build.display.id
grover-userdebug 7.1.1 XXXXCN00X1000DPX1704160 eng.buildf.20170416.024035 test-keys
grover:/ # getprop ro.build.id
getprop ro.build.id
XXXXCN00X1000DPX1704160
grover:/ # getprop ro.boot.hardware
getprop ro.boot.hardware
qcom
grover:/ # getprop ro.build.product
getprop ro.build.product
grover
grover:/ # getprop ro.build.version.sdk
getprop ro.build.version.sdk
25
grover:/ # getprop ro.build.version.release
getprop ro.build.version.release
7.1.1
grover:/ # getprop ro.build.id
getprop ro.build.id
XXXXCN00X1000DPX1704160

[    7.400371] ro.opengles.version=196608
[    7.400399] ro.sf.lcd_density=240
[    7.400594] persist.camera.longshot.stages=1
[    7.401097] ro.dalvik.vm.native.bridge=0
[    7.401117] dalvik.vm.isa.arm.variant=cortex-a7
[    7.401137] dalvik.vm.isa.arm.features=default
[    7.401152] dalvik.vm.usejit=true
[    7.401165] dalvik.vm.heapsize=256m
[    7.401180] dalvik.vm.lockprof.threshold=500
[    7.401194] dalvik.vm.dex2oat-Xms=64m
[    7.401212] dalvik.vm.dex2oat-Xmx=512m
[    7.401227] dalvik.vm.heapmaxfree=8m
[    7.401241] dalvik.vm.heapminfree=512k
[    7.401255] dalvik.vm.heapstartsize=8m
[    7.401268] dalvik.vm.appimageformat=lz4
[    7.401282] dalvik.vm.usejitprofiles=true
[    7.401296] dalvik.vm.dex2oat-threads=4
[    7.401309] dalvik.vm.heapgrowthlimit=96m
[    7.401322] dalvik.vm.stack-trace-file=/data/anr/traces.txt
[    7.401341] dalvik.vm.image-dex2oat-Xms=64m
[    7.401356] dalvik.vm.image-dex2oat-Xmx=64m
[    7.401369] dalvik.vm.boot-dex2oat-threads=4
[    7.401383] dalvik.vm.image-dex2oat-filter=verify-at-runtime
[    7.401397] dalvik.vm.zygotemaxfailedboots=5
[    7.401410] dalvik.vm.heaptargetutilization=0.75
[    7.401429] ro.config.ringtone=Ring_Synth_04.ogg
[    7.401443] ro.config.alarm_alert=Alarm_Classic.ogg
[    7.401457] ro.config.max_starting_bg=8
[    7.401471] ro.config.notification_sound=pixiedust.ogg
[    7.401571] ro.build.fingerprint=Zeusis/grover/grover:7.1.1/NMF26F/robbie05161640:eng/test-keys
[    7.401675] av.offload.enable=true
[    7.401699] db.log.slow_query_threshold=100
[    7.401714] mm.enable.qcom_parser=262143
[    7.401728] pm.dexopt.boot=verify-at-runtime
[    7.401746] pm.dexopt.ab-ota=speed-profile
[    7.401761] pm.dexopt.install=interpret-only
[    7.401774] pm.dexopt.core-app=speed
[    7.401788] pm.dexopt.bg-dexopt=speed-profile
[    7.401802] pm.dexopt.first-boot=verify-at-runtime
[    7.401815] pm.dexopt.shared-apk=speed
[    7.401834] pm.dexopt.nsys-library=speed
[    7.401848] pm.dexopt.forced-dexopt=speed
[    7.401862] ro.am.reschedule_service=true
[    7.401875] ro.fm.transmitter=false
[    7.401888] ro.qc.sdk.audio.ssr=false
[    7.401902] ro.qc.sdk.audio.fluencetype=none
[    7.401915] ro.com.android.dataroaming=true
[    7.401929] ro.frp.pst=/dev/block/bootdevice/by-name/config
[    7.401943] ro.gps.agps_provider=1
[    7.401995] ro.qti.sdk.sensors.gestures=false
[    7.402013] ro.qti.sensors.tap=false
[    7.402028] ro.qti.sensors.tilt=false
[    7.402042] ro.qti.sensors.facing=false
[    7.402055] ro.qti.sensors.qheart=true
[    7.402108] ro.qti.sensors.scrn_ortn=false
[    7.402126] ro.qti.sensors.wrist_tilt=true
[    7.402141] ro.qti.sensors.tilt_detector=false
[    7.402154] ro.qti.userspacectl=true
[    7.402167] ro.sys.fw.bservice_age=5000
[    7.402181] ro.sys.fw.bg_apps_limit=16
[    7.402194] ro.sys.fw.bservice_limit=5
[    7.402207] ro.sys.fw.bservice_enable=true
[    7.402220] ro.sys.fw.empty_app_percent=50
[    7.402234] ro.sys.fw.use_trim_settings=true
[    7.402247] ro.sys.fw.trim_cache_percent=100
[    7.402261] ro.sys.fw.trim_empty_percent=100
[    7.402280] ro.sys.fw.trim_enable_memory=1073741824
[    7.402297] ro.sys.usb.default.config=diag,serial_smd,rmnet_qti_bam,adb
[    7.402310] ro.sys.umsdirtyratio=20
[    7.402324] ro.boot.console=ttyHSL0
[    7.402337] ro.boot.baseband=msm
[    7.402351] ro.boot.hardware=qcom
[    7.402364] ro.boot.serialno=7d28cee
[    7.402377] ro.boot.bootdevice=7824900.sdhci
[    7.402390] ro.boot.bootdevice.type=eMMC
[    7.402403] ro.hwui.text_large_cache_height=2048
[    7.402417] ro.qcom.screencolor=1
[    7.402430] ro.wifi.channels=
[    7.402443] ro.allow.mock.location=1
[    7.402457] ro.board.platform=msm8909
[    7.402470] ro.build.id=NMF26F
[    7.402483] ro.build.date=Tue May 16 16:40:26 CST 2017
[    7.402497] ro.build.date.utc=1494924026
[    7.402510] ro.build.host=bf-rm-19
[    7.402524] ro.build.tags=test-keys
[    7.402537] ro.build.type=eng
[    7.402550] ro.build.user=robbie
[    7.402564] ro.build.flavor=grover-eng
[    7.402578] ro.build.display.id=grover-eng 7.1.1 NMF26F eng.robbie.20170516.164026 test-keys
[    7.402591] ro.build.product=grover
[    7.402605] ro.build.version.sdk=25
[    7.402618] ro.build.version.base_os=
[    7.402631] ro.build.version.release=7.1.1
[    7.402645] ro.build.version.codename=REL
[    7.402658] ro.build.version.incremental=eng.robbie.20170516.164026
[    7.402672] ro.build.version.preview_sdk=0
[    7.402685] ro.build.version.all_codenames=REL
[    7.402699] ro.build.version.security_patch=2016-12-05
[    7.402713] ro.build.description=grover-eng 7.1.1 NMF26F eng.robbie.20170516.164026 test-keys
[    7.402734] ro.build.characteristics=nosdcard,watch
[    7.402749] ro.kernel.android.checkjni=1
[    7.402763] ro.secure=0
[    7.402776] ro.vendor.extension_library=libqti-perfd-client.so
[    7.402790] ro.zygote=zygote32
[    7.402803] ro.carrier=unknown
[    7.402816] ro.product.cpu.abi=armeabi-v7a
[    7.402830] ro.product.cpu.abi2=armeabi
[    7.402843] ro.product.cpu.abilist=armeabi-v7a,armeabi
[    7.402857] ro.product.cpu.abilist32=armeabi-v7a,armeabi
[    7.402871] ro.product.cpu.abilist64=
[    7.402884] ro.product.name=grover
[    7.402897] ro.product.board=msm8909
[    7.402910] ro.product.brand=Zeusis
[    7.402924] ro.product.model=301A
[    7.402937] ro.product.device=grover
[    7.402951] ro.product.locale=en-US
[    7.402964] ro.product.manufacturer=Zeusis
[    7.402977] ro.sensors.wearqstp=1
[    7.402990] ro.sensors.wearqstp.lock=0
[    7.403004] ro.baseband=msm
[    7.403018] ro.bootmode=unknown
[    7.403031] ro.hardware=qcom
[    7.403044] ro.qualcomm.cabl=2
[    7.403058] ro.revision=0
[    7.403071] ro.serialno=7d28cee
[    7.403085] ro.bootimage.build.date=Tue May 16 16:40:26 CST 2017
[    7.403098] ro.bootimage.build.date.utc=1494924026
[    7.403112] ro.bootimage.build.fingerprint=Zeusis/grover/grover:7.1.1/NMF26F/robbie05161640:eng/test-keys
[    7.403126] ro.emmc_size=8GB
[    7.403139] ro.telephony.default_network=9
[    7.403152] ro.bootloader=unknown
[    7.403166] ro.debuggable=1
[    7.403180] ro.core_ctl_max_cpu=4
[    7.403193] ro.core_ctl_min_cpu=2
[    7.403207] ro.use_data_netmgrd=true
[    7.403220] ro.cutoff_voltage_mv=3200
[    7.403234] mmp.enable.3g2=true
[    7.403247] rec.playback.conc.disabled=true
[    7.403261] use.voice.path.for.pcm.voip=true
[    7.403274] init.svc.adbd=running
[    7.403287] init.svc.healthd=running
[    7.403300] init.svc.ueventd=running
[    7.403314] init.svc.recovery=running
[    7.403328] rild.libargs=-d /dev/smd0
[    7.403341] rild.libpath=/system/vendor/lib/libril-qc-qmi-1.so
[    7.403385] audio.dolby.ds2.enabled=true
[    7.403402] audio.offload.min.duration.secs=30
[    7.403417] audio.offload.buffer.size.kb=64
[    7.403431] audio.offload.disable=0
[    7.403445] audio.offload.gapless.enabled=true
[    7.403459] media.stagefright.enable-aac=true
[    7.403472] media.stagefright.enable-qcp=true
[    7.403486] media.stagefright.enable-http=true
[    7.403506] media.stagefright.enable-scan=true
[    7.403520] media.stagefright.use-awesome=false
[    7.403535] media.stagefright.enable-fma2dp=true
[    7.403548] media.stagefright.enable-player=true
[    7.403562] media.aac_51_output_enabled=true
[    7.403575] voice.voip.conc.disabled=true
[    7.403588] voice.record.conc.disabled=true
[    7.403601] voice.playback.conc.disabled=true
[    7.403615] tunnel.audio.encode=false
[    7.403629] persist.bt.a2dp_offload_cap=0
[    7.403642] persist.mm.sta.enable=1
[    7.403655] persist.cne.feature=1
[    7.403669] persist.gps.qc_nlp_in_use=1
[    7.403683] persist.hwc.enable_vds=1
[    7.403696] persist.loc.nlp_name=com.qualcomm.location
[    7.403710] persist.data.mode=concurrent
[    7.403723] persist.data.netmgrd.qos.enable=true
[    7.403737] persist.demo.hdmirotationlock=false
[    7.403750] persist.rild.nitz_plmn=
[    7.403763] persist.rild.nitz_long_ons_0=
[    7.403777] persist.rild.nitz_long_ons_1=
[    7.403791] persist.rild.nitz_long_ons_2=
[    7.403805] persist.rild.nitz_long_ons_3=
[    7.403818] persist.rild.nitz_short_ons_0=
[    7.403832] persist.rild.nitz_short_ons_1=
[    7.403845] persist.rild.nitz_short_ons_2=
[    7.403858] persist.rild.nitz_short_ons_3=
[    7.403872] persist.timed.enable=true
[    7.403886] persist.tuning.qdcm=1
[    7.403899] persist.zygote.async_preload=true
[    7.403913] persist.display.framerate=30
[    7.403926] persist.fuse_sdcard=true
[    7.403939] keyguard.no_require_sim=true
[    7.403952] telephony.lteOnCdmaDevice=1
[    7.403965] DEVICE_PROVISIONED=1
[    7.403979] persist.debug.wfd.enable=1
[    7.403997] sys.audio.init=false
[    7.404012] persist.audio.fluence.speaker=true
[    7.404026] persist.audio.fluence.voicerec=false
[    7.404039] persist.audio.fluence.voicecall=true
[    7.404062] debug.sf.hw=1
[    7.404082] debug.sf.hwc.canUseABC=1
[    7.404096] debug.sf.swaprect=1
[    7.404114] debug.egl.hw=1
[    7.404128] debug.hwc.dynThreshold=4.5
[    7.404142] debug.atrace.tags.enableflags=0
[    7.404156] debug.mdpcomp.logs=0
[    7.404174] debug.composition.type=dyn
[    7.404477] sys.usb.ffs.ready=1
[    7.404679] dev.pm.dyn_samplingrate=1
[    7.404698] net.bt.name=Android
[    7.404725] net.change=net.bt.name
[    7.404740] sys.hwc.gpu_perf_mode=1
[    7.404754] sys.use_fifo_ui=1
[    7.404768] persist.sys.usb.config=diag,serial_smd,rmnet_qti_bam,adb
[    7.404782] persist.sys.dalvik.vm.lib.2=libart.so
[    7.404800] persist.sys.synaptics_dsx.qhd=false
[    7.404895] ril.subscription.types=NV,RUIM
[    7.404914] persist.radio.custom_ecc=1
[    7.404933] persist.radio.sib16_support=1
[    7.404948] persist.radio.apm_sim_not_pwdn=1

5. recovery by command

/*
 * The recovery tool communicates with the main system through /cache files.
 *   /cache/recovery/command - INPUT - command line for tool, one arg per line
 *   /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
 *   /cache/recovery/intent - OUTPUT - intent that was passed in
 *
 * The arguments which may be supplied in the recovery.command file:
 *   --send_intent=anystring - write the text out to recovery.intent
 *   --update_package=path - verify install an OTA package file
 *   --wipe_data - erase user data (and cache), then reboot
 *   --wipe_cache - wipe cache (but not user data), then reboot
 *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
 *   --just_exit - do nothing; exit and reboot
 *
 * After completing, we remove /cache/recovery/command and reboot.
 * Arguments may also be supplied in the bootloader control block (BCB).
 * These important scenarios must be safely restartable at any point:
 *
 * FACTORY RESET
 * 1. user selects "factory reset"
 * 2. main system writes "--wipe_data" to /cache/recovery/command
 * 3. main system reboots into recovery
 * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
 *    -- after this, rebooting will restart the erase --
 * 5. erase_volume() reformats /data
 * 6. erase_volume() reformats /cache
 * 7. finish_recovery() erases BCB
 *    -- after this, rebooting will restart the main system --
 * 8. main() calls reboot() to boot main system
 *
 * OTA INSTALL
 * 1. main system downloads OTA package to /cache/some-filename.zip
 * 2. main system writes "--update_package=/cache/some-filename.zip"
 * 3. main system reboots into recovery
 * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
 *    -- after this, rebooting will attempt to reinstall the update --
 * 5. install_package() attempts to install the update
 *    NOTE: the package install must itself be restartable from any point
 * 6. finish_recovery() erases BCB
 *    -- after this, rebooting will (try to) restart the main system --
 * 7. ** if install failed **
 *    7a. prompt_and_wait() shows an error icon and waits for the user
 *    7b; the user reboots (pulling the battery, etc) into the main system
 * 8. main() calls maybe_install_firmware_update()
 *    ** if the update contained radio/hboot firmware **:
 *    8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
 *        -- after this, rebooting will reformat cache & restart main system --
 *    8b. m_i_f_u() writes firmware image into raw cache partition
 *    8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
 *        -- after this, rebooting will attempt to reinstall firmware --
 *    8d. bootloader tries to flash firmware
 *    8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
 *        -- after this, rebooting will reformat cache & restart main system --
 *    8f. erase_volume() reformats /cache
 *    8g. finish_recovery() erases BCB
 *        -- after this, rebooting will (try to) restart the main system --
 * 9. main() calls reboot() to boot main system
 */

static const struct option OPTIONS[] = {
  { "send_intent", required_argument, NULL, 'i' },
  { "update_package", required_argument, NULL, 'u' },
  { "retry_count", required_argument, NULL, 'n' },
  { "wipe_data", no_argument, NULL, 'w' },
  { "wipe_cache", no_argument, NULL, 'c' },
  { "show_text", no_argument, NULL, 't' },
  { "sideload", no_argument, NULL, 's' },
  { "sideload_auto_reboot", no_argument, NULL, 'a' },
  { "just_exit", no_argument, NULL, 'x' },
  { "locale", required_argument, NULL, 'l' },
  { "stages", required_argument, NULL, 'g' },
  { "shutdown_after", no_argument, NULL, 'p' },
  { "reason", required_argument, NULL, 'r' },
  { "security", no_argument, NULL, 'e'},
  { "wipe_ab", no_argument, NULL, 0 },
  { "wipe_package_size", required_argument, NULL, 0 },
  { NULL, 0, NULL, 0 },
};

static const char *CACHE_LOG_DIR = "/cache/recovery";
static const char *COMMAND_FILE = "/cache/recovery/command";
static const char *INTENT_FILE = "/cache/recovery/intent";
static const char *LOG_FILE = "/cache/recovery/log";
static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
static const char *LOCALE_FILE = "/cache/recovery/last_locale";
static const char *CONVERT_FBE_DIR = "/tmp/convert_fbe";
static const char *CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe";
static const char *CACHE_ROOT = "/cache";
static const char *DATA_ROOT = "/data";
static const char *SDCARD_ROOT = "/sdcard";
static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
static const char *LAST_LOG_FILE = "/cache/recovery/last_log";

grover:/cache/recovery # echo "--wipe_data\n--show_text" > command
echo "--wipe_data\n--show_text" > command
grover:/cache/recovery # cat command
cat command
--wipe_data
--show_text
// update android operating system and wipe data
grover:/cache/recovery # echo "--update_package=/data/update.zip\n--wipe_data\
n--show_text\n--locale=zh_CN" >command
grover:/cache/recovery # exit
exit

E:\work\adb>adb reboot recovery

// Then wipe data will be executed
grover:/cache # ls
ls
backup backup_stage lost+found recovery
grover:/cache # cd recovery
grover:/cache/recovery # ls
ls // log has been stored here
last_install last_kmsg last_kmsg.1 last_locale last_log last_log.1 log
grover:/cache/recovery #

// Now we can look some logs
[    0.000689] __bionic_open_tzdata: couldn't find any tzdata when looking for GMT!
[    0.000884] Starting recovery (pid 266) on Thu Jan  1 06:49:44 1970
[    0.001325] recovery filesystem table
[    0.001353] =========================
[    0.001374]   0 /system ext4 /dev/block/bootdevice/by-name/system 0
[    0.001391]   1 /cache ext4 /dev/block/bootdevice/by-name/cache 0
[    0.001405]   2 /data ext4 /dev/block/bootdevice/by-name/userdata -16384
[    0.001419]   3 /sdcard vfat /dev/block/mmcblk1p1 0
[    0.001449]   4 /boot emmc /dev/block/bootdevice/by-name/boot 0
[    0.001468]   5 /recovery emmc /dev/block/bootdevice/by-name/recovery 0
[    0.001483]   6 /misc emmc /dev/block/bootdevice/by-name/misc 0
[    0.001497]   7 /tmp ramdisk ramdisk 0
[    0.001511]
[    0.044070] I:Got arguments from /cache/recovery/command
[    0.046359] locale is [en_US]
[    0.046395] stage is []
[    0.046424] reason is [(null)]
[    0.292923] fb0 reports (possibly inaccurate):
[    0.292963]   vi.bits_per_pixel = 16
[    0.292980]   vi.red.offset   =  11   .length =   5
[    0.292995]   vi.green.offset =   5   .length =   6
[    0.293011]   vi.blue.offset  =   0   .length =   5
[    7.398906] Command: "/sbin/recovery" "--wipe_data"
[   25.476375] Data wipe complete.

6. menu (volume up, down, power key) selection logic

void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
                                 int initial_selection) {
    pthread_mutex_lock(&updateMutex);
    if (text_rows_ > 0 && text_cols_ > 0) {
        menu_headers_ = headers;
        size_t i = 0;
        for (; i < text_rows_ && items[i] != nullptr; ++i) {
            strncpy(menu_[i], items[i], text_cols_ - 1);
            menu_[i][text_cols_ - 1] = '\0';
        }
        menu_items = i;
        show_menu = true;
        menu_sel = initial_selection;
        update_screen_locked();
    }
    pthread_mutex_unlock(&updateMutex);
}

int ScreenRecoveryUI::SelectMenu(int sel) {
    pthread_mutex_lock(&updateMutex);
    if (show_menu) {
        int old_sel = menu_sel;
        menu_sel = sel;

        // Wrap at top and bottom.
        if (menu_sel < 0) menu_sel = menu_items - 1;
        if (menu_sel >= menu_items) menu_sel = 0;

        sel = menu_sel;
        if (menu_sel != old_sel) update_screen_locked();
    }
    pthread_mutex_unlock(&updateMutex);
    return sel;
}

void ScreenRecoveryUI::EndMenu() {
    pthread_mutex_lock(&updateMutex);
    if (show_menu && text_rows_ > 0 && text_cols_ > 0) {
        show_menu = false;
        update_screen_locked();
    }
    pthread_mutex_unlock(&updateMutex);
}

static int
get_menu_selection(const char* const * headers, const char* const * items,
                   int menu_only, int initial_selection, Device* device) {
    // throw away keys pressed previously, so user doesn't
    // accidentally trigger menu items.
    ui->FlushKeys();

    ui->StartMenu(headers, items, initial_selection);
    int selected = initial_selection;
    int chosen_item = -1;

    while (chosen_item < 0) {
        int key = ui->WaitKey();
        int visible = ui->IsTextVisible();

        if (key == -1) {   // ui_wait_key() timed out
            if (ui->WasTextEverVisible()) {
                continue;
            } else {
                LOGI("timed out waiting for key input; rebooting.\n");
                ui->EndMenu();
                return 0; // XXX fixme
            }
        }
    //   - move the menu highlight (kHighlight{Up,Down})
    //   - invoke the highlighted item (kInvokeItem)
    //   - do nothing (kNoAction)
    //   - invoke a specific action (a menu position: any non-negative number)
        int action = device->HandleMenuKey(key, visible);

        if (action < 0) {
            switch (action) {
                case Device::kHighlightUp:
                    selected = ui->SelectMenu(--selected);
                    break;
                case Device::kHighlightDown:
                    selected = ui->SelectMenu(++selected);
                    break;
                case Device::kInvokeItem:
                    chosen_item = selected;
                    break;
                case Device::kNoAction:
                    break;
            }
        } else if (!menu_only) {
            chosen_item = action;
        }
    }
    ui->EndMenu();
    return chosen_item;
}

7. png format and how to parse?

7.1 8-Bit Palette, Grayscale, and Truecolor

PNG was designed to replace GIF for online purposes and the inconsistently implemented TIFF format for image storage and printing. As a result, there are three types of PNG files: indexed color (palette images), grayscale, and truecolor.

7.1.1 8-Bit Palette Images

Like GIFs, PNGs can be saved as 8-bit indexed color. This means they can contain up to 256 colors, the maximum number that 8 bits of information can define. Indexed color means the set of colors in the image, its palette, are stored in a color table. Each pixel in the image contains a reference (or “index”) to its corresponding color and position in the color table.

Although 8-bit is the maximum, PNGs may be saved at lower bit-depths (1-, 2-, and 4-bit, specifically) as well, thus reducing the maximum number of colors in the image (and the file size).

Indexed color PNGs are also capable of containing multiple transparency levels within the index color palette itself (performing a task usually assigned to an Alpha Channel).

7.1.2 Grayscale

PNGs can also support 16-bit grayscale images – that’s as many as 65,536 shades of gray (216), enabling black and white photographs and illustrations to be stored with enormous subtlety of detail. This is useful for medical imaging and other types of imaging where detail must be maintained, but it is not much of an advantage for images intended for web delivery due to the inherent limitations of low-resolution images. Grayscale images are supported at 1-, 2-, 4-, and 8-bit depths as well.

7.1.3 Truecolor

PNG can support 24-bit and 48-bit truecolor images. “Truecolor” (or the “true color space” as it is referred to in this book) refers to the full color range (millions of colors) that can be defined by combinations of red, green, and blue (RGB) light on a computer monitor. Truecolor images do not use color tables and are limited only by the number of bits available to describe values for each color channel. In PNG format, each channel can be defined by 8-bit or 16-bit information. It should be noted that 48-bit images are useless for the Web. Even 24-bit should be used with care (other formats offer smaller file sizes with acceptable image quality).

static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr,
                    png_uint_32* width, png_uint_32* height, png_byte* channels) {
    char resPath[256];
    unsigned char header[8];
    int result = 0;
    int color_type, bit_depth;
    size_t bytesRead;

    snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
    resPath[sizeof(resPath)-1] = '\0';
    FILE* fp = fopen(resPath, "rb");
    if (fp == NULL) {
        result = -1;
        goto exit;
    }
    bytesRead = fread(header, 1, sizeof(header), fp);
    if (bytesRead != sizeof(header)) {
        result = -2;
        goto exit;
    }
    if (png_sig_cmp(header, 0, sizeof(header))) {
        result = -3;
        goto exit;
    }
    *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    *info_ptr = png_create_info_struct(*png_ptr);
    if (setjmp(png_jmpbuf(*png_ptr))) {
        result = -6;
        goto exit;
    }

    png_init_io(*png_ptr, fp);
    png_set_sig_bytes(*png_ptr, sizeof(header));
    png_read_info(*png_ptr, *info_ptr);

    png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth,
            &color_type, NULL, NULL, NULL);

    *channels = png_get_channels(*png_ptr, *info_ptr);

    if ((bit_depth == 8 || bit_depth == 24) && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) {
        // 8-bit RGB images: great, nothing to do.
    } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
        // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
        png_set_expand_gray_1_2_4_to_8(*png_ptr);
    } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) {
        // paletted images: expand to 8-bit RGB.  Note that we DON'T
        // currently expand the tRNS chunk (if any) to an alpha
        // channel, because minui doesn't support alpha channels in
        // general.
        png_set_palette_to_rgb(*png_ptr);
        *channels = 3;
    } else {
        fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n",
                bit_depth, *channels, color_type);
        result = -7;
        goto exit;
    }
    return result;
  exit:
    if (result < 0)
        png_destroy_read_struct(png_ptr, info_ptr, NULL);
    if (fp != NULL)
        fclose(fp);
    return result;
}

static void transform_rgb_to_draw(unsigned char* input_row,
                                  unsigned char* output_row,
                                  int channels, int width) {
    int x;
    unsigned char* ip = input_row;
    unsigned char* op = output_row;
#ifdef RECOVERY_RGB565
    unsigned short *rgb565 = (unsigned short *)output_row;
#endif

#ifndef RECOVERY_RGB565
    switch (channels) {
        case 1:
            // expand gray level to RGBX
            for (x = 0; x < width; ++x) {
                *op++ = *ip;
                *op++ = *ip;
                *op++ = *ip;
                *op++ = 0xff;
                ip++;
            }
            break;
        case 3:
            // expand RGBA to RGBX
            for (x = 0; x < width; ++x) {
                *op++ = *ip++;
                *op++ = *ip++;
                *op++ = *ip++;
                *op++ = 0xff;
            }
            break;
        case 4:
            // copy RGBA to RGBX
            memcpy(output_row, input_row, width*4);
            break;
    }
#else
    switch (channels) {
        case 1:
            for (x = 0; x < width; ++x) {
                *rgb565 = (*ip & 0x1f << 0) |
                        (*ip & 0x3f << 5) |
                        (*ip * 0x1f << 11);
                ++rgb565;
                ++ip;
            }
            break;
        case 3:
            short r, g, b;
            for (x = 0; x < width; ++x) {
                /* png RGB888 to RGB565 */
                r = *ip >> 3;
                g = *(ip + 1) >> 2;
                b = *(ip + 2) >> 3;
                *rgb565 = (r << 11) | (g << 5) | (b << 0);
                ip += 3;
                ++rgb565;
            }
            break;
        default:
            printf("4 channels in png can't be supported\n");
            break;
    }
#endif
}

int res_create_display_surface(const char* name, GRSurface** pSurface) {
    GRSurface* surface = NULL;
    int result = 0;
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
    png_uint_32 width, height;
    png_byte channels;
    unsigned char* p_row;
    unsigned int y;

    *pSurface = NULL;
    result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
    if (result < 0) return result;
    surface = init_display_surface(width, height);
    if (surface == NULL) {
        result = -8;
        goto exit;
    }
    printf("%s.png has %d channels\n", name, channels);
#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
    png_set_bgr(png_ptr);
#endif
    p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
    for (y = 0; y < height; ++y) {
        png_read_row(png_ptr, p_row, NULL);
        transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width);
    }
    free(p_row);
    *pSurface = surface;
  exit:
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    if (result < 0 && surface != NULL) free(surface);
    return result;
}

NOTE:
PNG picture can only support rectangle region!!!!!

Different locate has different font

Robot designed by google with the name: icon_error

Designed recovery GUI

7.2 Modified content
Author: Yang.Haibing
Date: Thu May 25 11:42:40 2017 +0800

Support for display information in recovery mode
Change-Id: I6d878b98238dfb824889b692e129ee4e879cb2c2

    modified:   minui/graphics.cpp
    modified:   minui/minui.h
    modified:   minui/resources.cpp
    modified:   recovery.cpp
    modified:   res-mdpi/images/icon_error.png
    modified:   res-mdpi/images/progress_empty.png
    modified:   res-mdpi/images/progress_fill.png
    modified:   screen_ui.cpp
    modified:   minui/Android.mk
    modified:   minui/graphics_fbdev.cpp

8. Display text on screen in recovery mode

// In screen_ui.cpp
void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
    std::string str;
    android::base::StringAppendV(&str, fmt, ap);

    if (copy_to_stdout) {
        fputs(str.c_str(), stdout);
    }

    if (gr_target_is_rgb565()) return;

    pthread_mutex_lock(&updateMutex);
    if (text_rows_ > 0 && text_cols_ > 0) {
        for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
            if (*ptr == '\n' || text_col_ >= text_cols_) {
                text_[text_row_][text_col_] = '\0';
                text_col_ = 0;
                text_row_ = (text_row_ + 1) % text_rows_;
                if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
            }
            if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
        }
        text_[text_row_][text_col_] = '\0';
        update_screen_locked();
    }
    pthread_mutex_unlock(&updateMutex);
}

void ScreenRecoveryUI::update_screen_locked() {
    draw_screen_locked();
    gr_flip();
}

// Redraw everything on the screen.  Does not flip pages.
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::draw_screen_locked() {
    // show_text can decide whether text is showed on screen!!!
    if (!show_text) {
        // show png on screen, if we don't need show text on screen, just keep show_text = 0
        draw_background_locked();
        draw_foreground_locked();
    } else {
        // show text on screen!! if we need show text on screen, set show_text to 1
        gr_color(0, 0, 0, 255);
        gr_clear();

        int y = 0;
        if (show_menu) {
            char recovery_fingerprint[PROPERTY_VALUE_MAX];
            property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, "");

            SetColor(INFO);
            DrawTextLine(TEXT_INDENT, &y, "Android Recovery", true);
            for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
                DrawTextLine(TEXT_INDENT, &y, chunk.c_str(), false);
            }
            DrawTextLines(TEXT_INDENT, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);

            SetColor(HEADER);
            DrawTextLines(TEXT_INDENT, &y, menu_headers_);

            SetColor(MENU);
            DrawHorizontalRule(&y);
            y += 4;
            for (int i = 0; i < menu_items; ++i) {
                if (i == menu_sel) {
                    // Draw the highlight bar.
                    SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG);
                    gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2);
                    // Bold white text for the selected item.
                    SetColor(MENU_SEL_FG);
                    gr_text(4, y, menu_[i], true);
                    SetColor(MENU);
                } else {
                    gr_text(4, y, menu_[i], false);
                }
                y += char_height_ + 4;
            }
            DrawHorizontalRule(&y);
        }

        // display from the bottom up, until we hit the top of the
        // screen, the bottom of the menu, or we've displayed the
        // entire text buffer.
        SetColor(LOG);
        int row = (text_top_ + text_rows_ - 1) % text_rows_;
        size_t count = 0;
        for (int ty = gr_fb_height() - char_height_;
             ty >= y && count < text_rows_;
             ty -= char_height_, ++count) {
            gr_text(0, ty, text_[row], false);
            --row;
            if (row < 0) row = text_rows_ - 1;
        }
    }
}

void ScreenRecoveryUI::Print(const char* fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    PrintV(fmt, true, ap);
    va_end(ap);
}

void ScreenRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    PrintV(fmt, false, ap);
    va_end(ap);
}

void ScreenRecoveryUI::PutChar(char ch) {
    pthread_mutex_lock(&updateMutex);
    if (ch != '\n') text_[text_row_][text_col_++] = ch;
    if (ch == '\n' || text_col_ >= text_cols_) {
        text_col_ = 0;
        ++text_row_;

        if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
    }
    pthread_mutex_unlock(&updateMutex);
}

void ScreenRecoveryUI::ClearText() {
    pthread_mutex_lock(&updateMutex);
    text_col_ = 0;
    text_row_ = 0;
    text_top_ = 1;
    for (size_t i = 0; i < text_rows_; ++i) {
        memset(text_[i], 0, text_cols_ + 1);
    }
    pthread_mutex_unlock(&updateMutex);
}

// In recovery.cpp, ui->Print()  ---- ScreenRecoveryUI::Print
static bool wipe_data(int should_confirm, Device* device) {
    if (should_confirm && !yes_no(device, "Wipe all user data?", "  THIS CAN NOT BE UNDONE!")) {
        return false;
    }
    modified_flash = true;
    ui->Print("\n-- Wiping data...\n");
    bool success =
        device->PreWipeData() &&
        erase_volume("/data") &&
        (has_cache ? erase_volume("/cache") : true) &&
        device->PostWipeData();
    ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
    return success;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值