Home > Android, Tech.Notes > Android基于ClockworkMod Recovery(CWM)的系统升级

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下载

  1. sss
    May 8th, 2012 at 15:12 | #1

    下了你的 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.

  2. May 8th, 2012 at 16:38 | #2

    应该是权限的问题,你把apk放到/system/app 目录下面运行一下试试看。

  3. sss
    May 8th, 2012 at 17:21 | #3

    啊。第一个问题不要紧。可以想其他的办法。主要是第二个问题,困扰我一下午了。
    我的机器是HTC G7.recovery 是CWM的5.0.2.0,总提示上面那个错误。但我用moto defy试了一下,没问题。recovery 都是cwm的。但htc的recovery 需要按 音量- 好多下,打开bootmenu enable,和这个有关系吗?@BianJiang

  4. May 8th, 2012 at 17:31 | #4

    @sss
    我手头上只有三星的平板,CWM recovery 版本是 5.5.0.4.
    你可以安装一个ROM Manager,用logcat看看,它打印了那些信息,或者它里面的脚本是怎么写的。我调试的时候就是跟踪RomManager才得到这些信息。

  5. sss
    May 8th, 2012 at 17:50 | #5

    啊。我下了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

  6. May 8th, 2012 at 17:53 | #6

    @sss 不客气,相互学习。
    你把RomManager相关的logcat打印信息贴出来,一起分析一下,应该就能找到问题在哪了。

  7. sss
    May 8th, 2012 at 18:40 | #7

    啊。。我用rom magager 选SD卡的rom更新非常成功。。。可以证明是我的问题了。你说的logcat信息,我什么也看不到啊,进入 recovery 的时候usb连接就断了。之前的都是一些下载连接。 请问你是怎么 cat extendedcommand 。我如果能看到 extendedcommand 里面的命令估计会有些帮助。
    @BianJiang

  8. May 8th, 2012 at 19:45 | #8

    @sss
    adb shell logcat

  9. May 8th, 2012 at 19:47 | #9

    @sss
    应该是CWM版本之间有些差别。

  10. sss
    May 9th, 2012 at 09:41 | #10

    啊,,在recovery模式下输入 adb shell logcat 报:
    /sbin/sh: logcat: not found

    真是不顺啊。。
    @BianJiang

  11. May 9th, 2012 at 09:51 | #11

    @sss
    不是在recovery模式下面运行 adb shell logcat.
    而是在正常启动后,你打开rom Manager软件的时候,运行 adb shell logcat.
    还有就是要把调试打开,才能用adb shell

  12. sss
    May 9th, 2012 at 10:22 | #12

    啊,搜噶。执行了/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

  13. May 9th, 2012 at 10:38 | #13

    @sss
    嗯,照这这个版本的脚本改改应该就没有问题了。

  14. sss
    May 9th, 2012 at 13:34 | #14

    崩溃了。。这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

  15. May 9th, 2012 at 14:13 | #15

    @sss
    晕,没碰到过这样的版本, 你可以升级一下你的cwm试试看。

  16. May 22nd, 2013 at 06:20 | #16

    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.

  17. May 22nd, 2013 at 23:05 | #17

    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!

  18. May 24th, 2013 at 11:20 | #18

    Hey administrator, I just wanted to give you a quick heads up that your Web address:
    http://wifihack.net/blog/2012/04/android-clockworkmod-recovery-system-updat/ is being flagged as a
    potentially malicious site in my browser opera. I would
    highly recommend having somebody look into it. You could certainly lose a lot of readers due
    to this kind of problem. Best of Luck.

  1. No trackbacks yet.