Android 5..x OTA Update官方文档(六、定制Recovery UI)

recovery界面

为了支持不同的硬件设备(物理按键、显示、LEDs等),我们可以定制recovery界面进行可视化显示,并进行相关的操作。那么我们可以通过继承bootable/recovery/defalust_device.cpp,并重写相关的函数来为我们的设备进行可视化定制。因此本篇博文旨在为大家介绍如何构建一个能够实现recovery界面定制的静态库。首先来了解西面这段头文件:

device/yoyodyne/tardis/recovery/recovery_ui.cpp

#include <linux/input.h>

#include "common.h"

#include "device.h"

#include "screen_ui.h"

1.标题文本和功能选项

Device类为我们提供了可视化标题和选项。标题为我们描述了如何操作菜单,而items则告诉我们recovery的功能选项。

注这里需要注意的是,请考虑屏幕宽度,尽量保持选项字符长度简洁。

2.按键功能

接着我们了解RecoveryUI按键功能的定制。我们可以继承ScreenRecoveryUIimplementation 并实现相关的方法,这里我们来实现用来处理案件的CheckKey()函数,参考如下:

class TardisUI : public ScreenRecoveryUI {

public:

virtual KeyAction CheckKey(int key) {

if (key == KEY_HOME) {

return TOGGLE;

}

return ENQUEUE;

}

};

按键常量定义在linux/input.h(CheckKey())中,无论recovery模式下在做何种操作(安装更新、擦除用户数据)都可以调用CheckKey()函数。

这里有四种常量值我们可以选择。

TOGGLE:触发器,可以用来控制显示、log

REBOOT:立即重启设备

IGNORE:忽略按键

ENQUEUE:同步队列按键

CheckKey()函数会对所有按键做出响应,即便是相同的按键事件。那么如果按键队列中有以下按键事件(A—down、B-down,B-up,A-up,那么此时只有CheckKey(B)会被调用。)。而且CheckKey()可以调用IsKeyPressed()函数来识别当前是否有保持按下的动作。(那么在上面的案件队列中如果此时CheckKey(B)被调用,IsKeyPressed(A)则会返回true)。

CheckKey()可以保存状态,以便于检测键序列。下面展示的这个例子展现给大家的是一个略微复杂的设置:同时按下Power+Volume-Up健切换显示;按下电源键连续5秒钟重启手机。

class TardisUI : public ScreenRecoveryUI {

private:

int consecutive_power_keys;

public:

TardisUI() : consecutive_power_keys(0) {}

virtual KeyAction CheckKey(int key) {

if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) {

return TOGGLE;

}

if (key == KEY_POWER) {

++consecutive_power_keys;

if (consecutive_power_keys >= 5) {

return REBOOT;

}

} else {

consecutive_power_keys = 0;

}

return ENQUEUE;

}

};

3.recovery界面:

我们可以通过设置下面一系列的属性来定制化(图标、动画、进度条)的Recovery显示系统,以便于帮助我们将更加完善Recovery显示系统展现给我们的用户。那么我们如何控制动画的帧的数量、叠加偏移量。那么我们可设置西面的这些变量。


Variable Name


Purpose


Release


animation_fps


speed (in frames per second) of animations动画播放速度


Android 5.x and earlier


installing_frames


number of frames in the installation animation


Android 4.x and earlier


install_overlay_offset_x, install_overlay_offset_y


offset of the per-frame overlay (relative to the base image) for the installation animation


Android 4.x and earlier

设置上面的这些变量,我们需要在子类中重写ScreenRecoveryUI::Init()函数。设置值之后还需要调用父类的Init()函数来完成初始化。如下所示:

class TardisUI : public ScreenRecoveryUI {

...

void Init() {

// change the speed at which animations run

animation_fps = 30;

ScreenRecoveryUI::Init();

}

4.Device Class

在我们实现了Recovery UI之后,我们可以定义我们自己的Device Class(也就是Device class的子类)。我们可以使用单例模式来创建这样一个子类,然后作为GetUI()的返回值返回,如下:

class TardisDevice : public Device {

private:

TardisUI* ui;

public:

TardisDevice() :

ui(new TardisUI) {

}

RecoveryUI* GetUI() { return ui; }

5.启动Reccovery

在recovery用户界面初始化完成,相关参数被解析之后,我们可以调用StartRecovery()函数来启动revoery。如果我们不需要在启动recovery时执行默写操作那么就不需要在我们创建的子类中去重写StartRecovery()函数,因为默认情况下这个函数只是一个拓展性的预留函数,并没有实际的操作,如下所示:

void StartRecovery() {

// ... do something tardis-specific here, if needed ....

}

6.配置和管理recovery选项菜单

recovery系统通过调用下面这两个函数可以可到一个标题列表和一个选项列表。在下面的实现当中,会返回两个之前已经定义好的静态数组,如下:

const char* const* GetMenuHeaders() { return HEADERS; }

const char* const* GetMenuItems() { return ITEMS; }

7.处理按键

接着我们来熟悉一个处理按键的函数,当菜单可见且我们进行按键操作时,我们可以针对不同的按键做出相应的响应。

int HandleMenuKey(int key, int visible) {

if (visible) {

switch (key) {

case KEY_VOLUMEDOWN: return kHighlightDown;

case KEY_VOLUMEUP:   return kHighlightUp;

case KEY_POWER:      return kInvokeItem;

}

}

return kNoAction;

}

上面函数的返回值都是整形,也就是下面这些预定义的常量,如下:

·  kHighlightUp. 向上高亮显示菜单

·  kHighlightDown. 向下高亮显示菜单

·  kInvokeItem. 调用当前高亮显示的菜单项

·  kNoAction. 不响应此按键

visible参数表示即便当前选项菜单不可见也可以调用HandleMenuKey()函数。这和CheckKey()函数不尽相同,CheckKey()函数只有在recovery处于空闲状态或者等待输入的时候才能够调用,而当recovery进行数据擦除、安装更新的时候是无法调用CheckKey()函数的。

8.定制按键响应

我们在处理按键的时候可以使用IsKeyPressed()函数来定制一些按键功能,如下所示,:

int HandleMenuKey(int key, int visible) {

if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) {

return 2;  // position of the "wipe data" item in the menu

}

...

}

9.执行菜单选项

在前面我们了解到,我们可以通过按键来高亮显示某个菜单选项,那么我们该如何能够执行菜单选项所对应的功能呢,那么recovery为我们提供了InvokeMenuItem()函数,来帮助我们完成菜单选项所对应的功能,如下:

BuiltinAction InvokeMenuItem(int menu_position) {

switch (menu_position) {

case 0: return REBOOT;

case 1: return APPLY_ADB_SIDELOAD;

case 2: return WIPE_DATA;

case 3: return WIPE_CACHE;

default: return NO_ACTION;

}

}

而且我们也可以通过GetMenuItem()函数来查看当前高亮显示的菜单选项所对应的动作,如REBOOT(重启)等,如下:

·  NO_ACTION. 不作出任何响应.

·  REBOOT. 退出recovery,重启.

·  APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. 安装更新I

·  WIPE_CACHE. 格式化cache分区.

·  WIPE_DATA. 擦除用户数据并格式化cache分区,也就是恢复出厂设置的动作。由于这个动作是及其危险的,因此出于人性化的设计在执行擦除操作前会要求用户进一步确认.

WipeData()函数是一个可选的操作,只要数据擦除操作初始化完成,我们可以在任何时候执行该操作。如果我们在data和cache分区之外其他地方储存了用户数据,而我们又想在执行恢复出厂设置时进行擦除我们可以在下面的方法中执行擦除操作,如下:

int WipeData() {

// ... do something tardis-specific here, if needed ....

return 0;

}

上面的函数中,如果返回值是0表示擦除成功,其他值则表示擦除失败。

原文如下:

Recovery UI



To support devices with different available hardware (physical buttons, LEDs, screens, etc.), you can customize the recovery interface to display status and access the manually-operated hidden features for each device.

Your goal is to build a small static library with a couple of C++ objects to provide the device-specific functionality. The file bootable/recovery/default_device.cpp is
used by default, and makes a good starting point to copy when writing a version of this file for your device.

device/yoyodyne/tardis/recovery/recovery_ui.cpp

#include <linux/input.h>

#include "common.h"
#include "device.h"
#include "screen_ui.h"

Header and item functions

The Device class requires functions for returning headers and items that appear in the hidden recovery menu. Headers describe how to operate the menu (i.e. controls to change/select the highlighted item).

static const char* HEADERS[] = { "Volume up/down to move highlight;",
                                 "power button to select.",
                                 "",
                                 NULL };

static const char* ITEMS[] =  {"reboot system now",
                               "apply update from ADB",
                               "wipe data/factory reset",
                               "wipe cache partition",
                               NULL };

Note: Long lines are truncated (not wrapped), so keep the width of your device screen in mind.

Customizing CheckKey

Next, define your device‘s RecoveryUI implementation. This example assumes the tardis device has a screen, so you can inherit from the built-in ScreenRecoveryUIimplementation (see instructions for devices
without a screen
.) The only function to customize from ScreenRecoveryUI is CheckKey(), which does the initial asynchronous key handling:

class TardisUI : public ScreenRecoveryUI {
  public:
    virtual KeyAction CheckKey(int key) {
        if (key == KEY_HOME) {
            return TOGGLE;
        }
        return ENQUEUE;
    }
};

KEY constants

The KEY_* constants are defined in linux/input.h. CheckKey() is called no matter what is going on in the rest
of recovery: when the menu is toggled off, when it is on, during package installation, during userdata wiping, etc. It can return one of four constants:

  • TOGGLE. Toggle the display of the menu and/or text log on or off
  • REBOOT. Immediately reboot the device
  • IGNORE. Ignore this keypress
  • ENQUEUE. Enqueue this keypress to be consumed synchronously (i.e., by the recovery menu system if the display is enabled)

CheckKey() is called each time a key-down event is followed by a key-up event for the same key. (The sequence of events A-down B-down B-up A-up results only in CheckKey(B) being
called.) CheckKey() can call IsKeyPressed(), to find out if other keys are being held down. (In the above sequence
of key events, if CheckKey(B) calledIsKeyPressed(A) it would have returned true.)

CheckKey() can maintain state in its class; this can be useful to detect sequences of keys. This example shows a slightly more complex setup: the display is toggled by holding down power
and pressing volume-up, and the device can be rebooted immediately by pressing the power button five times in a row (with no other intervening keys):

class TardisUI : public ScreenRecoveryUI {
  private:
    int consecutive_power_keys;

  public:
    TardisUI() : consecutive_power_keys(0) {}

    virtual KeyAction CheckKey(int key) {
        if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) {
            return TOGGLE;
        }
        if (key == KEY_POWER) {
            ++consecutive_power_keys;
            if (consecutive_power_keys >= 5) {
                return REBOOT;
            }
        } else {
            consecutive_power_keys = 0;
        }
        return ENQUEUE;
    }
};

ScreenRecoveryUI

When using your own images (error icon, installation animation, progress bars) with ScreenRecoveryUI, you might need to set some member variables to specify attributes such as the number of frames, speed, and overlay offsets. You can set the following variables:

Variable Name Purpose Release
animation_fps speed (in frames per second) of animations Android 5.x and earlier
installing_frames number of frames in the installation animation Android 4.x and earlier
install_overlay_offset_x, install_overlay_offset_y offset of the per-frame overlay (relative to the base image) for the installation animation Android 4.x and earlier

To set variables, override the ScreenRecoveryUI::Init() function in your subclass. Set the values, then call theparent
Init() 
function to complete initialization:

class TardisUI : public ScreenRecoveryUI {
  ...
  void Init() {
    // change the speed at which animations run
    animation_fps = 30;

    ScreenRecoveryUI::Init();
  }

The default values correspond to the default recovery images; when using these images you don‘t need to provide an Init() function. For details on images, see Recovery
UI Images
.

Device Class

After you have a RecoveryUI implementation, define your device class (subclassed from the built-in Device class). It should create a single instance of your UI class and return that from the GetUI() function:

class TardisDevice : public Device {
  private:
    TardisUI* ui;

  public:
    TardisDevice() :
        ui(new TardisUI) {
    }

    RecoveryUI* GetUI() { return ui; }

StartRecovery

The StartRecovery() method is called at the start of recovery, after the UI has been initialized and after the arguments have been parsed, but before any action has been taken. The default
implementation does nothing, so you do not need to provide this in your subclass if you have nothing to do:

   void StartRecovery() {
       // ... do something tardis-specific here, if needed ....
    }

Supplying and managing recovery menu

The system calls two methods to get the list of header lines and the list of items. In this implementation, it returns the static arrays defined at the top of the file:

const char* const* GetMenuHeaders() { return HEADERS; }
const char* const* GetMenuItems() { return ITEMS; }

HandleMenuKey

Next, provide a HandleMenuKey() function, which takes a keypress and the current menu visibility, and decides what action to take:

   int HandleMenuKey(int key, int visible) {
        if (visible) {
            switch (key) {
              case KEY_VOLUMEDOWN: return kHighlightDown;
              case KEY_VOLUMEUP:   return kHighlightUp;
              case KEY_POWER:      return kInvokeItem;
            }
        }
        return kNoAction;
    }

The method takes a key code (which has previously been processed and enqueued by the CheckKey() method of the UI object), and the current state of the menu/text log visibility. The return
value is an integer. If the value is 0 or higher, that is taken as the position of a menu item, which is invoked immediately (see the InvokeMenuItem()method below). Otherwise it can
be one of the following predefined constants:

  • kHighlightUp. Move the menu highlight to the previous item
  • kHighlightDown. Move the menu highlight to the next item
  • kInvokeItem. Invoke the currently highlighted item
  • kNoAction. Do nothing with this keypress

As implied by the the visible argument, HandleMenuKey() is called even if the menu is not visible. Unlike CheckKey(),
it is not called while recovery is doing something such as wiping data or installing a package—it‘s called only when recovery is idle and waiting for input.

Trackball Mechanisms

If your device has a trackball-like input mechanism (generates input events with type EV_REL and code REL_Y), recovery synthesizes KEY_UP and KEY_DOWN keypresses whenever the trackball-like input device reports motion in the Y axis. All you need to do is map
KEY_UP and KEY_DOWN events onto menu actions. This mapping does nothappen for CheckKey(), so you can‘t use trackball motions as triggers for rebooting or toggling the display.

Modifier Keys

To check for keys being held down as modifiers, call the IsKeyPressed() method of your own UI object. For example, on some devices pressing Alt-W in recovery would start a data wipe whether
the menu was visible or not. YOu could implement like this:

   int HandleMenuKey(int key, int visible) {
        if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) {
            return 2;  // position of the "wipe data" item in the menu
        }
        ...
    }

Note: If visible is false, it doesn‘t make sense to return the special values that manipulate the menu (move highlight, invoke highlighted item) since the user can‘t see the highlight. However, you can return the values if
desired.

InvokeMenuItem

Next, provide an InvokeMenuItem() method that maps integer positions in the array of items returned byGetMenuItems() to
actions. For the array of items in the tardis example, use:

   BuiltinAction InvokeMenuItem(int menu_position) {
        switch (menu_position) {
          case 0: return REBOOT;
          case 1: return APPLY_ADB_SIDELOAD;
          case 2: return WIPE_DATA;
          case 3: return WIPE_CACHE;
          default: return NO_ACTION;
        }
    }

This method can return any member of the BuiltinAction enum to tell the system to take that action (or the NO_ACTION member if you want the system to do nothing). This is the place to provide additional recovery functionality beyond what‘s in the system: Add
an item for it in your menu, execute it here when that menu item is invoked, and return NO_ACTION so the system does nothing else.

BuiltinAction contains the following values:

  • NO_ACTION. Do nothing.
  • REBOOT. Exit recovery and reboot the device normally.
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Install an update package from various places. For details, see Sideloading.
  • WIPE_CACHE. Reformat the cache partition only. No confirmation required as this is relatively harmless.
  • WIPE_DATA. Reformat the userdata and cache partitions, also known as a factory data reset. The user is asked to confirm this action before proceeding.

The last method, WipeData(), is optional and is called whenever a data wipe operation is initiated (either from recovery via the menu or when the user has chosen to do a factory data
reset from the main system). This method is called before the user data and cache partitions are wiped. If your device stores user data anywhere other than those two partitions, you should erase it here. You should return 0 to indicate success and another
value for failure, though currently the return value is ignored. The user data and cache partitions are wiped whether you return success or failure.

   int WipeData() {
       // ... do something tardis-specific here, if needed ....
       return 0;
    }

时间: 2024-08-09 22:03:03

Android 5..x OTA Update官方文档(六、定制Recovery UI)的相关文章

Android 5.x OTA Update官方文档(九、Sideloading-侧面安装机制)

写在前面: 本篇博文漏译了很多,英文功底比较好的同学可以选择阅读原版文档,如果方便请分享翻译后文档给大家,谢谢. recovery有一个侧面安装(sideloading)机制来方便我们手动的安装更新而不在使用OTA的方式.这种机制对于调试和维护是很有帮助的,特别是当我们无法正常启动我们的系统的时候. 目前来说我们,有了这种机制,我们可以在设备的SD卡中载入更新包.在没有启动设备的情况下,我们可以通过电脑将更新包拷贝到SD卡上,然后再将SD卡插入到设备中进行侧面安装.而且如果Android设备当前

Android 5.x OTA Update官方文档(十、版本签名)

在Android中,一般来说有两个地方使用加密签名. 1.每个.apk文件必须进行签名.Android的程序包管理器通过两种方式使用签名: 当一个应用程序被替换时,只有相同签名的应用才能操作旧版本的数据. 两个应用如果签名一致,那么这两个应用可以共享User ID和用户数据. 2.OTA更新包必须进行签名否则更新程序无法进行安装.(注!我们制作更新包的时候如果不指定key,系统会指定默认的key进行签名,如testkey.) 证书和秘钥 每个秘钥需要两个文件:扩展名为.x509.pem的证书(公

Android 5.x OTA Update官方文档(八、图说Recovery UI)

写在前面: 若能直译,不会意译,意译不成,为了选择不误导他人也会漏译无伤大雅的部分,英语是硬伤,如果有误译,请路过的朋友帮忙指正,以免误导更多的朋友. RecoveryUI除了之前提到的标题.选项菜单.提示性文字还有另外的一个重要的成员图片.在正常的更新过程中,用户看到的仅仅是图片(这里指的标准的OTA升级流程,如开机进入recovery模式.安装更新时进度条发生变化,最后再开机进入正常模式),而且在这个过程,用户是没有机会与RecoveryUI进行交互的.但是一旦在更新过程中出现了异常,用户所

Android 5..x OTA Update官方文档(七、构建设备)

我们通过调用recovery_ui.cpp中make_device()函数来创建一个Device的实例,如下: class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); } 编译recovery_ui.cpp 再通过前一篇对recovery_ui.cpp的介绍我们来介绍一下如何为recovery_ui.cpp

OTA Update官方文档(二,OTA 打包工具)

写在前面: OTA打包工具ota_from_target_files(build/tools/releasetools/目录下)可以为我们创建两种类型的更新包:整包和增量包.打包工具一般来说会对我们称之为目标文件(target-files.zip)进行打包,该目标文件是有Android编译系统产生,通常可在终端下使用make otapackage生成. 一.整包升级 一个整包包含了Android设备的整个终态(system/boot/recovery分区),只要设备可以接收更新包并且进入reco

OTA Update官方文档(五、Recovery系统分区映射)

Recovery系统包含了很多钩子程序,因此OAT更新除了可以更新Android系统也可以更新设备的其他部分.(如基带.无线电处理器). 分区映射 从Android2.3起,平台开始支持eMMC存储设备和ext4文件系统.它同样支持MTD设备和yaffs2文件系统. 分区映射文件TARGET_RECOVERY_FSTAB一般会被recovery二进制程序和打包工具所使用,我们可以在BoardConfig.mk文件中配置映射文件的文件名为TARGET_RECOVERY_FSTAB. 下面是一个简单

OTA Update官方文档(四、OTA更新包解读)

写在前面: 系统创建通过"bootable/recovery/updater"创建一个二进制更新程序并在OTA包中使用进行更新的安装. 更新包实际上也就是一个.zip格式的压缩文件(如我们之前提到过的ota_update.zip,incremental_ota_update.zip).在这个压缩包中包含了一个可执行的二进制程序-EMTA-INF/com/google/android/update-binary.这个二进制程序我们可以理解成一个升级程序,这个升级程序包含了一系列的内建函数

OTA Updates官方文档(一,OTA 升级)

写在前面: Android设备可以接受和安装基于系统和应用的更新.在设备中有一个特殊recovery分区,在这个分区中有一套特殊的恢复系统可以将下载的更新包进行解压并应用到系统中去. 本节主要描述了更新包的组织架构,并且概述了如何使用更新包打包工具制作系统升级的更新包.OTA 的存在是用来对潜在系统的升级,或者在系统分区安装只读(不可卸载)的应用程序,因此在升级过程中不会影响到用于已经安装到系统中的 应用程序. 本节的内容主要基于Android5.x版本. 一.Android设备布局的设计 通常

Android Studio官方文档之构建和运行你的APP

Android Studio官方文档之构建和运行你的APP 本文由MTJH翻译,jkYishon审校. 前言 默认情况下,Android Studio设置新的项目并且部署到模拟器或者真机设备上,只需要点击几下.使用即时运行,你并不需要构建一个新的APK即可将改变后的方法和现有的应用资源应用到一个正在运行的应用程序中,所以代码的改变是即时可见的. 点击Run来构建并运行你的APP.Android Studio通过Gradle构建你的App,选择一个部署的设备(模拟器或连接的设备),然后把你的APP