try_update_binary

本文深入分析了OTA升级过程中的关键步骤,重点介绍了recovery模式下的update-binary文件如何被解析及执行,包括其内部函数try_update_binary的具体实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

recovery代码分析之三:try_update_binary

2013年02月04日  ⁄ 综合 ⁄ 共 3675字 ⁄ 字号  小 中 大  ⁄ 评论关闭
id="cproIframe_u1507428" width="300" height="250" src="http://pos.baidu.com/acom?adn=0&at=103&aurl=&cad=1&ccd=32&cec=UTF-8&cfv=11&ch=0&col=zh-CN&conOP=0&cpa=1&dai=2&dis=0&ltr=http%3A%2F%2Fwww.baidu.com%2Fs%3Fie%3Dutf-8%26f%3D8%26rsv_bp%3D1%26tn%3Dbaidu%26wd%3Drecovery%25E4%25B8%25AD%25E8%2584%259A%25E6%259C%25AC%25E8%25A7%25A3%25E6%259E%2590%26rsv_enter%3D0%26rsv_sug3%3D5%26rsv_sug4%3D1269%26rsv_sug2%3D0%26inputT%3D4798&ltu=http%3A%2F%2Fwww.xuebuyuan.com%2F1554719.html&lunum=6&n=83099053_cpr&pcs=1366x599&pis=10000x10000&ps=326x909&psr=1366x768&pss=1366x346&qn=82e7d9eabe672930&rad=&rsi0=300&rsi1=250&rsi5=4&rss0=%23FFFFFF&rss1=%23FFFFFF&rss2=%230080c0&rss3=%23444444&rss4=%23008000&rss5=&rss6=%23e10900&rss7=&scale=&skin=&td_id=1507428&tn=text_default_300_250&tpr=1411638264064&ts=1&xuanting=0&dtm=BAIDU_DUP2_SETJSONADSLOT&dc=2&di=u1507428" align="center,center" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="margin: 0px; padding: 0px; border-width: 0px; background-color: transparent;">

        OTA升级包路径META-INF\com\google\android中,存在着两个关键的文件:update-script和update-binary。在这两个脚本文件中,update-script记载着系统升级所需要执行的命令(如图1所示),而update-binary则是对于每条命令的解析。进入recovery模式后,系统将会执行文件中记载的命令,完成升级。

图1 update-script内容截图

http://blog.youkuaiyun.com/liudekuan/article/details/8707044可知,在文件./bootable/recovery/install.c中定义了对应于每条命令的执行函数(如图2所示),即在recovery模式下

,系统会将这些命令转换为相应的函数去执行。而RegisterInstallFunctions函数在./bootable/recovery/update.c中被调用,在源码编译过程中,./bootable/recovery/updater目录下的代码被编译为可执行文件:out/target/product/sp8825ea/system/bin/updater,供recovery模式下系统使用。

图2 函数注册

static int
try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {
    const ZipEntry* binary_entry =
            mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
    if (binary_entry == NULL) {
        mzCloseZipArchive(zip);
        return INSTALL_CORRUPT;
    }

    char* binary = "/tmp/update_binary";
    unlink(binary);
    int fd = creat(binary, 0755);
    if (fd < 0) {
        mzCloseZipArchive(zip);
        LOGE("Can't make %s\n", binary);
        return INSTALL_ERROR;
    }
    bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
    close(fd);
    mzCloseZipArchive(zip);

    if (!ok) {
        LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
        return INSTALL_ERROR;
    }

    int pipefd[2];
    pipe(pipefd);

    // When executing the update binary contained in the package, the
    // arguments passed are:
    //
    //   - the version number for this interface
    //
    //   - an fd to which the program can write in order to update the
    //     progress bar.  The program can write single-line commands:
    //
    //        progress <frac> <secs>
    //            fill up the next <frac> part of of the progress bar
    //            over <secs> seconds.  If <secs> is zero, use
    //            set_progress commands to manually control the
    //            progress of this segment of the bar
    //
    //        set_progress <frac>
    //            <frac> should be between 0.0 and 1.0; sets the
    //            progress bar within the segment defined by the most
    //            recent progress command.
    //
    //        firmware <"hboot"|"radio"> <filename>
    //            arrange to install the contents of <filename> in the
    //            given partition on reboot.
    //
    //            (API v2: <filename> may start with "PACKAGE:" to
    //            indicate taking a file from the OTA package.)
    //
    //            (API v3: this command no longer exists.)
    //
    //        ui_print <string>
    //            display <string> on the screen.
    //
    //   - the name of the package zip file.
    //

    char** args = malloc(sizeof(char*) * 5);
    args[0] = binary;
    args[1] = EXPAND(RECOVERY_API_VERSION);   // defined in Android.mk
    args[2] = malloc(10);
    sprintf(args[2], "%d", pipefd[1]);
    args[3] = (char*)path;
    args[4] = NULL;

    pid_t pid = fork();
    if (pid == 0) {
        close(pipefd[0]);
        execv(binary, args);
        fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
        _exit(-1);
    }
    close(pipefd[1]);

    *wipe_cache = 0;

    char buffer[1024];
    FILE* from_child = fdopen(pipefd[0], "r");
    while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
        char* command = strtok(buffer, " \n");
        if (command == NULL) {
            continue;
        } else if (strcmp(command, "progress") == 0) {
            char* fraction_s = strtok(NULL, " \n");
            char* seconds_s = strtok(NULL, " \n");

            float fraction = strtof(fraction_s, NULL);
            int seconds = strtol(seconds_s, NULL, 10);

            ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION),
                             seconds);
        } else if (strcmp(command, "set_progress") == 0) {
            char* fraction_s = strtok(NULL, " \n");
            float fraction = strtof(fraction_s, NULL);
            ui_set_progress(fraction);
        } else if (strcmp(command, "ui_print") == 0) {
            char* str = strtok(NULL, "\n");
            if (str) {
                ui_print("%s", str);
            } else {
                ui_print("\n");
            }
        } else if (strcmp(command, "wipe_cache") == 0) {
            *wipe_cache = 1;
        } else {
            LOGE("unknown command [%s]\n", command);
        }
    }
    fclose(from_child);

    int status;
    waitpid(pid, &status, 0);
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
        return INSTALL_ERROR;
    }

    return INSTALL_SUCCESS;
}

代码段1 try_update_binary函数

        ./bootable/recovery/install.c中的try_update_binary函数,是真正实现读取升级包中的脚本文件并执行相应的函数的地方。在此函数中,通过调用fork函数创建出一个子进程(代码第72行),在子进程中开始读取并执行升级脚本文件(代码73-78)。在此需要注意的是函数fork的用法,fork被调用一次,将做两次返回,在父进程中返回的是子进程的进程ID,为正数;而在子进程中,则返回0。因此代码73-78事实是子进程中所进行的操作,即execv(binary,
args)。子进程创建成功后,开始执行升级代码,并通过管道与父进程交互(代码27-28,创建管道,并将pipefd[1]作为参数传递给子进程,子进程则将相关信息写入到此管道描述符中);而父进程则通过读取子进程传递过来的信息更新UI(代码84-114)。

from maix import touchscreen, display, image import time class GUI: def __init__(self) -> None: self.background = None self.items = list() self.callbacks = list() self.labels = list() self.touch_x = 0 self.touch_y = 0 # 加载字体 try: image.load_font("sourcehansans", "/maixapp/share/font/SourceHansansCN-Regular.otf") image.set_default_font("sourcehansans") print("Font loaded successfully") except Exception as e: print(f"Font loading error: {e}, using default font") image.set_default_font("default") # 初始化触摸屏和显示 try: self._ts = touchscreen.TouchScreen() self._disp = display.Display() print("Touchscreen and display initialized") except Exception as e: print(f"Display/touchscreen init error: {e}") raise self._last_pressed = 0 self._last_state = 0 # 0: release, 1: press def _is_in_item(self, item_id: int, x: int, y: int) -> bool: """检查点(x, y)是否在指定的item内""" if item_id >= len(self.items) or self.background is None: return False item = self.items[item_id] # 计算item在屏幕上的实际位置 screen_width = self._disp.width() screen_height = self._disp.height() bg_width = self.background.width() bg_height = self.background.height() # 计算映射比例 scale_x = screen_width / bg_width scale_y = screen_height / bg_height # 计算item在屏幕上的位置和大小 item_x = item * scale_x item_y = item * scale_y item_w = item * scale_x item_h = item * scale_y # 检查点是否在矩形内 return (item_x <= x <= item_x + item_w and item_y <= y <= item_y + item_h) def createButton(self, x: int, y: int, w: int, h: int) -> int: """创建按钮,返回按钮ID""" self.items.append((x, y, w, h)) self.callbacks.append(None) self.labels.append("") return len(self.items) - 1 def setItemLabel(self, item_id: int, label: str): """设置按钮标签""" if item_id < len(self.labels): self.labels[item_id] = label def setItemCallback(self, item_id: int, callback): """设置按钮回调函数""" if item_id < len(self.callbacks): self.callbacks[item_id] = callback def get_touch(self): """获取当前触摸点坐标(返回图像坐标系)""" # 获取屏幕尺寸 screen_width = self._disp.width() screen_height = self._disp.height() # 计算缩放比例 scale_x = self.background.width() / screen_width scale_y = self.background.height() / screen_height # 返回映射到图像坐标系的触摸点 return int(self.touch_x * scale_x), int(self.touch_y * scale_y) def run(self, img): """更新显示并处理触摸事件""" # 1. 更新背景图像 self.background = img.copy() # 2. 绘制所有按钮 for i, (x, y, w, h) in enumerate(self.items): # 修复:使用draw_rect而不是draw_rectangle self.background.draw_rect(x, y, w, h, image.COLOR_WHITE, thickness=2) # 绘制按钮标签 self.background.draw_string(x + 5, y + h//2 - 8, self.labels[i], image.COLOR_WHITE) # 3. 显示图像 self._disp.show(self.background) # 4. 处理触摸事件 points = self._ts.read() if points: point = points state = point # 触摸状态 x, y = point, point # 更新触摸点坐标 self.touch_x = x self.touch_y = y # 只处理按下事件 if state == 1 and self._last_state == 0: self._last_state = 1 # 检查触摸点是否在按钮内 for i in range(len(self.items)): if self._is_in_item(i, x, y): if self.callbacks[i]: self.callbacks[i](i, state) break elif state == 0: self._last_state = 0 from gui import GUI from maix import camera, image, time, app import math # 屏幕宽度和高度 _image_width = 320 _image_height = 240 _btn_width = _image_width // 6 _btn_height = _image_height // 6 _btn_id_pixel = -1 _btn_id_binary = -1 _to_show_binary = False _to_get_pixel = False def rgb_to_lab(rgb): # RGB到XYZ的转换矩阵 M = [ [0.412453, 0.357580, 0.180423], [0.212671, 0.715160, 0.072169], [0.019334, 0.119193, 0.950227] ] # 归一化RGB值 r, g, b = rgb / 255.0, rgb / 255.0, rgb / 255.0 # 线性化RGB值 r = r / 12.92 if r <= 0.04045 else ((r + 0.055) / 1.055) ** 2.4 g = g / 12.92 if g <= 0.04045 else ((g + 0.055) / 1.055) ** 2.4 b = b / 12.92 if b <= 0.04045 else ((b + 0.055) / 1.055) ** 2.4 # 计算XYZ值 X = M * r + M * g + M * b Y = M * r + M * g + M * b Z = M * r + M * g + M * b # XYZ归一化 X /= 0.95047 Y /= 1.00000 Z /= 1.08883 def f(t): return t ** (1/3) if t > 0.008856 else 7.787 * t + 16/116 L = 116 * f(Y) - 16 a = 500 * (f(X) - f(Y)) b_val = 200 * (f(Y) - f(Z)) return [L, a, b_val] def set_configured_threshold(threshold): """阈值参数信息存入配置文件""" if len(threshold) < 6: return # 阈值范围验证 threshold = max(threshold, 0) # Lmin >= 0 threshold = min(threshold, 100) # Lmax <= 100 threshold = max(threshold, -128) # amin >= -128 threshold = min(threshold, 127) # amax <= 127 threshold = max(threshold, -128) # bmin >= -128 threshold = min(threshold, 127) # bmax <= 127 app.set_app_config_kv('demo_find_line', 'lmin', str(threshold), False) app.set_app_config_kv('demo_find_line', 'lmax', str(threshold), False) app.set_app_config_kv('demo_find_line', 'amin', str(threshold), False) app.set_app_config_kv('demo_find_line', 'amax', str(threshold), False) app.set_app_config_kv('demo_find_line', 'bmin', str(threshold), False) app.set_app_config_kv('demo_find_line', 'bmax', str(threshold), True) def get_configured_threshold(): """获取所有储配置文件中的阈值参数""" threshold = [0, 100, -128, 127, -128, 127] # 默认阈值 value_str = app.get_app_config_kv('demo_find_line', 'lmin', '', False) if value_str: threshold = int(value_str) value_str = app.get_app_config_kv('demo_find_line', 'lmax', '', False) if value_str: threshold = int(value_str) value_str = app.get_app_config_kv('demo_find_line', 'amin', '', False) if value_str: threshold = int(value_str) value_str = app.get_app_config_kv('demo_find_line', 'amax', '', False) if value_str: threshold = int(value_str) value_str = app.get_app_config_kv('demo_find_line', 'bmin', '', False) if value_str: threshold = int(value_str) value_str = app.get_app_config_kv('demo_find_line', 'bmax', '', False) if value_str: threshold = int(value_str) return threshold def btn_pressed(btn_id, state): """界面上按键的状态改变回调函数""" global _to_show_binary, _to_get_pixel, _btn_id_binary, _btn_id_pixel if state == 0: # 只响应触摸指起的动作 return if btn_id == _btn_id_binary: _to_show_binary = not _to_show_binary if _to_get_pixel: _to_get_pixel = False print(f"Binary mode: {'ON' if _to_show_binary else 'OFF'}") elif btn_id == _btn_id_pixel: _to_get_pixel = not _to_get_pixel print(f"Get pixel mode: {'ON' if _to_get_pixel else 'OFF'}") def main(): global _to_show_binary, _to_get_pixel, _btn_id_binary, _btn_id_pixel print("App config path:", app.get_app_config_path()) try: # 初始化摄像头 cam = camera.Camera(_image_width, _image_height) print("Camera initialized") # 初始化GUI gui = GUI() print("GUI initialized") # 创建按钮 _btn_id_pixel = gui.createButton(10, _image_height - _btn_height - 10, _btn_width, _btn_height) gui.setItemLabel(_btn_id_pixel, '取阈值') gui.setItemCallback(_btn_id_pixel, btn_pressed) _btn_id_binary = gui.createButton(_image_width - _btn_width - 10, _image_height - _btn_height - 10, _btn_width, _btn_height) gui.setItemLabel(_btn_id_binary, '二值化') gui.setItemCallback(_btn_id_binary, btn_pressed) last_x = -1 last_y = -1 threshold = get_configured_threshold() print("Initial threshold:", threshold) # FPS计算 start_time = time.ticks_ms() frame_count = 0 fps_value = 0 while not app.need_exit(): frame_count += 1 current_time = time.ticks_ms() elapsed = current_time - start_time # 每秒更新FPS if elapsed >= 1000: fps_value = frame_count * 1000 // elapsed frame_count = 0 start_time = current_time print(f"FPS: {fps_value}") try: # 1. 读取图像 img = cam.read() except Exception as e: print(f"Camera read error: {e}") time.sleep(0.1) # 防止错误循环占用100% CPU continue # 2. 处理图像显示模式 if _to_show_binary: # 二值化显示 try: # 使用正确的参数格式 [lmin, lmax, amin, amax, bmin, bmax] img_bin = img.binary([threshold], invert=False) img = img_bin except Exception as e: print(f"Binary conversion error: {e}") else: # 原始图像显示 try: # 2. 获取触摸点像素值 if _to_get_pixel: x, y = gui.get_touch() if 0 <= x < _image_width and 0 <= y < _image_height: if last_x != x or last_y != y: last_x = x last_y = y rgb = img.get_pixel(x, y, True) lab = rgb_to_lab(rgb) if len(lab) >= 3: # 计算新阈值范围 threshold = max(math.floor(lab) - 30, 0) threshold = min(math.ceil(lab) + 30, 100) threshold = max(math.floor(lab) - 10, -128) threshold = min(math.ceil(lab) + 10, 127) threshold = max(math.floor(lab) - 10, -128) threshold = min(math.ceil(lab) + 10, 127) print("New threshold:", threshold) set_configured_threshold(threshold) img.draw_cross(x, y, image.COLOR_YELLOW, 8, 2) except Exception as e: print(f"Touch processing error: {e}") try: # 3. 线性回归找线 area_threshold = 100 # 使用正确的参数格式 lines = img.get_regression( [threshold], # 阈值列表 area_threshold=area_threshold, pixels_threshold=area_threshold ) if lines: # 修复:get_regression返回的是点坐标元组列表 # 每个元素是((x1, y1), (x2, y2)) line = lines x1, y1 = line x2, y2 = line # 绘制检测到的线段 img.draw_line(x1, y1, x2, y2, image.COLOR_GREEN, 2) # 计算线段参数 magnitude = math.sqrt((x2 - x1)**2 + (y2 - y1)**2) theta = math.degrees(math.atan2(y2 - y1, x2 - x1)) rho = (x1 + x2) / 2 # 简化表示中点x坐标 info = f"mag:{magnitude:.1f} theta:{theta:.1f} rho:{rho:.1f}" img.draw_string(0, 0, info, image.COLOR_RED) except Exception as e: print(f"Line detection error: {e}") # 显示FPS img.draw_string(_image_width - 80, 0, f"FPS:{fps_value:.1f}", image.COLOR_RED) # 4. 更新显示 try: gui.run(img) except Exception as e: print(f"GUI update error: {e}") except Exception as e: print(f"Application error: {e}") finally: # 显式释放资源 try: if 'cam' in locals(): cam.close() print("Camera released") except Exception as e: print(f"Camera release error: {e}") print("Application exited") if __name__ == '__main__': main() 检查代码
最新发布
07-18
ERROR: Exception: Traceback (most recent call last): File "D:\python\Quant\modec\lib\site-packages\pip\_internal\cli\base_command.py", line 180, in _main status = self.run(options, args) File "D:\python\Quant\modec\lib\site-packages\pip\_internal\cli\req_command.py", line 204, in wrapper return func(self, options, args) File "D:\python\Quant\modec\lib\site-packages\pip\_internal\commands\install.py", line 319, in run reqs, check_supported_wheels=not options.target_dir File "D:\python\Quant\modec\lib\site-packages\pip\_internal\resolution\resolvelib\resolver.py", line 128, in resolve requirements, max_rounds=try_to_avoid_resolution_too_deep File "D:\python\Quant\modec\lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 473, in resolve state = resolution.resolve(requirements, max_rounds=max_rounds) File "D:\python\Quant\modec\lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 367, in resolve failure_causes = self._attempt_to_pin_criterion(name) File "D:\python\Quant\modec\lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 213, in _attempt_to_pin_criterion criteria = self._get_criteria_to_update(candidate) File "D:\python\Quant\modec\lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 203, in _get_criteria_to_update name, crit = self._merge_into_criterion(r, parent=candidate) File "D:\python\Quant\modec\lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 172, in _merge_into_criterion if not criterion.candidates: File "D:\python\Quant\modec\lib\site-packages\pip\_vendor\resolvelib\structs.py", line 139, in __bool__ return bool(self._sequence) File "D:\python\Quant\modec\lib\site-packages\pip\_internal\resolution\resolvelib\found_candidates.py", line 143, in __bool__ return any(self) File "D:\python\Quant\modec\lib\site-packages\pip\_internal\resolution\resolvelib\found_candidates.py", line 129, in <genexpr> return (c for c in iterator if id(c) not in se
03-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值