如题,各位,我想问下,怎么自制android的卡刷机ROM包update.zip。我最近的工作是在MT6516平台下制作一个官方固件升级包以支持公司一款即将出货的使用MT6516开发的android2.2智能手机,折腾了好几天,也google了一些资料,现在遇到的问题是update.zip的签名不能通过(failed to verify whole-file signature)。下面给出我的update.zip包的制作过程。首先,我是参考taohl04的这篇博客文章来做的:http://blog.csdn.net/taohl04/archive/2011/05/02/6384686.aspx,按照上面的步骤,我在update目录下得到了如下文件:
.--boot.img|-- META-INF| `-- com| `-- google| `-- android| |-- update-binary | `-- updater-script (说明1)`—system说明1:这个文件我是直接拷贝自android源码树根目录下的这个目录:bootable\recovery\etc\META-INF\com\google\android然后开始签名:$java –jar signapk.jar –w testkey.x509.pem testkey.pk8 update.zip update_signed.zip第三步,将update_signed.zip改名为update.zip($mv update_signed.zip update.zip),因为recovery源代码里对ROM做了这样的指定:static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip";
#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" (说明2)
#define PUBLIC_KEYS_FILE "/res/keys" (说明3)说明3:见4.1所以将改名后的update.zip存放到sdcard根目录下。第四步,重启机器进入recovery模式,开始刷机,报错信息大致是说签名未能验证成功。我在源码中如下几个地方添加了打印信息:
4.1.install_package函数中
int numKeys;
RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
ui_print("numKeys = %6d loadedKeys->len = %6d\n", numKeys,loadedKeys->len);打印结果为:numkeys = 1 loadedKeys->len = 64
其中PUBLIC_KEYS_FILE(见说明3)这个是什么东西,是要在刷机包中添加/res/key这个文件(或目录)吗?google了一下,发现老外也在问这个问题:http://forum.xda-developers.com/showthread.php?t=5812164.2.install_packge函数中
err = verify_file(path, loadedKeys, numKeys); //path = "sdcard/update.zip"
free(loadedKeys);
ui_print("verify_file returned %d\n", err); //打印为err = 1.即VERIFY_FAILURE ,参考4.3.
if (err != VERIFY_SUCCESS) {
ui_print("signature verification failed\n"); //报错,刷机失败,因为signature验证失败。
reset__block();
return INSTALL_CORRUPT;
}4.3.好吧,能不能绕过签名呢?我试了一下,直接将err = VERIFY_SUCCESS,程序继续向下运行,又报错,大致是因为等待子进程结束时遇到堵塞。出错信息见4.3.3
/* Try to open the package.
*/
ZipArchive zip;
err = mzOpenZipArchive(path, &zip);
if (err != 0) {
ui_print("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
reset__block();
return INSTALL_CORRUPT;
} /* Verify and install the contents of the package.
*/
int status = handle_update_package(path, &zip); //函数体见4.3.1.
mzCloseZipArchive(&zip);4.3.1.函数handle_update_package
static int
handle_update_package(const char *path, ZipArchive *zip)
{
// Update should take the rest of the progress bar.
ui_print("Installing update...\n"); int result = try_update_binary(path, zip); //函数体见4.3.2.
register_package_root(NULL, NULL); // Unregister package root
return result;
}
4.3.2.函数try_update_binary (代码有点长,拷贝到sourceinsight里面看比较明了)
// If the package contains an update binary, extract it and run it.
static int
try_update_binary(const char *path, ZipArchive *zip) {
const ZipEntry* binary_entry =
mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); //参考说明2,如去掉update-binary呢?参考4.4.
if (binary_entry == NULL) {
ui_print("because binary_entry == NULL,return INSTALL_CORRUPT\n");
return INSTALL_CORRUPT;
}
char* binary = "/tmp/update_binary";
unlink(binary);
int fd = creat(binary, 0755);
if (fd < 0) {
ui_print("Can't make %s\n", binary);
return 1;
}
bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
close(fd); if (!ok) {
ui_print("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
return 1;
} int pipefd[2];
pipe(pipefd); 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]); //关闭管道0
execv(binary, args);
fprintf(stderr, "E:Can't run %s (%s)\n", binary, strerror(errno));
_exit(-1);
}
close(pipefd[1]); //关闭管道1 char buffer[1024];
FILE* from_child = fdopen(pipefd[0], "r"); //打开管道0
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(str);
} else {
ui_print("\n");
}
} else {
ui_print("unknown command [%s]\n", command);
}
}
fclose(from_child); int status;
waitpid(pid, &status, 0);
ui_print("pid = %6d status = %6d\n", pid,status); //出错在这里。参考4.3.3.
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { //两个宏函数的定义,见4.3.4
ui_print("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
return INSTALL_ERROR;
} return INSTALL_SUCCESS;
}
4.3.3.出错消息为
pid = 59 status = 768
Error in /sdcard/update.zip
(status3)
Installation abort另外,想了解wait和waitpid,可以看这篇文章:http://hi.baidu.com/qiaoyongfeng/blog/item/3d6fc100bcf93e17738b6576.html4.3.4.WIFEXITED和WEXITSTATUS
#define WIFEXITED(status) (((status) & 0xff) == 0)
#define WEXITSTATUS(status) (((status) >> 8) & 0xff)4.4.去掉update.zip包中的update-binary,报错信息为
because binary_entry == NULL,return INSTALL_CORRUPT
Installation aborted.
各位请帮帮我,到底我哪一步错了,万分火急,先谢谢啦。
.--boot.img|-- META-INF| `-- com| `-- google| `-- android| |-- update-binary | `-- updater-script (说明1)`—system说明1:这个文件我是直接拷贝自android源码树根目录下的这个目录:bootable\recovery\etc\META-INF\com\google\android然后开始签名:$java –jar signapk.jar –w testkey.x509.pem testkey.pk8 update.zip update_signed.zip第三步,将update_signed.zip改名为update.zip($mv update_signed.zip update.zip),因为recovery源代码里对ROM做了这样的指定:static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip";
#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" (说明2)
#define PUBLIC_KEYS_FILE "/res/keys" (说明3)说明3:见4.1所以将改名后的update.zip存放到sdcard根目录下。第四步,重启机器进入recovery模式,开始刷机,报错信息大致是说签名未能验证成功。我在源码中如下几个地方添加了打印信息:
4.1.install_package函数中
int numKeys;
RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
ui_print("numKeys = %6d loadedKeys->len = %6d\n", numKeys,loadedKeys->len);打印结果为:numkeys = 1 loadedKeys->len = 64
其中PUBLIC_KEYS_FILE(见说明3)这个是什么东西,是要在刷机包中添加/res/key这个文件(或目录)吗?google了一下,发现老外也在问这个问题:http://forum.xda-developers.com/showthread.php?t=5812164.2.install_packge函数中
err = verify_file(path, loadedKeys, numKeys); //path = "sdcard/update.zip"
free(loadedKeys);
ui_print("verify_file returned %d\n", err); //打印为err = 1.即VERIFY_FAILURE ,参考4.3.
if (err != VERIFY_SUCCESS) {
ui_print("signature verification failed\n"); //报错,刷机失败,因为signature验证失败。
reset__block();
return INSTALL_CORRUPT;
}4.3.好吧,能不能绕过签名呢?我试了一下,直接将err = VERIFY_SUCCESS,程序继续向下运行,又报错,大致是因为等待子进程结束时遇到堵塞。出错信息见4.3.3
/* Try to open the package.
*/
ZipArchive zip;
err = mzOpenZipArchive(path, &zip);
if (err != 0) {
ui_print("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
reset__block();
return INSTALL_CORRUPT;
} /* Verify and install the contents of the package.
*/
int status = handle_update_package(path, &zip); //函数体见4.3.1.
mzCloseZipArchive(&zip);4.3.1.函数handle_update_package
static int
handle_update_package(const char *path, ZipArchive *zip)
{
// Update should take the rest of the progress bar.
ui_print("Installing update...\n"); int result = try_update_binary(path, zip); //函数体见4.3.2.
register_package_root(NULL, NULL); // Unregister package root
return result;
}
4.3.2.函数try_update_binary (代码有点长,拷贝到sourceinsight里面看比较明了)
// If the package contains an update binary, extract it and run it.
static int
try_update_binary(const char *path, ZipArchive *zip) {
const ZipEntry* binary_entry =
mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); //参考说明2,如去掉update-binary呢?参考4.4.
if (binary_entry == NULL) {
ui_print("because binary_entry == NULL,return INSTALL_CORRUPT\n");
return INSTALL_CORRUPT;
}
char* binary = "/tmp/update_binary";
unlink(binary);
int fd = creat(binary, 0755);
if (fd < 0) {
ui_print("Can't make %s\n", binary);
return 1;
}
bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
close(fd); if (!ok) {
ui_print("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
return 1;
} int pipefd[2];
pipe(pipefd); 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]); //关闭管道0
execv(binary, args);
fprintf(stderr, "E:Can't run %s (%s)\n", binary, strerror(errno));
_exit(-1);
}
close(pipefd[1]); //关闭管道1 char buffer[1024];
FILE* from_child = fdopen(pipefd[0], "r"); //打开管道0
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(str);
} else {
ui_print("\n");
}
} else {
ui_print("unknown command [%s]\n", command);
}
}
fclose(from_child); int status;
waitpid(pid, &status, 0);
ui_print("pid = %6d status = %6d\n", pid,status); //出错在这里。参考4.3.3.
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { //两个宏函数的定义,见4.3.4
ui_print("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
return INSTALL_ERROR;
} return INSTALL_SUCCESS;
}
4.3.3.出错消息为
pid = 59 status = 768
Error in /sdcard/update.zip
(status3)
Installation abort另外,想了解wait和waitpid,可以看这篇文章:http://hi.baidu.com/qiaoyongfeng/blog/item/3d6fc100bcf93e17738b6576.html4.3.4.WIFEXITED和WEXITSTATUS
#define WIFEXITED(status) (((status) & 0xff) == 0)
#define WEXITSTATUS(status) (((status) >> 8) & 0xff)4.4.去掉update.zip包中的update-binary,报错信息为
because binary_entry == NULL,return INSTALL_CORRUPT
Installation aborted.
各位请帮帮我,到底我哪一步错了,万分火急,先谢谢啦。
签名直接绕过去了,直接success,签名是什么?为什么要签名?不是很懂这东西。知道的话还望指教指教。
updater-script : 该文件需要自己根据更新包需要更新的内容自行编写. 具体用什么名字是由 myandroid/bootable/recovery/updater/updater.c 文件中的宏 SCRIPT_NAME 的值而定.(注意: 是updater-script, 而不是 update-script).检查了一下,原来我的update.zip包中的update-script也应该使用updater-script现在改为:
cp myandroid/bootable/recovery/etc/META-INF/com/google/android/update-script META-INF/com/google/android/updater-script
assert file_contains("SYSTEM:build.prop", "ro.product.device=dream") == "true" || file_contains("SYSTEM:build.prop", "ro.build.product=dream") == "true"
assert file_contains("RECOVERY:default.prop", "ro.product.device=dream") == "true" || file_contains("RECOVERY:default.prop", "ro.build.product=dream") == "true"
assert getprop("ro.product.device") == "dream"
format BOOT:
format SYSTEM:
copy_dir PACKAGE:system SYSTEM:
write_raw_image PACKAGE:boot.img BOOT: