Android基于ClockworkMod Recovery(CWM)的系统升级
Android 系统自带了升级的接库RecoverySystem.installPackage,但是通过installPackage进入ClockworkMod Recovery(CWM)后,包的签名验证是打开的,导致升级错误。但是在实践中发现,如果通过自己手动按键进入CWM Recovery模式后,签名验证默认是关闭的,而自己通过APK升级进入后验证是打开的,这个就说明签名验证是可以通过外部参数调整的。由于CWM本身是不开源的,无法看到具体的实现细节,只能通过反编译,RomManager的日志和猜。
CWM提供了一个配套的RomManager.apk进行升级,那我们就从RomManager.apk下手。
通过分析RomManager.apk的日志,发现RomManager.apk在运行的时候会通过一个 rommanager.sh 的脚本,来进行相关的reboot 并进入recovery模式。
1 2 3 4 5 6 7 | border@kvbian:~ s$ cat rommanager.sh mkdir -p /cache/recovery ; cat /data/data/com.koushikdutta.rommanager/files/extendedcommand > /cache/recovery/extendedcommand ; rm /cache/recovery/command ; mkdir -p /sdcard/clockworkmod ; echo 1 > /sdcard/clockworkmod/.recoverycheckpoint ; reboot recovery ; border@kvbian:~ s$ cat extendedcommand ui_print("ROM Manager Version 5.0.0.5"); ui_print("2012年4月18日"); ui_print("Preparing to install ROM..."); assert(install_zip("/sdcard/update/kvbian-rom-signed-20120417-2112.zip")); |
从上面的rommanager.sh 脚本可以看出:
CWM如果要取消签名验证进行升级,不能使用Android默认的
/cache/recovery/command 文件进行相关的命令。
而是使用 /cache/recovery/extendedcommand 执行相关的升级命令。
同时要在 /sdcard/clockworkmod/.recoverycheckpoint 文件做相关的标记。
相关的升级代码:
权限:
1 2 | <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" /> <uses-permission android:name="android.permission.REBOOT" /> |
Java 代码(完成的程序参考: 升级程序RomUpdate下载):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | package com.kvbian.romupdate; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.PowerManager; import android.os.StrictMode; import android.util.Log; import android.widget.TextView; /** * @author Jiang Bian * mail: borderj@gmail.com * blog: http://wifihack.net * */ public class RomUpdateActivity extends Activity { /** Called when the activity is first created. */ /* * Android 3.0 * 测试机器: GT-P7300/GT-P7310 * 本程序之在 ClockworkMod_Recovery_v5.5.0.4 版本测试过,其他版本没有测试。 * ClockworkMod Recovery(CWM) 如果要取消签名验证进行升级,不能使用Android默认的 * /cache/recovery/command 文件进行相关的命令。 * 而是使用 /cache/recovery/extendedcommand 执行相关的升级命令。 * * 同时要在 /sdcard/clockworkmod/.recoverycheckpoint 文件做相关的标记。 * * 本文参考: * 1. frameworks/base/core/java/android/os/RecoverySystem.java * 2. bootable/recovery/recovery.c * 3. RomManager.apk */ private static final String TAG = "RomUpdateActivity"; private static final String ROM_DOWNLOAD_URL = "http://192.168.1.66/update.zip"; private static final String ROMPATH = "/sdcard/update.zip"; private static int buffer_size = 1024 * 10; /** Used to communicate with recovery. See bootable/recovery/recovery.c. */ private static File RECOVERY_DIR = new File("/cache/recovery"); private static File CLOCK_WORK_MOD_DIR = new File("/sdcard/clockworkmod"); private static File CLOCK_WORK_MOD_CHECKPOINT_FILE = new File(CLOCK_WORK_MOD_DIR, ".recoverycheckpoint"); private static File COMMAND_FILE = new File(RECOVERY_DIR, "command"); // // /cache/recovery/extendedcommand private static File EXTENDED_FILE = new File(RECOVERY_DIR, "extendedcommand"); private static File LOG_FILE = new File(RECOVERY_DIR, "log"); /** * Reboot into the recovery system with the supplied argument. * @throws IOException if something goes wrong. * From: frameworks/base/core/java/android/os/RecoverySystem.java border@kvbian:~ s$ cat rommanager.sh mkdir -p /cache/recovery ; cat /data/data/com.koushikdutta.rommanager/files/extendedcommand > /cache/recovery/extendedcommand ; rm /cache/recovery/command ; mkdir -p /sdcard/clockworkmod ; echo 1 > /sdcard/clockworkmod/.recoverycheckpoint ; reboot recovery ; border@kvbian:~ s$ cat extendedcommand ui_print("ROM Manager Version 5.0.0.5"); ui_print("2012年4月18日"); ui_print("Preparing to install ROM..."); assert(install_zip("/sdcard/update/kvbian-rom-signed-20120417-2112.zip")); */ private void bootCommand(Context context) throws IOException { RECOVERY_DIR.mkdirs(); // In case we need it CLOCK_WORK_MOD_DIR.mkdirs(); EXTENDED_FILE.delete(); COMMAND_FILE.delete(); // In case it's not writable CLOCK_WORK_MOD_CHECKPOINT_FILE.delete(); LOG_FILE.delete(); Log.v(TAG, "write ClockWorkMod Checkpoint File: " + CLOCK_WORK_MOD_CHECKPOINT_FILE.getAbsolutePath()); FileWriter checkpoint = new FileWriter(CLOCK_WORK_MOD_CHECKPOINT_FILE); try { checkpoint.write("1"); checkpoint.write("\n"); } finally { checkpoint.close(); } Log.v(TAG, "write Extended Command File: " + EXTENDED_FILE.getAbsolutePath()); Log.v(TAG, "write Extended Command File args: " + ROMPATH); FileWriter command = new FileWriter(EXTENDED_FILE); try { command.write("ui_print(\"ZPad ROM Manager Version:\"); "); command.write("\n"); command.write("ui_print(\"2012.4.18\");"); command.write("\n"); command.write("ui_print(\"Preparing to install ROM...\");"); command.write("\n"); //assert(install_zip("/sdcard/update/kvbian-rom-signed-20120417-2112.zip")); command.write("assert(install_zip(\"" + ROMPATH + "\"));"); command.write("\n"); } finally { command.close(); } /** * boot-recovery * recovery */ // Having written the command file, go ahead and reboot PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); //pm.reboot("recovery"); pm.reboot("recovery"); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads().detectDiskWrites().detectNetwork() // or // .detectAll() // for // all // detectable // problems .penaltyLog().build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects().detectLeakedClosableObjects() .penaltyLog().penaltyDeath().build()); // getRomFromNet(ROM_DOWNLOAD_URL); romUpdate(this); TextView tv = new TextView(this); tv.setText("RomUpdate Version: 1.1 - 2012.4.18"); setContentView(tv); } public void romUpdate(Context context) { try { bootCommand(context); } catch (IOException e) { Log.v(TAG, e.getMessage()); e.printStackTrace(); } } public void getRomFromNet(String romURL) { try { // set the download URL, a url that points to a file on the internet // this is the file to be downloaded Log.v(TAG, " ------------- url: " + romURL + " ------------- "); URL url = new URL(romURL); // create the new connection HttpURLConnection urlConnection = (HttpURLConnection) url .openConnection(); File file = new File(ROMPATH); // this will be used in reading the data from the internet InputStream inputStream = urlConnection.getInputStream(); // this is the total size of the file int totalSize = urlConnection.getContentLength(); // variable to store total downloaded bytes int downloadedSize = 0; // create a buffer... byte[] buffer = new byte[1024]; int bufferLength = 0; // used to store a temporary size of the Log.v(TAG, "Download From [" + romURL + "], Save File To: " + ROMPATH + "]"); FileOutputStream fileOutput = new FileOutputStream(file); while ((bufferLength = inputStream.read(buffer)) > 0) { fileOutput.write(buffer, 0, bufferLength); downloadedSize += bufferLength; } // close the output stream when done fileOutput.close(); Log.v(TAG, " Save File To: [" + ROMPATH + "]"); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } |
测试机器: GT-P7300/GT-P7310
测试环境: Android 3.0
在 ClockworkMod_Recovery_v5.5.0.4 版本测试过,其他版本没有测试。
本文参考:
1. frameworks/base/core/java/android/os/RecoverySystem.java
2. bootable/recovery/recovery.c
3. RomManager.apk
4. 升级程序RomUpdate下载
![[Google]]( http://wifihack.net/blog/wp-content/plugins/easy-adsense-lite/google-light.gif)
下了你的 demo
报:
java.io.FileNotFoundException: /cache/recovery/extendedcommand (Permission denied)
我手动把 extendedcommand 放到cache下。
恢复的时候又报:
ClockworkMod Recovery V5.0.2.0
Waiting for SD Card to mount (20s)
SD Card mounted…
Error verifying extendedcommand.
Error processing ROM Manager script. Please verify you have ROM Manager v4.4.0.0 or higher installed.
/tmp/recovery.log was copied to /sdcard/clockworkmod/recovery.lod. Please open ROM Manager to report the issue.
应该是权限的问题,你把apk放到/system/app 目录下面运行一下试试看。
啊。第一个问题不要紧。可以想其他的办法。主要是第二个问题,困扰我一下午了。
我的机器是HTC G7.recovery 是CWM的5.0.2.0,总提示上面那个错误。但我用moto defy试了一下,没问题。recovery 都是cwm的。但htc的recovery 需要按 音量- 好多下,打开bootmenu enable,和这个有关系吗?@BianJiang
@sss
我手头上只有三星的平板,CWM recovery 版本是 5.5.0.4.
你可以安装一个ROM Manager,用logcat看看,它打印了那些信息,或者它里面的脚本是怎么写的。我调试的时候就是跟踪RomManager才得到这些信息。
啊。我下了Rom manager ,也更新了一下,还是5.0.2.0…还是报:
ClockworkMod Recovery V5.0.2.0
Waiting for SD Card to mount (20s)
SD Card mounted…
Error verifying extendedcommand.
Error processing ROM Manager script. Please verify you have ROM Manager v4.4.0.0 or higher installed.
/tmp/recovery.log was copied to /sdcard/clockworkmod/recovery.lod. Please open ROM Manager to report the issue.
我觉得是挂载SD卡的错误。。
我再研究一下吧,感谢你的回复
@BianJiang
@sss 不客气,相互学习。
你把RomManager相关的logcat打印信息贴出来,一起分析一下,应该就能找到问题在哪了。
啊。。我用rom magager 选SD卡的rom更新非常成功。。。可以证明是我的问题了。你说的logcat信息,我什么也看不到啊,进入 recovery 的时候usb连接就断了。之前的都是一些下载连接。 请问你是怎么 cat extendedcommand 。我如果能看到 extendedcommand 里面的命令估计会有些帮助。
@BianJiang
@sss
adb shell logcat
@sss
应该是CWM版本之间有些差别。
啊,,在recovery模式下输入 adb shell logcat 报:
/sbin/sh: logcat: not found
真是不顺啊。。
@BianJiang
@sss
不是在recovery模式下面运行 adb shell logcat.
而是在正常启动后,你打开rom Manager软件的时候,运行 adb shell logcat.
还有就是要把调试打开,才能用adb shell
啊,搜噶。执行了/data/data/com.koushikdutta.rommanager/files/rommanager.sh using shell /system/bin/sh : sh
原来是在 com.koushikdutta.rommanager/files 文件夹里。都看到了。果然脚本是不一样的,多了一个 preparewipedalvikcache.sh。。 觉得离成功不远了。。
/data/data/com.koushikdutta.rommanager/files/preparewipedalvikcache.sh ; mkdir -p /cache/recovery ; cat /data/data/com.koushikdutta.rommanager/files/extendedcommand > /cache/recovery/extendedcommand ; rm /cache/recovery/command ; mkdir -p /sdcard/clockworkmod ; echo 1 > /sdcard/clockworkmod/.recoverycheckpoint ; reboot recovery ; /data/data/com.koushikdutta.rommanager/files/reboot recovery ;
@BianJiang
@sss
嗯,照这这个版本的脚本改改应该就没有问题了。
崩溃了。。这cwm问题还真多。reboot recovery 的时候在 /sdcard/clockworkmod 下会生成一个 .salted_hash 文件。里面记录了 一串hash值9329ccbdb1c7b1bdfa3d128b50943cec 。这个就是 签名认证的。和 extendedcommand 有关。我测试的时候用 rom manager 生成的extendedcommand 没问题,但我要改 extendedcommand 的话,就不行了。猜测是和 extendedcommand 的生成时间有关,.salted_hash 里的hash值就应该是根据extendedcommand的创建时间算出来的。 把.salted_hash 删掉的话就会报 sd marker not found 错。唉@BianJiang
@sss
晕,没碰到过这样的版本, 你可以升级一下你的cwm试试看。
Its like you read my mind! You appear to know a lot about
this, like you wrote the book in it or something. I think
that you could do with some pics to drive the message home a bit, but other than that, this is fantastic blog.
A fantastic read. I will definitely be back.
Hi! I’m at work surfing around your blog from my new iphone 4! Just wanted to say I love reading through your blog and look forward to all your posts! Carry on the excellent work!