├── Android.mk
├── AndroidManifest.xml
├── jni
├── Android.mk
└── android_rockchip_update_UpdateService.cpp
├── res
├── drawable-hdpi
│ ├── icon.png
│ └── ota_update.png
├── drawable-mdpi
│ ├── ic_dialog_alert.png
│ ├── icon.png
│ └── ota_update.png
├── layout
│ ├── download_notify.xml
│ ├── notify_dialog.xml
│ ├── package_download.xml
│ └── setting.xml
├── values-pl-rPL
│ └── strings.xml
├── values-ru-rRU
│ └── strings.xml
├── values-zh-rCN
│ └── strings.xml
├── values-zh-rTW
│ └── strings.xml
└── values
│ └── strings.xml
└── src
└── android
└── rockchip
└── update
├── service
├── CustomerHttpClient.java
├── FileDownloadTask.java
├── FileInfo.java
├── FirmwareUpdatingActivity.java
├── InvalidFirmwareImageActivity.java
├── NoImageActivity.java
├── NotifyDeleteActivity.java
├── OtaUpdateNotifyActivity.java
├── PackageDownloadActivity.java
├── RKUpdateReceiver.java
├── RKUpdateService.java
├── RecoverySystem.java
├── Setting.java
└── UpdateAndRebootActivity.java
└── util
└── RegetInfoUtil.java
/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH:= $(call my-dir)
2 | include $(CLEAR_VARS)
3 |
4 | LOCAL_MODULE_TAGS := optional
5 |
6 | LOCAL_PACKAGE_NAME := RKUpdateService
7 | LOCAL_CERTIFICATE := platform
8 | LOCAL_SRC_FILES := $(call all-subdir-java-files)
9 | LOCAL_JNI_SHARED_LIBRARIES := rockchip_update_jni
10 | LOCAL_PROGUARD_ENABLED := disabled
11 |
12 | include $(BUILD_PACKAGE)
13 |
14 | include $(call all-makefiles-under,$(LOCAL_PATH))
15 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
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 |
57 |
58 |
63 |
64 |
69 |
70 |
75 |
76 |
81 |
82 |
88 |
89 |
94 |
95 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/jni/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH := $(call my-dir)
2 |
3 | include $(CLEAR_VARS)
4 |
5 | LOCAL_MODULE := rockchip_update_jni
6 | LOCAL_MODULE_TAGS := optional
7 | LOCAL_PRELINK_MODULE := false
8 | LOCAL_SRC_FILES := android_rockchip_update_UpdateService.cpp
9 | LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
10 | LOCAL_SHARED_LIBRARIES := liblog libutils
11 |
12 | include $(BUILD_SHARED_LIBRARY)
13 |
--------------------------------------------------------------------------------
/jni/android_rockchip_update_UpdateService.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #define LOG_TAG __FILE__
8 | #include
9 |
10 | static int read_file(const char* path, int offset, int len, char* buf)
11 | {
12 | int result, ret;
13 | int fd = open(path, O_RDWR);
14 | if (fd < 0) {
15 | LOGE("Fail to open image file file '%s', error : '%s'.",
16 | path, strerror(errno));
17 | return fd;
18 | }
19 | if (lseek(fd, offset, SEEK_SET) < 0) {
20 | LOGE("Fail to seek image file file '%s', error : '%s'.",
21 | path, strerror(errno));
22 | result = -1;
23 | goto clean_and_return;
24 | }
25 | ret = read(fd, buf, len);
26 | if (ret < 0) {
27 | LOGE("Fail to read image file file '%s', bytes read : '%d', error : '%s'.",
28 | path, ret, strerror(errno));
29 | result = errno;
30 | goto clean_and_return;
31 | }
32 | result = 0;
33 | clean_and_return:
34 | if (fd) close(fd);
35 | return result;
36 | }
37 |
38 | static jstring getImageProductName(JNIEnv* env, jobject object, jstring j_path)
39 | {
40 | jstring result = NULL;
41 | const char* path = env->GetStringUTFChars(j_path, 0);
42 | if (!path) {
43 | LOGE("Failed to get utf-8 path from 'j_path'.");
44 | return NULL;
45 | }
46 | LOGD("Image file path : '%s'.", path);
47 | char buf[64];
48 | int offset = 0;
49 | int ret = read_file(path, offset, sizeof(buf), buf);
50 | if (ret) {
51 | LOGE("Fail to read image file file '%s', error : '%s'.",
52 | path, strerror(errno));
53 | goto clean_and_return;
54 | }
55 | if (*(unsigned*)(buf)==0x57464B52)
56 | offset = *(unsigned*)(buf+0x21);
57 | ret = read_file(path, offset+8, sizeof(buf), buf);
58 | if (ret) {
59 | LOGE("Fail to read image file file '%s', error : '%s'.",
60 | path, strerror(errno));
61 | goto clean_and_return;
62 | }
63 | ret = strlen(buf);
64 | if (ret >= 64) {
65 | LOGE("Read invalid (too long) name info(length : '%d'). "
66 | "Image file must be invalid!", ret);
67 | goto clean_and_return;
68 | }
69 | LOGD("Porduce name : '%s'.", buf);
70 | result = env->NewStringUTF(buf);
71 | clean_and_return:
72 | env->ReleaseStringUTFChars(j_path, path);
73 | return result;
74 | }
75 |
76 | static jstring getImageVersion(JNIEnv* env, jobject object, jstring j_path)
77 | {
78 | jstring result = NULL;
79 | const char* path = env->GetStringUTFChars(j_path, 0);
80 | if (!path) {
81 | LOGE("Failed to get utf-8 path from 'j_path'.");
82 | return NULL;
83 | }
84 | LOGD("Image file path : '%s'.", path);
85 | char buf[64];
86 | int offset = 0;
87 | int ret = read_file(path, offset, sizeof(buf), buf);
88 | if (ret) {
89 | LOGE("Fail to read image file file '%s', error : '%s'.",
90 | path, strerror(errno));
91 | goto clean_and_return;
92 | }
93 | if (*(unsigned*)(buf)==0x57464B52)
94 | offset = *(unsigned*)(buf+0x21);
95 | ret = read_file(path, offset+0x84, 4, buf);
96 | if (ret) {
97 | LOGE("Fail to read image file file '%s', error : '%s'.",
98 | path, strerror(errno));
99 | goto clean_and_return;
100 | }
101 | sprintf(buf, "%d.%d.%d", buf[3], buf[2], buf[0] + (buf[1]<<8));
102 | LOGD("Image version : '%s'.", buf);
103 | result = env->NewStringUTF(buf);
104 | clean_and_return:
105 | env->ReleaseStringUTFChars(j_path, path);
106 | return result;
107 | }
108 |
109 | static JNINativeMethod gMethods[] = {
110 | { "getImageVersion", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getImageVersion},
111 | { "getImageProductName", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getImageProductName }
112 | };
113 | #define gMethodsCount (sizeof(gMethods) / sizeof(gMethods[0]))
114 |
115 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
116 | {
117 | const char* name = "android/rockchip/update/service/RKUpdateService";
118 | JNIEnv* env = NULL;
119 | jclass gClass;
120 | LOGI("JNI_OnLoad");
121 | if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_4) != JNI_OK) {
122 | LOGE( "ERROR: GetEnv failed");
123 | return -1;
124 | }
125 | if ((gClass = env->FindClass(name)) == NULL) {
126 | LOGE("Native registration unable to find class '%s'", name);
127 | goto clean_and_return;
128 | }
129 | if (env->RegisterNatives(gClass, gMethods, gMethodsCount) < 0) {
130 | fprintf(stderr, "RegisterNatives failed for '%s'", name);
131 | goto clean_and_return;
132 | }
133 | return JNI_VERSION_1_4;
134 | clean_and_return:
135 | LOGE("ERROR: registerNatives failed");
136 | return -1;
137 | }
138 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msink/android_packages_apps_rkupdateservice/c4a00ecbb2fe41acd9834f5d637adbdf61072a39/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ota_update.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msink/android_packages_apps_rkupdateservice/c4a00ecbb2fe41acd9834f5d637adbdf61072a39/res/drawable-hdpi/ota_update.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_dialog_alert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msink/android_packages_apps_rkupdateservice/c4a00ecbb2fe41acd9834f5d637adbdf61072a39/res/drawable-mdpi/ic_dialog_alert.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msink/android_packages_apps_rkupdateservice/c4a00ecbb2fe41acd9834f5d637adbdf61072a39/res/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ota_update.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msink/android_packages_apps_rkupdateservice/c4a00ecbb2fe41acd9834f5d637adbdf61072a39/res/drawable-mdpi/ota_update.png
--------------------------------------------------------------------------------
/res/layout/download_notify.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
11 |
16 |
17 |
--------------------------------------------------------------------------------
/res/layout/notify_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
13 |
16 |
21 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/res/layout/package_download.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
16 |
22 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/res/layout/setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 | android:orientation="horizontal"
10 |
16 |
20 |
21 |
24 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/res/values-pl-rPL/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Aktualizacja Systemu
4 | Pokaż UI aktualizacji systemu.
5 | Pokaż UI aktualizacji systemu.
6 | Aktualizacja Oprogramowania
7 | "Znaleziono paczke z aktualizacją: '%1$s'. Czy chcesz ją teraz zainstalować?"
8 | Instaluj
9 | Anuluj
10 | "Sprawdzanie paczki aktualizacji. To może potrwać kilka minut. Proszę nie korzystać z urządzenia w trakcie trwania procesu. Urządzenie uruchomi sie ponownie i zainstaluje aktualizcję.\n\nSugerujemy podłączenie urządzenia do źródła zasilania."
11 | Proszę NIE usuwać karty SD z urządzenia!
12 | Nieprawidłowy obraz oprogramowania
13 | "Znaleziono nieprawidłowy plik oprogramowania '%1$s'. Czy chcesz go usunąć?"
14 | Tak
15 | Nie
16 | Nie znaleziono pliku z aktulizacją
17 | "Plik aktualizacji nie istnieje w lokalizacjach '%1$s' i '%2$s'!"
18 | OK
19 | Usunąć plik z aktualizacją? Jeśli plik nie zostanie usunięty urządzenie może ponownie pytać o wykonanie aktualizacji!
20 | Aktualizacja przebiegła pomyślnie!
21 | Aktualizacja nie powiodła się!
22 | Stan: Bezczynny
23 | Pobieranie
24 | Znaleziono nową wersję oprogramowania! Czy chcesz wykonać aktualizację? Wersja oprogramowania:
25 | , rozmiar:
26 | Rozpocznij
27 | Rozpoczynanie
28 | Ponów
29 | Wstrzymaj
30 | Wstrzymywanie
31 | Anuluj pobieranie
32 | Sprawdzaj automatycznie
33 | Sprawdź teraz
34 | błąd
35 | nieprawidłowa paczka oprogramowania!
36 |
37 |
--------------------------------------------------------------------------------
/res/values-ru-rRU/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Обновление системы
4 | Показать настройки обновления системы
5 | Показать настройки обновления системы
6 | Обновление системы
7 | "Файл обновления найден: '%1$s'. Установить обновление?"
8 | Установить
9 | Отмена
10 | Установочный пакет проверяется. Устройство перезагрузится и начнётся обновление системы. В течение этого процесса кабель USB или сетевое зарядное устройство должно быть подключено.
11 | Не извлекайте карту памяти!
12 | Неверный дистрибутив обновленя
13 | "Файл обновления некорректен: '%1$s'. Удалить этот файл?"
14 | Да
15 | Нет
16 | Файл обновления не найден
17 | "Файл обновления не найден в директориях '%1$s' and '%2$s'!"
18 | Да
19 | Удалить файл обновления? Это безопасно. Если нет, система может повторно задать этот вопрос.
20 | Обновление прошло успешно!
21 | Обновление не удалось
22 | Режим ожидания
23 | Загрузка
24 | Доступно OTA-обновление. Вы хотите его установить? Новая версия:
25 | , размер:
26 | Старт
27 | Запуск
28 | Повтор
29 | Пауза
30 | Остановка
31 | Отмена загрузки
32 | Автопроверка обновлений
33 | Проверить сейчас
34 | ошибка
35 | не является допустимым пакетом обновлений!
36 |
37 |
--------------------------------------------------------------------------------
/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 系统升级
4 | 固件升级
5 | "发现一个升级包 : '%1$s'. 是否要安装升级包?"
6 | 安装
7 | 放弃
8 | 设备将重启并升级固件, 请保证您的 USB 电缆或充电器是连接状态!
9 | 请切勿将 SD 卡从设备中拔出.
10 | 无效的固件镜像.
11 | "发现一个无效的固件镜像文件 '%1$s'. 是否需要删除它?"
12 | 是
13 | 否
14 | 没有升级用镜像文件
15 | "在目录 '%1$s' 和 '%2$s' 中, 没有固件镜像文件."
16 | 确定
17 | 是否删除升级包?如果不删除,系统可能再次提示升级,可能会给您带来不便!
18 | 恭喜升级成功!
19 | 对不起升级失败!
20 | 可以升级新版本! 确定要升级吗? 升级包版本:
21 | , 大小:
22 | 开始
23 | 正在开始
24 | 重试
25 | 暂停
26 | 正在停止
27 | 取消下载
28 | 自动检查更新
29 | 立即检查
30 |
31 |
--------------------------------------------------------------------------------
/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 系統升級
4 | 固件升級
5 | "發現一個固件文件 : '%1$s'.是否要升級到該鏡像文件?"
6 | 安裝
7 | 放棄
8 | 設備將重啟併升級固件, 請保證您的USB 電纜或充電器是連接狀態!
9 | 請切勿將 SD 卡從設備中拔出.
10 | 無效的固件鏡像.
11 | "發現一個無效的固件鏡像文件 '%1$s'. 是否需要刪除它?"
12 | 是
13 | 否
14 | 沒有升級用鏡像文件
15 | "在目錄 '%1$s' 和 '%2$s' 中, 沒有固件鏡像文件."
16 | 確認
17 | 是否刪除升級包?如果不刪除,系統可能會再次提示升級,可能會給您帶來不便!
18 | 恭喜升級成功!
19 | 對不起升級失敗!
20 | 可以升級新版本! 確定要升級嗎? 升級包版本:
21 | , 大小:
22 | 開始
23 | 正在開始
24 | 重試
25 | 暫停
26 | 正在停止
27 | 取消下載
28 | 自動檢查更新
29 | 立即檢查
30 |
31 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | SystemUpdate
4 | Show UI for firmware update.
5 | Show UI for firmware update.
6 | Firmware Updating
7 | "An update package file is found: '%1$s'. Would you like to install?"
8 | Install
9 | Cancel
10 | Now is to check the package! Device is going to reboot and update firmware, please keep your USB cable or DC-in connected!
11 | Do NOT remove SD card from the device!
12 | Invalid firmware image
13 | "Found an invalid firmware image file '%1$s'. Would you like to delete it?"
14 | Yes
15 | No
16 | No update image file
17 | "There is no firmware image file in directory '%1$s' and '%2$s'!"
18 | OK
19 | "To delete update package? It's safe to delete. If not, the system may once again asking for update, which is annoying!"
20 | Congratulations. Update succeeded!
21 | Sorry, firmware update failed!
22 | State: Idle
23 | Downloading
24 | Find an OTA update package! Do you want to update? Update package version:
25 | , size:
26 | Start
27 | Starting
28 | Retry
29 | Pause
30 | Stopping
31 | Cancel download
32 | Auto check update
33 | Check now
34 | error
35 | not a valid update package!
36 |
37 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/CustomerHttpClient.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import org.apache.http.HttpHost;
4 | import org.apache.http.HttpVersion;
5 | import org.apache.http.client.HttpClient;
6 | import org.apache.http.client.params.HttpClientParams;
7 | import org.apache.http.conn.ClientConnectionManager;
8 | import org.apache.http.conn.params.ConnPerRouteBean;
9 | import org.apache.http.conn.params.ConnManagerParams;
10 | import org.apache.http.conn.routing.HttpRoute;
11 | import org.apache.http.conn.scheme.PlainSocketFactory;
12 | import org.apache.http.conn.scheme.Scheme;
13 | import org.apache.http.conn.scheme.SchemeRegistry;
14 | import org.apache.http.conn.ssl.SSLSocketFactory;
15 | import org.apache.http.impl.client.DefaultHttpClient;
16 | import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
17 | import org.apache.http.params.BasicHttpParams;
18 | import org.apache.http.params.HttpConnectionParams;
19 | import org.apache.http.params.HttpParams;
20 | import org.apache.http.params.HttpProtocolParams;
21 | import org.apache.http.protocol.HTTP;
22 |
23 | public class CustomerHttpClient {
24 | private static HttpClient customerHttpClient;
25 |
26 | private CustomerHttpClient() {
27 | }
28 |
29 | public static synchronized HttpClient getHttpClient() {
30 | if (null == customerHttpClient) {
31 | HttpParams params = new BasicHttpParams();
32 | HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
33 | HttpProtocolParams.setContentCharset(params, HTTP.UTF_8) ;
34 | HttpProtocolParams.setUseExpectContinue(params, true);
35 | HttpProtocolParams.setUserAgent(params, "rk29sdk/4.0");
36 | ConnManagerParams.setMaxTotalConnections(params, 100);
37 | ConnPerRouteBean connPerRoute = new ConnPerRouteBean(20);
38 | HttpHost localhost = new HttpHost("locahost", 80);
39 | connPerRoute.setMaxForRoute(new HttpRoute(localhost), 50);
40 | ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
41 | ConnManagerParams.setTimeout(params, 1000);
42 | HttpConnectionParams.setConnectionTimeout(params, 2 * 1000);
43 | HttpConnectionParams.setSoTimeout(params, 4000);
44 | HttpClientParams.setRedirecting(params, true);
45 | SchemeRegistry schReg = new SchemeRegistry();
46 | schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
47 | schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
48 | ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
49 | customerHttpClient = new DefaultHttpClient(conMgr, params);
50 | }
51 | return customerHttpClient;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/FileDownloadTask.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 | import android.rockchip.update.util.RegetInfoUtil;
3 |
4 | import android.os.Bundle;
5 | import android.os.Message;
6 | import android.os.Handler;
7 | import android.util.Log;
8 | import org.apache.http.Header;
9 | import org.apache.http.HttpResponse;
10 | import org.apache.http.client.HttpClient;
11 | import org.apache.http.client.ClientProtocolException;
12 | import org.apache.http.client.methods.HttpGet;
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 | import java.io.RandomAccessFile;
17 | import java.net.URI;
18 | import java.util.concurrent.Executors;
19 | import java.util.concurrent.ExecutorService;
20 | import java.util.concurrent.TimeUnit;
21 |
22 | public class FileDownloadTask extends Thread {
23 | private String TAG = "FileDownloadTask";
24 | private HttpClient mHttpClient;
25 | private String mPath;
26 | private String mFileName;
27 | private String mTempFileName;
28 | private URI mUri;
29 | private FileInfo mFileInfo;
30 | private boolean mDebug = true;
31 | private long mContentLength;
32 | private long mReceivedCount;
33 | private boolean mAcceptRanges = false;
34 | private int mPoolThreadNum;
35 | private Handler mProgressHandler;
36 | private ExecutorService mDownloadThreadPool;
37 | private volatile int err = ERR_NOERR;
38 | private boolean requestStop = false;
39 | private Object sync = new Object();
40 | private static final int BUFF_SIZE = 4096;
41 |
42 | public static final int ERR_CONNECT_TIMEOUT = 1;
43 | public static final int ERR_NOERR = 0;
44 | public static final int ERR_FILELENGTH_NOMATCH = 2;
45 | public static final int ERR_REQUEST_STOP = 3;
46 | public static final int ERR_NOT_EXISTS = 4;
47 | public static final int ERR_SOCKET_TIMEOUT = 5;
48 | public static final int ERR_CLIENT_PROTOCAL = 6 ;
49 | public static final int ERR_IOEXCEPTION = 7 ;
50 |
51 | // message
52 | public static final int PROGRESS_UPDATE = 1;
53 | public static final int PROGRESS_STOP_COMPLETE = 2;
54 | public static final int PROGRESS_START_COMPLETE = 3;
55 | public static final int PROGRESS_DOWNLOAD_COMPLETE = 4;
56 |
57 | public FileDownloadTask(HttpClient httpClient, URI uri, String path, String fileName, int poolThreadNum) {
58 | mHttpClient = httpClient;
59 | mPath = path;
60 | mUri = uri;
61 | mPoolThreadNum = poolThreadNum;
62 | mReceivedCount = (long) 0;
63 |
64 | if (fileName == null) {
65 | String uriStr = uri.toString();
66 | mFileName = uriStr.substring(uriStr.lastIndexOf("/") + 1,
67 | uriStr.lastIndexOf("?") > 0 ? uriStr.lastIndexOf("?"): uriStr.length());
68 | } else {
69 | mFileName = fileName;
70 | }
71 | if (mFileName.lastIndexOf(".") > 0) {
72 | mTempFileName = "."
73 | + mFileName.substring(0, mFileName.lastIndexOf("."))
74 | + "__tp.xml";
75 | } else {
76 | mTempFileName = "." + mFileName + "__tp.xml";
77 | }
78 | Log.d(TAG, "tempFileName = " + mTempFileName);
79 | }
80 |
81 | public void setProgressHandler(Handler progressHandler) {
82 | mProgressHandler = progressHandler;
83 | }
84 |
85 | @Override
86 | public void run() {
87 | startTask();
88 | }
89 |
90 | private void startTask() {
91 | try {
92 | err = ERR_NOERR;
93 | requestStop = false;
94 | getDownloadFileInfo(mHttpClient);
95 | startWorkThread();
96 | monitor();
97 | finish();
98 | } catch (IOException e) {
99 | e.printStackTrace();
100 | Log.e(TAG, "can't connect the network or timeout");
101 | err = ERR_CONNECT_TIMEOUT;
102 | onProgressStopComplete(err);
103 | } catch (Exception e) {
104 | e.printStackTrace();
105 | onProgressStopComplete(err);
106 | }
107 | }
108 |
109 | public void stopDownload() {
110 | err = ERR_REQUEST_STOP;
111 | requestStop = true;
112 | }
113 |
114 | private void onProgressUpdate(int percent) {
115 | if (mProgressHandler != null) {
116 | Message m = new Message();
117 | m.what = PROGRESS_UPDATE;
118 | Bundle b = new Bundle();
119 | b.putInt("percent", percent);
120 | m.setData(b);
121 | mProgressHandler.sendMessage(m);
122 | Log.d(TAG, "send ProgressUpdate");
123 | }
124 | }
125 |
126 | private void onProgressStopComplete(int errCode) {
127 | if (mProgressHandler != null) {
128 | Message m = new Message();
129 | m.what = PROGRESS_STOP_COMPLETE;
130 | Bundle b = new Bundle();
131 | b.putInt("err", errCode);
132 | m.setData(b);
133 | mProgressHandler.sendMessage(m);
134 | Log.d(TAG, "send ProgressStopComplete");
135 | }
136 | }
137 |
138 | private void onProgressStartComplete() {
139 | if (mProgressHandler != null) {
140 | Message m = new Message();
141 | m.what = PROGRESS_START_COMPLETE;
142 | mProgressHandler.sendMessage(m);
143 | Log.d(TAG, "send ProgressStartComplete");
144 | }
145 | }
146 |
147 | private void onProgressDownloadComplete() {
148 | if (mProgressHandler != null) {
149 | Message m = new Message();
150 | m.what = PROGRESS_DOWNLOAD_COMPLETE;
151 | mProgressHandler.sendMessage(m);
152 | Log.d(TAG, "send ProgressDownloadComplete");
153 | }
154 | }
155 |
156 | private void finish() throws InterruptedException, IllegalArgumentException, IllegalStateException, IOException {
157 | if (err == ERR_NOERR) {
158 | String fullTempfilePath = mPath.endsWith("/") ? (mPath + mTempFileName) : (mPath + "/" + mTempFileName);
159 | Log.d(TAG, "tempfilepath = " + fullTempfilePath);
160 | File f = new File(fullTempfilePath);
161 | if (f.exists()) {
162 | f.delete();
163 | Log.d(TAG, "finish(): delete the temp file!");
164 | }
165 |
166 | onProgressDownloadComplete();
167 | Log.d(TAG, "download successfull");
168 | return;
169 | } else if (err == ERR_REQUEST_STOP) {
170 | mDownloadThreadPool.shutdownNow();
171 | while (!mDownloadThreadPool.awaitTermination(3, TimeUnit.SECONDS)) {
172 | Log.d(TAG, "monitor: progress ===== " + mReceivedCount + "/" + mContentLength);
173 | onProgressUpdate((int)(mReceivedCount * 100 / mContentLength));
174 | }
175 |
176 | } else if (err == ERR_CONNECT_TIMEOUT) {
177 | mDownloadThreadPool.shutdown();
178 | while (!mDownloadThreadPool.awaitTermination(3, TimeUnit.SECONDS) && requestStop == false) {
179 | Log.d(TAG, "monitor: progress ===== " + mReceivedCount + "/"+ mContentLength);
180 | onProgressUpdate((int)(mReceivedCount * 100 / mContentLength));
181 | }
182 |
183 | mDownloadThreadPool.shutdownNow();
184 | while (!mDownloadThreadPool.awaitTermination(3, TimeUnit.SECONDS));
185 | }
186 |
187 | String fullTempfilePath = mPath.endsWith("/") ? (mPath + mTempFileName) : (mPath + "/" + mTempFileName);
188 | Log.d(TAG, "tempfilepath = " + fullTempfilePath);
189 | File f = new File(fullTempfilePath);
190 | RegetInfoUtil.writeFileInfoXml(f, mFileInfo);
191 | Log.d(TAG, "download task not complete, save the progress !!!");
192 | onProgressStopComplete(err);
193 | }
194 |
195 | private void monitor() {
196 | onProgressStartComplete();
197 | while (mReceivedCount < mContentLength && err == ERR_NOERR) {
198 | Log.d(TAG, "monitor: progress ===== " + mReceivedCount + "/"
199 | + mContentLength);
200 | try {
201 | Thread.sleep(1000);
202 | onProgressUpdate((int)(mReceivedCount * 100 / mContentLength));
203 | } catch (InterruptedException e) {
204 | e.printStackTrace();
205 | }
206 | }
207 |
208 | if (err == ERR_CONNECT_TIMEOUT) {
209 | Log.e(TAG, "monitor : ERR_CONNECT_TIMEOUT");
210 | }
211 |
212 | if (err == ERR_REQUEST_STOP) {
213 | Log.e(TAG, "monitor: ERR_REQUEST_STOP");
214 | }
215 | }
216 |
217 | private int startWorkThread() throws Exception {
218 |
219 | String fullPath = mPath.endsWith("/") ? (mPath + mFileName) : (mPath
220 | + "/" + mFileName);
221 | String fullTempfilePath = mPath.endsWith("/") ? (mPath + mTempFileName)
222 | : (mPath + "/" + mTempFileName);
223 |
224 | Log.d(TAG, "tempfilepath = " + fullTempfilePath);
225 | File targetFile = new File(fullPath);
226 |
227 | if (!targetFile.exists()) {
228 | targetFile.createNewFile();
229 | } else {
230 | File tmpFile = new File(fullTempfilePath);
231 | if (tmpFile.exists()) {
232 | mFileInfo = RegetInfoUtil.parseFileInfoXml(tmpFile);
233 | Log.d(TAG, "target file have not download complete, so we try to continue download!");
234 | } else {
235 | targetFile.delete();
236 | targetFile.createNewFile();
237 | Log.d(TAG, "find the same name target file, so delete and rewrite it!!!");
238 | }
239 | }
240 |
241 | if (mFileInfo == null) {
242 | mFileInfo = new FileInfo();
243 | mFileInfo.setFileLength(mContentLength);
244 | mFileInfo.setmURI(mUri);
245 | mFileInfo.setFileName(mFileName);
246 | mFileInfo.setReceivedLength(0);
247 | }
248 |
249 | if (mFileInfo.getFileLength() != mContentLength
250 | && mFileInfo.getURI().equals(mUri)) {
251 | err = ERR_FILELENGTH_NOMATCH;
252 | Log.e(TAG,"FileLength or uri not the same, you can't continue download!");
253 | throw new Exception("ERR_FILELENGTH_NOMATCH!");
254 | }
255 |
256 | DownloadListener listener = new DownloadListener() {
257 | public void onPerBlockDown(int count, int pieceId, long posNew) {
258 | synchronized (this) {
259 | mReceivedCount += count;
260 | }
261 |
262 | mFileInfo.modifyPieceState(pieceId, posNew);
263 | mFileInfo.setReceivedLength(mReceivedCount);
264 | }
265 |
266 | public void onPieceComplete() {
267 | Log.d(TAG, "one piece complete");
268 | }
269 |
270 | public void onErrorOccurre(int pieceId, long posNow) {
271 | mFileInfo.modifyPieceState(pieceId, posNow);
272 | }
273 | };
274 |
275 | if (mAcceptRanges) {
276 | Log.d(TAG, "Support Ranges");
277 | if (mDownloadThreadPool == null) {
278 | mDownloadThreadPool = Executors.newFixedThreadPool(mPoolThreadNum);
279 | }
280 | if (mFileInfo.getPieceNum() == 0) {
281 | long pieceSize = (mContentLength / mPoolThreadNum) + 1;
282 | long start = 0, end = pieceSize - 1;
283 | int pieceId = 0;
284 | do {
285 | if (end > mContentLength - 1) {
286 | end = mContentLength - 1;
287 | }
288 | Log.d(TAG, "piece info, startpos = " + start + " , endpos = " + end);
289 | DownloadFilePieceRunnable task = new DownloadFilePieceRunnable(targetFile, pieceId, start, end, start, true);
290 | mFileInfo.addPiece(start, end, start);
291 | task.setDownloadListener(listener);
292 | mDownloadThreadPool.execute(task);
293 |
294 | start += pieceSize;
295 | end = start + pieceSize - 1;
296 | pieceId++;
297 |
298 | } while (start < mContentLength);
299 | } else {
300 | Log.d(TAG, "try to continue download ====>");
301 | mReceivedCount = mFileInfo.getReceivedLength();
302 | for (int index = 0; index < mFileInfo.getPieceNum(); index++) {
303 | FileInfo.Piece p = mFileInfo.getPieceById(index);
304 | DownloadFilePieceRunnable task = new DownloadFilePieceRunnable(
305 | targetFile, index, p.getStart(), p.getEnd(),
306 | p.getPosNow(), true);
307 | task.setDownloadListener(listener);
308 | mDownloadThreadPool.execute(task);
309 | }
310 | }
311 | } else {
312 | Log.d(TAG, "Can't Ranges!");
313 | if (mDownloadThreadPool == null) {
314 | mDownloadThreadPool = Executors.newFixedThreadPool(1);
315 | }
316 | if (mFileInfo.getPieceNum() == 0) {
317 | DownloadFilePieceRunnable task = new DownloadFilePieceRunnable(
318 | targetFile, 0, 0, mContentLength - 1, 0, false);
319 | mFileInfo.addPiece(0, mContentLength - 1, 0);
320 | task.setDownloadListener(listener);
321 | mDownloadThreadPool.execute(task);
322 | } else {
323 | Log.d(TAG, "try to continue download ====>");
324 | mReceivedCount = (long) 0;
325 | FileInfo.Piece p = mFileInfo.getPieceById(0);
326 | p.setPosNow(0);
327 | DownloadFilePieceRunnable task = new DownloadFilePieceRunnable(
328 | targetFile, 0, 0, mContentLength - 1, p.getPosNow(),
329 | false);
330 | task.setDownloadListener(listener);
331 | mDownloadThreadPool.execute(task);
332 | }
333 | }
334 |
335 | return 0;
336 | }
337 |
338 | private void getDownloadFileInfo(HttpClient httpClient) throws IOException, ClientProtocolException, Exception {
339 | HttpGet httpGet = new HttpGet(mUri);
340 | HttpResponse response = httpClient.execute(httpGet);
341 | int statusCode = response.getStatusLine().getStatusCode();
342 |
343 | if (statusCode != 200) {
344 | err = ERR_NOT_EXISTS;
345 | Log.d(TAG, "response statusCode = " + statusCode);
346 | throw new Exception("resource is not exist!");
347 | }
348 |
349 | if (mDebug) {
350 | for (Header header : response.getAllHeaders()) {
351 | Log.d(TAG, header.getName() + ":" + header.getValue());
352 | }
353 | }
354 |
355 | Header[] headers = response.getHeaders("OtaPackageLength");
356 | if (headers.length > 0) {
357 | mContentLength = Long.valueOf(headers[0].getValue()).longValue();
358 | }
359 | httpGet.abort();
360 | httpGet = new HttpGet(mUri);
361 | httpGet.addHeader("Range", "bytes=0-" + (mContentLength - 1));
362 | response = httpClient.execute(httpGet);
363 | if (response.getStatusLine().getStatusCode() == 206) {
364 | mAcceptRanges = true;
365 | }
366 | httpGet.abort();
367 | }
368 |
369 | private interface DownloadListener {
370 | public void onPerBlockDown(int count, int pieceId, long posNew);
371 |
372 | public void onPieceComplete();
373 |
374 | public void onErrorOccurre(int pieceId, long posNew);
375 | }
376 |
377 | private class DownloadFilePieceRunnable implements Runnable {
378 | private File mFile;
379 | private long mStartPosition;
380 | private long mEndPosition;
381 | private long mPosNow;
382 | private boolean mIsRange;
383 | private DownloadListener mListener;
384 | private int mPieceId;
385 |
386 | public DownloadFilePieceRunnable(File file, int pieceId,
387 | long startPosition, long endPosition, long posNow,
388 | boolean isRange) {
389 | mFile = file;
390 | mStartPosition = startPosition;
391 | mEndPosition = endPosition;
392 | mIsRange = isRange;
393 | mPieceId = pieceId;
394 | mPosNow = posNow;
395 | }
396 |
397 | public void setDownloadListener(DownloadListener listener) {
398 | mListener = listener;
399 | }
400 |
401 | public void run() {
402 | if (mDebug) {
403 | Log.d(TAG, "Start:" + mStartPosition + "-" + mEndPosition + " posNow:" + mPosNow);
404 | }
405 | try {
406 | HttpGet httpGet = new HttpGet(mUri);
407 | if (mIsRange) {
408 | httpGet.addHeader("Range", "bytes=" + mPosNow + "-" + mEndPosition);
409 | }
410 | HttpResponse response = mHttpClient.execute(httpGet);
411 | int statusCode = response.getStatusLine().getStatusCode();
412 | if (mDebug) {
413 | for (Header header : response.getAllHeaders()) {
414 | Log.d(TAG, header.getName() + ":" + header.getValue());
415 | }
416 | Log.d(TAG, "statusCode:" + statusCode);
417 | }
418 | if (statusCode == 206 || (statusCode == 200 && !mIsRange)) {
419 | InputStream inputStream = response.getEntity().getContent();
420 |
421 | @SuppressWarnings("resource")
422 | RandomAccessFile outputStream = new RandomAccessFile(mFile, "rw");
423 |
424 | outputStream.seek(mPosNow);
425 | int count = 0;
426 | byte[] buffer = new byte[BUFF_SIZE];
427 | while ((count = inputStream.read(buffer, 0, buffer.length)) > 0) {
428 | if (Thread.interrupted()) {
429 | Log.d("WorkThread", "interrupted ====>>");
430 | httpGet.abort();
431 | return;
432 | }
433 | outputStream.write(buffer, 0, count);
434 | mPosNow += count;
435 | afterPerBlockDown(count, mPieceId, mPosNow);
436 | }
437 | outputStream.close();
438 | }
439 | httpGet.abort();
440 | } catch (IOException e) {
441 | ErrorOccurre(mPieceId, mPosNow);
442 | err = ERR_CONNECT_TIMEOUT;
443 | return;
444 | }
445 | onePieceComplete();
446 | if (mDebug) {
447 | Log.d(TAG, "End:" + mStartPosition + "-" + mEndPosition);
448 | }
449 | }
450 |
451 | private void afterPerBlockDown(int count, int pieceId, long posNew) {
452 | if (mListener != null) {
453 | mListener.onPerBlockDown(count, pieceId, posNew);
454 | }
455 | }
456 |
457 | private void onePieceComplete() {
458 | if (mListener != null) {
459 | mListener.onPieceComplete();
460 | }
461 | }
462 |
463 | private void ErrorOccurre(int pieceId, long posNew) {
464 | if (mListener != null) {
465 | mListener.onErrorOccurre(pieceId, posNew);
466 | }
467 | }
468 | }
469 | }
470 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/FileInfo.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import java.net.URI;
4 | import java.util.ArrayList;
5 | import android.util.Log;
6 |
7 | public class FileInfo {
8 | private static final String TAG = "FileInfo";
9 | private URI mURI;
10 | private long mFileLength;
11 | private long mReceivedLength;
12 | private String mFileName;
13 | private ArrayList mPieceList;
14 |
15 | public URI getURI() {
16 | return mURI;
17 | }
18 |
19 | synchronized public void setmURI(URI mURI) {
20 | this.mURI = mURI;
21 | }
22 |
23 | public long getReceivedLength() {
24 | return mReceivedLength;
25 | }
26 |
27 | synchronized public void setReceivedLength(long length) {
28 | mReceivedLength = length;
29 | }
30 |
31 | public long getFileLength() {
32 | return mFileLength;
33 | }
34 |
35 | synchronized public void setFileLength(long mFileLength) {
36 | this.mFileLength = mFileLength;
37 | }
38 |
39 | public int getPieceNum() {
40 | if(mPieceList == null) {
41 | mPieceList = new ArrayList();
42 | }
43 | return mPieceList.size();
44 | }
45 |
46 | public String getFileName() {
47 | return mFileName;
48 | }
49 |
50 | synchronized public void setFileName(String mFileName) {
51 | this.mFileName = mFileName;
52 | }
53 |
54 | synchronized public void addPiece(long start, long end, long posNow){
55 | if(mPieceList == null) {
56 | mPieceList = new ArrayList();
57 | }
58 |
59 | Piece p = new Piece(start, end, posNow);
60 | mPieceList.add(p);
61 | }
62 |
63 | synchronized public void modifyPieceState(int pieceID, long posNew) {
64 | Piece p = mPieceList.get(pieceID);
65 | if(p != null) {
66 | p.setPosNow(posNew);
67 | }
68 | }
69 |
70 | public Piece getPieceById(int pieceID) {
71 | return mPieceList.get(pieceID);
72 | }
73 |
74 | public void printDebug() {
75 | Log.d(TAG, "filename = " + mFileName);
76 | Log.d(TAG, "uri = " + mURI);
77 | Log.d(TAG, "PieceNum = " + mPieceList.size());
78 | Log.d(TAG, "FileLength = " + mFileLength);
79 |
80 | int id = 0;
81 | for(Piece p : mPieceList) {
82 | Log.d(TAG, "piece " + id + " :start = " + p.getStart() + " end = " + p.getEnd() + " posNow = " + p.getPosNow());
83 | id++;
84 | }
85 | }
86 |
87 | public class Piece {
88 | private long start;
89 | private long end;
90 | private long posNow;
91 |
92 | public Piece(long s, long e, long n) {
93 | start = s;
94 | end = e;
95 | posNow = n;
96 | }
97 | public long getStart() {
98 | return start;
99 | }
100 | public long getEnd() {
101 | return end;
102 | }
103 | public long getPosNow() {
104 | return posNow;
105 | }
106 | public void setPosNow(long posNow) {
107 | this.posNow = posNow;
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/FirmwareUpdatingActivity.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.os.Bundle;
9 | import android.util.Log;
10 | import com.android.internal.app.AlertActivity;
11 | import com.android.internal.app.AlertController;
12 | import java.util.Formatter;
13 | import java.util.Locale;
14 |
15 | public class FirmwareUpdatingActivity extends AlertActivity implements DialogInterface.OnClickListener {
16 | private static final String TAG = "FirmwareUpdatingActivity";
17 | private static void LOG(String msg) { Log.d(TAG, msg); }
18 |
19 | private String FLASH_ROOT = "/flash";
20 | private String SDCARD_ROOT = "/sdcard";
21 | private String mCurrentVersion;
22 | private String mImageFilePath;
23 | private String mImageVersion;
24 | private static StringBuilder sFormatBuilder = new StringBuilder();
25 | private static Formatter sFormatter = new Formatter(sFormatBuilder, Locale.getDefault());
26 |
27 | protected void onCreate(Bundle savedInstanceState) {
28 | LOG("onCreate() : Entered.");
29 | super.onCreate(savedInstanceState);
30 | Bundle extr = getIntent().getExtras();
31 | mImageFilePath = extr.getString(RKUpdateService.EXTRA_IMAGE_PATH);
32 | mImageVersion = extr.getString(RKUpdateService.EXTRA_IMAGE_VERSION);
33 | mCurrentVersion = extr.getString(RKUpdateService.EXTRA_CURRENT_VERSION);
34 | AlertController.AlertParams p = mAlertParams;
35 | p.mTitle = getString(R.string.updating_title);
36 | String messageFormat = getString(R.string.updating_message_formate);
37 | sFormatBuilder.setLength(0);
38 | sFormatter.format(messageFormat, mImageFilePath);
39 | p.mMessage = sFormatBuilder.toString();
40 | p.mPositiveButtonText = getString(R.string.updating_button_install);
41 | p.mPositiveButtonListener = this;
42 | p.mNegativeButtonText = getString(R.string.updating_button_cancel);
43 | p.mNegativeButtonListener = this;
44 | setupAlert();
45 | IntentFilter filter = new IntentFilter();
46 | filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
47 | filter.addDataScheme("file");
48 | registerReceiver(mReceiver, filter);
49 | }
50 |
51 | protected void onDestroy() {
52 | super.onDestroy();
53 | LOG("onDestroy() : Entered.");
54 | mImageFilePath = null;
55 | mImageVersion = null;
56 | mCurrentVersion = null;
57 | unregisterReceiver(mReceiver);
58 | }
59 |
60 | protected void onPause() {
61 | super.onPause();
62 | LOG("onPause() : Entered.");
63 | }
64 |
65 | protected void onResume() {
66 | super.onResume();
67 | }
68 |
69 | private BroadcastReceiver mReceiver = new BroadcastReceiver() {
70 | public void onReceive(Context context, Intent intent) {
71 | LOG("mReceiver.onReceive() : 'action' =" + intent.getAction());
72 | if (intent.getAction() == Intent.ACTION_MEDIA_UNMOUNTED) {
73 | String path = intent.getData().getPath();
74 | LOG("mReceiver.onReceive() : original mount point : " + path + "; image file path : " + mImageFilePath);
75 | if (mImageFilePath != null && mImageFilePath.contains(path)) {
76 | LOG("mReceiver.onReceive() : Media that img file live in is unmounted, to finish this activity.");
77 | finish();
78 | }
79 | }
80 | }
81 | };
82 |
83 | public void onClick(DialogInterface dialog, int which) {
84 | if (which == DialogInterface.BUTTON_POSITIVE) {
85 | Intent intent = new Intent();
86 | intent.setClass(this, UpdateAndRebootActivity.class);
87 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
88 | intent.putExtra(RKUpdateService.EXTRA_IMAGE_PATH, mImageFilePath);
89 | startActivity(intent);
90 | }
91 | finish();
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/InvalidFirmwareImageActivity.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import android.content.Context;
4 | import android.content.BroadcastReceiver;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.os.Bundle;
9 | import android.util.Log;
10 | import com.android.internal.app.AlertActivity;
11 | import com.android.internal.app.AlertController;
12 | import java.io.File;
13 | import java.util.Formatter;
14 | import java.util.Locale;
15 |
16 | public class InvalidFirmwareImageActivity extends AlertActivity implements DialogInterface.OnClickListener {
17 | private static final String TAG = "InvalidFirmwareImageActivity";
18 | private static void LOG(String msg) { Log.d(TAG, msg); }
19 |
20 | private String SDCARD_ROOT = "/sdcard";;
21 | private static StringBuilder sFormatBuilder = new StringBuilder();;
22 | private static Formatter sFormatter = new Formatter(sFormatBuilder, Locale.getDefault());
23 | private String mImageFilePath;
24 |
25 | private BroadcastReceiver mReceiver = new BroadcastReceiver() {
26 | public void onReceive(Context context, Intent intent) {
27 | LOG("onReceive() : 'action' = " + intent.getAction());
28 | if (intent.getAction() == Intent.ACTION_MEDIA_UNMOUNTED) {
29 | String path = intent.getData().getPath();
30 | LOG("mReceiver.onReceive() : original mount point : " + path + "; image file path : " + mImageFilePath);
31 | if (mImageFilePath != null && mImageFilePath.contains(path)) {
32 | LOG("mReceiver.onReceive() : Media that image file lives in is unmounted, to finish this activity.");
33 | finish();
34 | }
35 | }
36 | }
37 | };
38 |
39 | protected void onCreate(Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | LOG("onCreate() : Entered.");
42 | Bundle extr = getIntent().getExtras();
43 | mImageFilePath = extr.getString(RKUpdateService.EXTRA_IMAGE_PATH);
44 | AlertController.AlertParams p = mAlertParams;
45 | p.mIconId = R.drawable.ic_dialog_alert;
46 | p.mTitle = getString(R.string.IFIA_title);
47 | String messageFormat = getString(R.string.IFIA_msg);
48 | sFormatBuilder.setLength(0);
49 | sFormatter.format(messageFormat, mImageFilePath);
50 | p.mMessage = sFormatBuilder.toString();
51 | p.mPositiveButtonText = getString(R.string.IFIA_btn_yes);
52 | p.mPositiveButtonListener = this;
53 | p.mNegativeButtonText = getString(R.string.IFIA_btn_no);
54 | p.mNegativeButtonListener = this;
55 | setupAlert();
56 | IntentFilter filter = new android.content.IntentFilter();
57 | filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
58 | filter.addDataScheme("file");
59 | registerReceiver(mReceiver, filter);
60 | }
61 |
62 | protected void onResume() {
63 | super.onResume();
64 | }
65 |
66 | protected void onDestroy() {
67 | super.onDestroy();
68 | LOG("onDestroy() : Entered.");
69 | mImageFilePath = null;
70 | unregisterReceiver(mReceiver);
71 | }
72 |
73 | public void onClick(DialogInterface dialog, int which) {
74 | if (which == DialogInterface.BUTTON_POSITIVE) {
75 | LOG("onClick() : User desided to delete the invalid image file.");
76 | if (new File(mImageFilePath).delete() == false) {
77 | Log.w(TAG, "onClick() : Failed to delete invalid image file : " + mImageFilePath);
78 | }
79 | }
80 | finish();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/NoImageActivity.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import com.android.internal.app.AlertActivity;
4 | import com.android.internal.app.AlertController;
5 | import android.content.DialogInterface;
6 | import android.os.Bundle;
7 |
8 | public class NoImageActivity extends AlertActivity implements DialogInterface.OnClickListener {
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | AlertController.AlertParams p = mAlertParams;
12 | p.mTitle = getString(R.string.NIA_title);
13 | p.mIconId = R.drawable.ic_dialog_alert;
14 | p.mMessage = String.format(getString(R.string.NIA_msg_format), "/flash", "/sdcard");
15 | p.mPositiveButtonText = getString(R.string.NIA_btn_ok);
16 | p.mPositiveButtonListener = this;
17 | p.mNegativeButtonText = null;
18 | p.mNegativeButtonListener = null;
19 | setupAlert();
20 | }
21 |
22 | public void onClick(DialogInterface dialog, int which) {
23 | finish();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/NotifyDeleteActivity.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import android.app.Activity;
4 | import android.content.ComponentName;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.ServiceConnection;
8 | import android.os.Bundle;
9 | import android.os.IBinder;
10 | import android.util.Log;
11 | import android.view.KeyEvent;
12 | import android.view.View;
13 | import android.view.Window;
14 | import android.view.WindowManager.LayoutParams;
15 | import android.widget.Button;
16 | import android.widget.TextView;
17 |
18 | public class NotifyDeleteActivity extends Activity {
19 | private static String TAG = "NotifyDeleteActivity";
20 | private boolean mIfDelete = true;
21 | private String mPath;
22 | private Context mContext;
23 | private RKUpdateService.LocalBinder mBinder;
24 |
25 | private ServiceConnection mConnection = new ServiceConnection() {
26 | public void onServiceConnected(ComponentName className, IBinder service) {
27 | mBinder = (RKUpdateService.LocalBinder) service;
28 | if (mIfDelete) mBinder.deletePackage(mPath);
29 | mBinder.unLockWorkHandler();
30 | finish();
31 | }
32 | public void onServiceDisconnected(ComponentName className) {
33 | mBinder = null;
34 | }
35 | };
36 |
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | mContext = this;
40 | requestWindowFeature(Window.FEATURE_LEFT_ICON);
41 | setContentView(R.layout.notify_dialog);
42 | getWindow().addFlags(LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG);
43 | getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
44 | android.R.drawable.ic_dialog_alert);
45 | Intent startIntent = getIntent();
46 | TextView text = (TextView) findViewById(R.id.notify);
47 | int flag = startIntent.getIntExtra("flag", 0);
48 | mPath = startIntent.getStringExtra("path");
49 | if (flag == RKUpdateService.RESULT_SUCCESS) {
50 | text.setText(getString(R.string.update_success) +
51 | getString(R.string.ask_delete_package));
52 | } else if (flag == RKUpdateService.RESULT_FAILED) {
53 | text.setText(getString(R.string.update_failed) +
54 | getString(R.string.ask_delete_package));
55 | }
56 | Button btn_ok = (Button) findViewById(R.id.button_ok);
57 | Button btn_cancel = (Button) findViewById(R.id.button_cancel);
58 | btn_ok.setOnClickListener(new View.OnClickListener() {
59 | public void onClick(View v) {
60 | mIfDelete = true;
61 | mContext.bindService(new Intent(mContext, RKUpdateService.class),
62 | mConnection, Context.BIND_AUTO_CREATE);
63 | }
64 | });
65 | btn_cancel.setOnClickListener(new View.OnClickListener() {
66 | public void onClick(View v) {
67 | mIfDelete = false;
68 | mContext.bindService(new Intent(mContext, RKUpdateService.class),
69 | mConnection, Context.BIND_AUTO_CREATE);
70 | }
71 | });
72 | }
73 |
74 | protected void onDestroy() {
75 | android.util.Log.d(TAG, "onDestory.........");
76 | mContext.unbindService(mConnection);
77 | super.onDestroy();
78 | }
79 |
80 | public boolean onKeyDown(int keyCode, KeyEvent event) {
81 | Log.d(TAG, "onKeyDown...........");
82 | return super.onKeyDown(keyCode, event);
83 | }
84 |
85 | protected void onStop() {
86 | mContext.bindService(new Intent(mContext, RKUpdateService.class),
87 | mConnection, Context.BIND_AUTO_CREATE);
88 | super.onStop();
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/OtaUpdateNotifyActivity.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.view.Window;
9 | import android.view.WindowManager.LayoutParams;
10 | import android.widget.Button;
11 | import android.widget.TextView;
12 |
13 | public class OtaUpdateNotifyActivity extends Activity {
14 | private String TAG = "OtaUpdateNotifyActivity";
15 | private String mRemoteURI = null;
16 | private String mOtaPackageVersion = null;
17 | private String mSystemVersion = null;
18 | private String mOtaPackageName = null;
19 | private String mOtaPackageLength = null;
20 | private Context mContext;
21 |
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | mContext = this;
25 | requestWindowFeature(Window.FEATURE_LEFT_ICON);
26 | setContentView(R.layout.notify_dialog);
27 | getWindow().addFlags(LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG);
28 | getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
29 | android.R.drawable.ic_dialog_alert);
30 | Intent startIntent = getIntent();
31 | mRemoteURI = startIntent.getStringExtra("uri");
32 | mOtaPackageVersion = startIntent.getStringExtra("OtaPackageVersion");
33 | mSystemVersion = startIntent.getStringExtra("SystemVersion");
34 | mOtaPackageName = startIntent.getStringExtra("OtaPackageName");
35 | mOtaPackageLength = startIntent.getStringExtra("OtaPackageLength");
36 | long packageSize = Long.valueOf(mOtaPackageLength).longValue();
37 | String packageSize_string = null;
38 | if (packageSize < 1024) {
39 | packageSize_string = String.valueOf(packageSize) + "B";
40 | } else if (packageSize / 1024 > 0 && packageSize / 1024 / 1024 == 0) {
41 | packageSize_string = String.valueOf(packageSize / 1024) + "K";
42 | } else if (packageSize / 1024 / 1024 > 0) {
43 | packageSize_string = String.valueOf(packageSize / 1024 / 1024) + "M";
44 | }
45 | TextView txt = (TextView) findViewById(R.id.notify);
46 | txt.setText(
47 | getString(R.string.ota_update) + mOtaPackageVersion +
48 | getString(R.string.ota_package_size) + packageSize_string);
49 | Button btn_ok = (Button) findViewById(R.id.button_ok);
50 | Button btn_cancel = (Button) findViewById(R.id.button_cancel);
51 | btn_ok.setOnClickListener(new android.view.View.OnClickListener() {
52 | public void onClick(View v) {
53 | Intent intent = new Intent(mContext, PackageDownloadActivity.class);
54 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
55 | intent.putExtra("uri", mRemoteURI);
56 | intent.putExtra("OtaPackageLength", mOtaPackageLength);
57 | intent.putExtra("OtaPackageName", mOtaPackageName);
58 | intent.putExtra("OtaPackageVersion", mOtaPackageVersion);
59 | intent.putExtra("SystemVersion", mSystemVersion);
60 | mContext.startActivity(intent);
61 | finish();
62 | }
63 | });
64 | btn_cancel.setOnClickListener(new android.view.View.OnClickListener() {
65 | public void onClick(View v) {
66 | finish();
67 | }
68 | });
69 | }
70 |
71 | protected void onStop() {
72 | finish();
73 | super.onStop();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/PackageDownloadActivity.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import android.app.Activity;
4 | import android.app.Notification;
5 | import android.app.NotificationManager;
6 | import android.app.PendingIntent;
7 | import android.content.ActivityNotFoundException;
8 | import android.content.ComponentName;
9 | import android.content.Context;
10 | import android.content.Intent;
11 | import android.content.ServiceConnection;
12 | import android.content.pm.ActivityInfo;
13 | import android.content.pm.PackageManager;
14 | import android.content.pm.ResolveInfo;
15 | import android.os.Bundle;
16 | import android.os.Handler;
17 | import android.os.IBinder;
18 | import android.os.Message;
19 | import android.os.PowerManager;
20 | import android.util.Log;
21 | import android.view.KeyEvent;
22 | import android.view.View;
23 | import android.widget.Button;
24 | import android.widget.ProgressBar;
25 | import android.widget.RemoteViews;
26 | import org.apache.http.client.HttpClient;
27 | import java.net.URI;
28 | import java.net.URISyntaxException;
29 |
30 | public class PackageDownloadActivity extends Activity {
31 | private String TAG = "PackageDownloadActivity";
32 | private String WAKELOCK_KEY = "myDownload";
33 |
34 | private final static int STATE_READY = 0;
35 | private final static int STATE_DOWNLOADING = 2;
36 | private final static int STATE_ERROR = 4;
37 | private int mState = STATE_READY;
38 | private int notification_id = 20110921;
39 |
40 | private static PowerManager.WakeLock mWakeLock;
41 | private Context mContext;
42 | private RKUpdateService.LocalBinder mBinder;
43 | private ResolveInfo homeInfo;
44 | private String mFileName;
45 | private HttpClient mHttpClient;
46 | private Notification mNotify;
47 | private NotificationManager mNotifyManager;
48 | private FileDownloadTask mTask;
49 | private URI mUri;
50 | private ProgressBar mProgressBar;
51 | private Button mBtnCancel;
52 | private Button mBtnControl;
53 |
54 | private ServiceConnection mConnection = new ServiceConnection() {
55 | public void onServiceConnected(ComponentName className, IBinder service) {
56 | mBinder = (RKUpdateService.LocalBinder) service;
57 | mBinder.LockWorkHandler();
58 | }
59 | public void onServiceDisconnected(ComponentName className) {
60 | mBinder = null;
61 | }
62 | };
63 |
64 | public void onCreate(Bundle savedInstanceState) {
65 | super.onCreate(savedInstanceState);
66 | setContentView(R.layout.package_download);
67 | mContext = this;
68 | mContext.bindService(new Intent(mContext, RKUpdateService.class),
69 | mConnection, Context.BIND_AUTO_CREATE);
70 | Intent startIntent = getIntent();
71 | mUri = null;
72 | try {
73 | mUri = new URI(startIntent.getStringExtra("uri"));
74 | } catch (URISyntaxException e) {
75 | e.printStackTrace();
76 | }
77 | mFileName = startIntent.getStringExtra("OtaPackageName");
78 | homeInfo = getPackageManager().resolveActivity(
79 | (new Intent(Intent.ACTION_MAIN))
80 | .addCategory(Intent.CATEGORY_HOME), 0);
81 | mNotifyManager = (NotificationManager)
82 | getSystemService(Context.NOTIFICATION_SERVICE);
83 | mNotify = new Notification(R.drawable.ota_update,
84 | getString(R.string.app_name), System.currentTimeMillis());
85 | mNotify.contentView = new RemoteViews(getPackageName(),
86 | R.layout.download_notify);
87 | mNotify.contentView.setProgressBar(R.id.pb_download, 100, 0, false);
88 | Intent intent = new Intent(this, PackageDownloadActivity.class);
89 | PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0);
90 | mNotify.contentIntent = pending;
91 | PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
92 | mWakeLock = pm.newWakeLock(1, WAKELOCK_KEY);
93 | mProgressBar = (ProgressBar) findViewById(R.id.progress_horizontal);
94 | mBtnControl = (Button) findViewById(R.id.btn_control);
95 | mBtnCancel = (Button) findViewById(R.id.button_cancel);
96 | mBtnControl.setOnClickListener(new View.OnClickListener() {
97 | public void onClick(View v) {
98 | if (mState == STATE_READY || mState == STATE_ERROR) {
99 | mTask = new FileDownloadTask(mHttpClient, mUri, "/flash", mFileName, 3);
100 | mTask.setProgressHandler(mProgressHandler);
101 | mTask.start();
102 | mBtnControl.setText(getString(R.string.starting));
103 | mBtnControl.setClickable(false);
104 | mBtnControl.setFocusable(false);
105 | } else if (mState == STATE_DOWNLOADING) {
106 | mTask.stopDownload();
107 | mBtnControl.setText(getString(R.string.stoping));
108 | mBtnControl.setClickable(false);
109 | mBtnControl.setFocusable(false);
110 | }
111 | }
112 | });
113 | mBtnCancel.setOnClickListener(new View.OnClickListener() {
114 | public void onClick(View v) {
115 | finish();
116 | }
117 | });
118 | mProgressBar.setIndeterminate(false);
119 | mProgressBar.setProgress(0);
120 | mProgressHandler = new ProgressHandler();
121 | mHttpClient = CustomerHttpClient.getHttpClient();
122 | mTask = new FileDownloadTask(mHttpClient, mUri, "/flash", mFileName, 3);
123 | mTask.setProgressHandler(mProgressHandler);
124 | mTask.start();
125 | mBtnControl.setText(getString(R.string.starting));
126 | mBtnControl.setClickable(false);
127 | mBtnControl.setFocusable(false);
128 | }
129 |
130 | private ProgressHandler mProgressHandler;
131 | private class ProgressHandler extends Handler {
132 | public void handleMessage(Message msg) {
133 | switch (msg.what) {
134 | case FileDownloadTask.PROGRESS_UPDATE:
135 | int percent = msg.getData().getInt("percent", 0);
136 | Log.d(TAG, "percent = " + percent);
137 | mProgressBar.setProgress(percent);
138 | setNotificationProgress(percent);
139 | showNotification();
140 | break;
141 | case FileDownloadTask.PROGRESS_DOWNLOAD_COMPLETE:
142 | mState = STATE_READY;
143 | mBtnControl.setText(getString(R.string.start));
144 | mBtnControl.setClickable(true);
145 | mBtnControl.setFocusable(true);
146 | Intent intent = new Intent();
147 | intent.setClass(mContext, UpdateAndRebootActivity.class);
148 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
149 | intent.putExtra(RKUpdateService.EXTRA_IMAGE_PATH, "/flash/" + mFileName);
150 | startActivity(intent);
151 | finish();
152 | break;
153 | case FileDownloadTask.PROGRESS_START_COMPLETE:
154 | mState = STATE_DOWNLOADING;
155 | mBtnControl.setText(getString(R.string.pause));
156 | mBtnControl.setClickable(true);
157 | mBtnControl.setFocusable(true);
158 | showNotification();
159 | mWakeLock.acquire();
160 | break;
161 | case FileDownloadTask.PROGRESS_STOP_COMPLETE:
162 | int err = msg.getData().getInt("err", FileDownloadTask.ERR_NOERR);
163 | if (err == FileDownloadTask.ERR_CONNECT_TIMEOUT) {
164 | } else if (err == FileDownloadTask.ERR_FILELENGTH_NOMATCH) {
165 | } else if (err == FileDownloadTask.ERR_NOT_EXISTS) {
166 | } else if (err == FileDownloadTask.ERR_REQUEST_STOP) {
167 | }
168 | mState = STATE_ERROR;
169 | mBtnControl.setText(getString(R.string.retry));
170 | mBtnControl.setClickable(true);
171 | mBtnControl.setFocusable(true);
172 | if (mWakeLock.isHeld()) {
173 | mWakeLock.release();
174 | }
175 | break;
176 | }
177 | }
178 | }
179 |
180 | private void showNotification() {
181 | mNotifyManager.notify(notification_id, mNotify);
182 | }
183 |
184 | private void clearNotification() {
185 | mNotifyManager.cancel(notification_id);
186 | }
187 |
188 | private void setNotificationProgress(int percent) {
189 | mNotify.contentView.setProgressBar(R.id.pb_download, 100, percent, false);
190 | }
191 |
192 | protected void onDestroy() {
193 | Log.d(TAG, "ondestroy");
194 | if (mTask != null) {
195 | mTask.stopDownload();
196 | }
197 | if (mWakeLock.isHeld()) {
198 | mWakeLock.release();
199 | }
200 | clearNotification();
201 | if (mBinder != null) {
202 | mBinder.unLockWorkHandler();
203 | }
204 | mContext.unbindService(mConnection);
205 | super.onDestroy();
206 | }
207 |
208 | protected void onPause() {
209 | Log.d(TAG, "onPause");
210 | super.onPause();
211 | }
212 |
213 | protected void onRestart() {
214 | Log.d(TAG, "onRestart");
215 | super.onRestart();
216 | }
217 |
218 | protected void onStart() {
219 | Log.d(TAG, "onStart");
220 | super.onStart();
221 | }
222 |
223 | protected void onStop() {
224 | Log.d(TAG, "onStop");
225 | super.onStop();
226 | }
227 |
228 | public boolean onKeyDown(int keyCode, KeyEvent event) {
229 | if (keyCode == KeyEvent.KEYCODE_BACK) {
230 | ActivityInfo ai = homeInfo.activityInfo;
231 | Intent startIntent = new Intent(Intent.ACTION_MAIN);
232 | startIntent.addCategory(Intent.CATEGORY_LAUNCHER);
233 | startIntent.setComponent(new ComponentName(ai.packageName, ai.name));
234 | startActivitySafely(startIntent);
235 | return true;
236 | }
237 | return super.onKeyDown(keyCode, event);
238 | }
239 |
240 | void startActivitySafely(android.content.Intent intent) {
241 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
242 | try {
243 | startActivity(intent);
244 | } catch (ActivityNotFoundException e) {
245 | } catch (SecurityException e) {
246 | }
247 | }
248 | }
249 |
250 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/RKUpdateReceiver.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.net.ConnectivityManager;
7 | import android.net.NetworkInfo;
8 | import android.util.Log;
9 |
10 | public class RKUpdateReceiver extends BroadcastReceiver {
11 | private static final String TAG = "RKUpdateReceiver";
12 |
13 | public void onReceive(Context context, Intent intent) {
14 | String action = intent.getAction();
15 | Log.d(TAG, "action = " + action);
16 | if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
17 | Log.d(TAG, "RKUpdateReceiver recv ACTION_BOOT_COMPLETED.");
18 | Intent serviceIntent = new Intent(RKUpdateService.SERVICE_NAME);
19 | serviceIntent.putExtra("command", RKUpdateService.COMMAND_CHECK_LOCAL_UPDATING);
20 | serviceIntent.putExtra("delay", 1000);
21 | context.startService(serviceIntent);
22 | } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
23 | String[] path = new String[1];
24 | path[0] = intent.getData().getPath();
25 | Intent serviceIntent = new Intent(RKUpdateService.SERVICE_NAME);
26 | serviceIntent.putExtra("command", RKUpdateService.COMMAND_CHECK_LOCAL_UPDATING);
27 | serviceIntent.putExtra("delay", 1000);
28 | context.startService(serviceIntent);
29 | Log.d(TAG, "media is mounted to '" + path[0] + "'. To check local update.");
30 | } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
31 | ConnectivityManager cmanger = (ConnectivityManager)
32 | context.getSystemService("connectivity");
33 | NetworkInfo netInfo = cmanger.getActiveNetworkInfo();
34 | if (netInfo == null || netInfo.getType() != ConnectivityManager.TYPE_WIFI
35 | || !netInfo.isConnected()) {
36 | return;
37 | }
38 | Intent serviceIntent = new Intent(RKUpdateService.SERVICE_NAME);
39 | serviceIntent.putExtra("command", RKUpdateService.COMMAND_CHECK_WIFI_UPDATING);
40 | serviceIntent.putExtra("delay", 3000);
41 | context.startService(serviceIntent);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/RKUpdateService.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import android.app.Service;
4 | import android.content.ComponentName;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.SharedPreferences;
8 | import android.os.Binder;
9 | import android.os.Environment;
10 | import android.os.Handler;
11 | import android.os.HandlerThread;
12 | import android.os.IBinder;
13 | import android.os.Message;
14 | import android.os.SystemProperties;
15 | import android.util.Log;
16 | import org.apache.http.client.ClientProtocolException;
17 | import org.apache.http.client.HttpClient;
18 | import org.apache.http.client.methods.HttpHead;
19 | import org.apache.http.Header;
20 | import org.apache.http.HttpResponse;
21 | import java.io.File;
22 | import java.io.FileInputStream;
23 | import java.io.InputStream;
24 | import java.io.IOException;
25 | import java.net.URI;
26 | import java.net.URISyntaxException;
27 | import java.security.GeneralSecurityException;
28 | import java.util.Properties;
29 |
30 | public class RKUpdateService extends Service {
31 | private static final String TAG = "RKUpdateService";
32 | private static void LOG(String msg) { Log.d(TAG, msg); }
33 |
34 | static final String SERVICE_NAME = "android.rockchip.update.service";
35 |
36 | public final static int COMMAND_CHECK_LOCAL_UPDATING = 1;
37 | public final static int COMMAND_CHECK_WIFI_UPDATING = 2;
38 | public final static int COMMAND_CHECK_NOW = 3;
39 |
40 | public final static int COMMAND_INSTALL_RKIMAGE = 1;
41 | public final static int COMMAND_INSTALL_PACKAGE = 2;
42 |
43 | public final static int RESULT_SUCCESS = 1;
44 | public final static int RESULT_FAILED = 2;
45 |
46 | public final static String EXTRA_IMAGE_PATH =
47 | "android.rockchip.update.extra.IMAGE_PATH";
48 | public final static String EXTRA_IMAGE_VERSION =
49 | "android.rockchip.update.extra.IMAGE_VERSION";
50 | public final static String EXTRA_CURRENT_VERSION =
51 | "android.rockchip.update.extra.CURRENT_VERSION";
52 |
53 | static {
54 | System.loadLibrary("rockchip_update_jni");
55 | }
56 |
57 | private static volatile boolean mWorkHandleLocked = false;
58 | final public static String CACHE_ROOT =
59 | Environment.getDownloadCacheDirectory().getAbsolutePath();
60 | final private static String[] IMAGE_FILE_DIRS =
61 | { "/flash/", "/sdcard/" };
62 |
63 | public static URI mRemoteURI = null;
64 | private boolean mIsFirstStartUp = true;
65 | private String mTargetURI = null;
66 | private String mOtaPackageVersion = null;
67 | private String mSystemVersion = null;
68 | private String mOtaPackageName = null;
69 | private String mOtaPackageLength = null;
70 |
71 | private SharedPreferences mAutoCheckSet;
72 | private Context mContext;
73 |
74 | public IBinder onBind(Intent arg0) {
75 | return mBinder;
76 | }
77 |
78 | public void onCreate() {
79 | super.onCreate();
80 | mContext = this;
81 | Log.d(TAG, "starting RKUpdateService, version is 1.3.1");
82 | try {
83 | mRemoteURI = new URI(getRemoteUri());
84 | LOG("remote uri is " + mRemoteURI.toString());
85 | } catch (URISyntaxException e) {
86 | e.printStackTrace();
87 | }
88 | mAutoCheckSet = getSharedPreferences("auto_check", 0);
89 | HandlerThread thread = new HandlerThread("UpdateService : work thread");
90 | thread.start();
91 | mWorkHandler = new WorkHandler(thread.getLooper());
92 | if (mIsFirstStartUp) {
93 | Log.d(TAG, "first startup!!!");
94 | mIsFirstStartUp = false;
95 | String command = RecoverySystem.readFlagCommand();
96 | if (command != null) {
97 | LOG("command = " + command);
98 | if (command.contains("$path")) {
99 | String path = command.substring(command.indexOf(61) + 1);
100 | LOG("last_flag: path = " + path);
101 | if (command.startsWith("success")) {
102 | Intent intent = new Intent(mContext, NotifyDeleteActivity.class);
103 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
104 | intent.putExtra("flag", RESULT_SUCCESS);
105 | intent.putExtra("path", path);
106 | startActivity(intent);
107 | mWorkHandleLocked = true;
108 | } else if (command.startsWith("updating")) {
109 | Intent intent = new Intent(mContext, NotifyDeleteActivity.class);
110 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
111 | intent.putExtra("flag", RESULT_FAILED);
112 | intent.putExtra("path", path);
113 | startActivity(intent);
114 | mWorkHandleLocked = true;
115 | }
116 | }
117 | }
118 | }
119 | }
120 |
121 | public void onDestroy() {
122 | LOG("onDestroy.......");
123 | super.onDestroy();
124 | }
125 |
126 | public void onStart(Intent intent, int startId) {
127 | LOG("onStart.......");
128 | super.onStart(intent, startId);
129 | }
130 |
131 | public int onStartCommand(Intent intent, int flags, int startId) {
132 | LOG("onStartCommand.......");
133 | if (intent == null) {
134 | return START_NOT_STICKY;
135 | }
136 | int command = intent.getIntExtra("command", 0);
137 | int delayTime = intent.getIntExtra("delay", 1000);
138 | LOG("command = " + command + " delaytime = " + delayTime);
139 | if (command == 0) {
140 | return START_NOT_STICKY;
141 | }
142 | if (command == COMMAND_CHECK_WIFI_UPDATING &&
143 | !mAutoCheckSet.getBoolean("auto_check", true)) {
144 | LOG("user set not auto check!");
145 | return START_NOT_STICKY;
146 | }
147 | if (command == COMMAND_CHECK_NOW) {
148 | command = COMMAND_CHECK_WIFI_UPDATING;
149 | }
150 | Message msg = new Message();
151 | msg.what = command;
152 | msg.arg1 = 0;
153 | mWorkHandler.sendMessageDelayed(msg, (long)delayTime);
154 | return START_REDELIVER_INTENT;
155 | }
156 |
157 | private final LocalBinder mBinder = new LocalBinder();
158 | public class LocalBinder extends Binder {
159 | public void updateFirmware(String imagePath, int mode) {
160 | LOG("updateFirmware(): imagePath = " + imagePath);
161 | try {
162 | mWorkHandleLocked = true;
163 | if (mode == COMMAND_INSTALL_PACKAGE) {
164 | RecoverySystem.installPackage(mContext, new File(imagePath));
165 | } else if (mode == COMMAND_INSTALL_RKIMAGE) {
166 | RecoverySystem.installRKimage(mContext, imagePath);
167 | }
168 | } catch (IOException e) {
169 | Log.e(TAG, "updateFirmware() : Reboot for updateFirmware() failed", e);
170 | }
171 | }
172 | public boolean doesOtaPackageMatchProduct(String imagePath) {
173 | LOG("doesImageMatchProduct(): start verify package , imagePath = " + imagePath);
174 | try {
175 | RecoverySystem.verifyPackage(new File(imagePath), null, null);
176 | } catch (GeneralSecurityException e) {
177 | LOG("doesImageMatchProduct(): verifaPackage faild!");
178 | return false;
179 | } catch (IOException exc) {
180 | LOG("doesImageMatchProduct(): verifaPackage faild!");
181 | return false;
182 | }
183 | return true;
184 | }
185 | public void deletePackage(String path) {
186 | LOG("try to deletePackage...");
187 | File f = new File(path);
188 | if (f.exists()) {
189 | f.delete();
190 | LOG("delete complete! path=" + path);
191 | } else {
192 | LOG("path=" + path + " ,file not exists!");
193 | }
194 | }
195 | public void unLockWorkHandler() {
196 | LOG("unLockWorkHandler...");
197 | mWorkHandleLocked = false;
198 | }
199 | public void LockWorkHandler() {
200 | mWorkHandleLocked = true;
201 | LOG("LockWorkHandler...!");
202 | }
203 | }
204 |
205 | private WorkHandler mWorkHandler;
206 | private class WorkHandler extends Handler {
207 |
208 | public WorkHandler(android.os.Looper looper) {
209 | super(looper);
210 | }
211 |
212 | public void handleMessage(Message msg) {
213 | String currentFirmwareVersion = null;
214 |
215 | switch (msg.what) {
216 |
217 | case COMMAND_CHECK_LOCAL_UPDATING:
218 | LOG("WorkHandler::handleMessage() : To perform 'COMMAND_CHECK_LOCAL_UPDATING'.");
219 | if (mWorkHandleLocked) {
220 | LOG("WorkHandler::handleMessage() : locked !!!");
221 | break;
222 | }
223 | String[] validFirmwareImageFile = getValidFirmwareImageFile(IMAGE_FILE_DIRS);
224 | if (validFirmwareImageFile != null) {
225 | if (validFirmwareImageFile.length == 1) {
226 | String imageFile = validFirmwareImageFile[0];
227 | String imageFileVersion = null;
228 | if (imageFile.endsWith("img")) {
229 | if (checkRKimage(imageFile) == false) {
230 | LOG("WorkHandler::handleMessage() : not a valid rkimage !!");
231 | break;
232 | }
233 | imageFileVersion = getImageVersion(imageFile);
234 | LOG("WorkHandler::handleMessage() : Find a VALID image file : '"
235 | + imageFile + "'. imageFileVersion is '" + imageFileVersion);
236 | currentFirmwareVersion = getCurrentFirmwareVersion();
237 | LOG("WorkHandler::handleMessage() : Current system firmware version : '"
238 | + currentFirmwareVersion + "'");
239 | String tmp = currentFirmwareVersion;
240 | currentFirmwareVersion = imageFileVersion;
241 | imageFileVersion = tmp;
242 | }
243 | startProposingActivity(imageFile, currentFirmwareVersion, imageFileVersion);
244 | } else {
245 | LOG("WorkHandler::handleMessage() : Find a INVALID image file : '"
246 | + validFirmwareImageFile[0] + "'. To notify user.");
247 | notifyInvalidImage(validFirmwareImageFile[0]);
248 | }
249 | }
250 | break;
251 |
252 | case COMMAND_CHECK_WIFI_UPDATING:
253 | if (mWorkHandleLocked) {
254 | LOG("WorkHandler::handleMessage() : locked !!!");
255 | break;
256 | }
257 | for (int retry = 0; retry < 1; retry++) {
258 | try {
259 | if (requestRemoteServerForUpdate() == true) {
260 | LOG("find a remote update package, now start PackageDownloadActivity...");
261 | Intent intent = new Intent(mContext, OtaUpdateNotifyActivity.class);
262 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
263 | intent.putExtra("uri", mTargetURI);
264 | intent.putExtra("OtaPackageLength", mOtaPackageLength);
265 | intent.putExtra("OtaPackageName", mOtaPackageName);
266 | intent.putExtra("OtaPackageVersion", mOtaPackageVersion);
267 | intent.putExtra("SystemVersion", mSystemVersion);
268 | mContext.startActivity(intent);
269 | } else {
270 | LOG("no find remote update package...");
271 | }
272 | } catch (IOException e) {
273 | LOG("request remote server error...");
274 | try {
275 | Thread.sleep(5000);
276 | } catch (InterruptedException e2) {
277 | e2.printStackTrace();
278 | }
279 | }
280 | }
281 | break;
282 | }
283 | }
284 | }
285 |
286 | private String[] getValidFirmwareImageFile(String[] searchPaths) {
287 | for (String dir_path : searchPaths) {
288 | String filePath = dir_path + "update.zip";
289 | LOG("getValidFirmwareImageFile() : Target image file path : " + filePath);
290 | if (new File(filePath).exists()) {
291 | return new String[]{filePath};
292 | }
293 | }
294 | for (String dir_path : searchPaths) {
295 | String filePath = dir_path + "update.img";
296 | if (new File(filePath).exists()) {
297 | return new String[]{filePath};
298 | }
299 | }
300 | return null;
301 | }
302 |
303 | private void startProposingActivity(String path, String imageVersion, String currentVersion) {
304 | Intent intent = new Intent();
305 | intent.setComponent(new ComponentName(SERVICE_NAME,
306 | "android.rockchip.update.service.FirmwareUpdatingActivity"));
307 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
308 | intent.putExtra(EXTRA_IMAGE_PATH, path);
309 | intent.putExtra(EXTRA_IMAGE_VERSION, imageVersion);
310 | intent.putExtra(EXTRA_CURRENT_VERSION, currentVersion);
311 | mContext.startActivity(intent);
312 | }
313 |
314 | static native private String getImageProductName(String path);
315 | static native private String getImageVersion(String path);
316 |
317 | private boolean checkRKimage(String path) {
318 | String imageProductName = getImageProductName(path);
319 | LOG("checkRKimage() : imageProductName = " + imageProductName);
320 | if (imageProductName.equals(getProductName())) return true;
321 | return false;
322 | }
323 |
324 | private String getCurrentFirmwareVersion() {
325 | return SystemProperties.get("ro.firmware.version");
326 | }
327 |
328 | private static String getProductName() {
329 | return SystemProperties.get("ro.product.model");
330 | }
331 |
332 | private void notifyInvalidImage(String path) {
333 | Intent intent = new Intent();
334 | intent.setComponent(new ComponentName(SERVICE_NAME,
335 | "android.rockchip.update.service.InvalidFirmwareImageActivity"));
336 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
337 | intent.putExtra(EXTRA_IMAGE_PATH, path);
338 | mContext.startActivity(intent);
339 | }
340 |
341 | public static String getRemoteUri() {
342 | return "http://" + getRemoteHost() +
343 | "/OtaUpdater/android?product=" + getOtaProductName() +
344 | "&version=" + getSystemVersion();
345 | }
346 |
347 | public static String getRemoteHost() {
348 | String remoteHost = SystemProperties.get("ro.product.ota.host");
349 | if (remoteHost == null || remoteHost.length() == 0) {
350 | remoteHost = getRemoteHostFromFile();
351 | if (remoteHost == null) {
352 | remoteHost = "172.16.7.55:2300";
353 | }
354 | }
355 | return remoteHost;
356 | }
357 |
358 | public static String getRemoteHostFromFile() {
359 | String remoteHostPath = null;
360 | Properties props = new Properties();
361 | try {
362 | File f1 = new File(Environment.getExternalStorageDirectory() +
363 | "/OTA_UPDATE_SERVICE_PATH");
364 | File f2 = new File("/system/OTA_UPDATE_SERVICE_PATH");
365 | if (f1.exists()) {
366 | InputStream in = new FileInputStream(f1);
367 | props.load(in);
368 | remoteHostPath = props.get("remoteHost").toString();
369 | }
370 | if (remoteHostPath == null) {
371 | InputStream in = new FileInputStream(f2);
372 | props.load(in);
373 | remoteHostPath = props.get("remoteHost").toString();
374 | }
375 | } catch (Exception e) {
376 | e.printStackTrace();
377 | }
378 | return remoteHostPath;
379 | }
380 |
381 | public static String getOtaProductName() {
382 | String productName = SystemProperties.get("ro.product.model");
383 | if (productName.contains(" ")) {
384 | productName = productName.replaceAll(" ", "");
385 | }
386 | return productName;
387 | }
388 |
389 | private boolean requestRemoteServerForUpdate()
390 | throws IOException, ClientProtocolException {
391 | HttpClient httpClient = CustomerHttpClient.getHttpClient();
392 | HttpHead httpHead = new HttpHead(mRemoteURI);
393 | HttpResponse response = httpClient.execute(httpHead);
394 | int statusCode = response.getStatusLine().getStatusCode();
395 | if (statusCode != 200) {
396 | return false;
397 | }
398 | for (Header header : response.getAllHeaders()) {
399 | LOG(header.getName() + ":" + header.getValue());
400 | }
401 | Header[] headLength = response.getHeaders("OtaPackageLength");
402 | if (headLength.length > 0) {
403 | mOtaPackageLength = headLength[0].getValue();
404 | }
405 | Header[] headName = response.getHeaders("OtaPackageName");
406 | if (headName.length > 0) {
407 | mOtaPackageName = headName[0].getValue();
408 | }
409 | Header[] headVersion = response.getHeaders("OtaPackageVersion");
410 | if (headVersion.length > 0) {
411 | mOtaPackageVersion = headVersion[0].getValue();
412 | }
413 | Header[] headTargetURI = response.getHeaders("OtaPackageUri");
414 | if (headTargetURI.length > 0) {
415 | mTargetURI = headTargetURI[0].getValue();
416 | }
417 | mTargetURI = "http://" + getRemoteHost() +
418 | (mTargetURI.startsWith("/") ? mTargetURI : "/" + mTargetURI);
419 | mSystemVersion = getSystemVersion();
420 | LOG("OtaPackageName = " + mOtaPackageName +
421 | " OtaPackageVersion = " + mOtaPackageVersion +
422 | " OtaPackageLength = " + mOtaPackageLength +
423 | " SystemVersion = " + mSystemVersion +
424 | "OtaPackageUri = " + mTargetURI);
425 | return true;
426 | }
427 |
428 | public static String getSystemVersion() {
429 | String version = SystemProperties.get("ro.product.version");
430 | if (version == null || version.length() == 0) {
431 | version = "1.0.0";
432 | }
433 | return version;
434 | }
435 | }
436 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/RecoverySystem.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | /*
4 | * Copyright (C) 2010 The Android Open Source Project
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import java.io.ByteArrayInputStream;
20 | import java.io.File;
21 | import java.io.FileNotFoundException;
22 | import java.io.FileReader;
23 | import java.io.FileWriter;
24 | import java.io.IOException;
25 | import java.io.RandomAccessFile;
26 | import java.security.GeneralSecurityException;
27 | import java.security.PublicKey;
28 | import java.security.Signature;
29 | import java.security.SignatureException;
30 | import java.security.cert.Certificate;
31 | import java.security.cert.CertificateFactory;
32 | import java.security.cert.X509Certificate;
33 | import java.util.Collection;
34 | import java.util.Enumeration;
35 | import java.util.HashSet;
36 | import java.util.Iterator;
37 | import java.util.List;
38 | import java.util.zip.ZipEntry;
39 | import java.util.zip.ZipFile;
40 |
41 | import android.os.PowerManager;
42 | import android.content.Context;
43 | import android.util.Log;
44 |
45 | import org.apache.harmony.security.asn1.BerInputStream;
46 | import org.apache.harmony.security.pkcs7.ContentInfo;
47 | import org.apache.harmony.security.pkcs7.SignedData;
48 | import org.apache.harmony.security.pkcs7.SignerInfo;
49 | import org.apache.harmony.security.provider.cert.X509CertImpl;
50 |
51 | /**
52 | * RecoverySystem contains methods for interacting with the Android
53 | * recovery system (the separate partition that can be used to install
54 | * system updates, wipe user data, etc.)
55 | */
56 | public class RecoverySystem {
57 | private static final String TAG = "RecoverySystem";
58 |
59 | /**
60 | * Default location of zip file containing public keys (X509
61 | * certs) authorized to sign OTA updates.
62 | */
63 | private static final File DEFAULT_KEYSTORE =
64 | new File("/system/etc/security/otacerts.zip");
65 |
66 | /** Send progress to listeners no more often than this (in ms). */
67 | private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500;
68 |
69 | /** Used to communicate with recovery. See bootable/recovery/recovery.c. */
70 | private static File RECOVERY_DIR = new File("/cache/recovery");
71 | private static File UPDATE_FLAG_FILE = new File(RECOVERY_DIR, "last_flag");
72 | private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
73 | private static File LOG_FILE = new File(RECOVERY_DIR, "log");
74 | private static String LAST_PREFIX = "last_";
75 |
76 | // Length limits for reading files.
77 | private static int LOG_FILE_MAX_LENGTH = 64 * 1024;
78 |
79 | /**
80 | * Interface definition for a callback to be invoked regularly as
81 | * verification proceeds.
82 | */
83 | public interface ProgressListener {
84 | /**
85 | * Called periodically as the verification progresses.
86 | *
87 | * @param progress the approximate percentage of the
88 | * verification that has been completed, ranging from 0
89 | * to 100 (inclusive).
90 | */
91 | public void onProgress(int progress);
92 | }
93 |
94 | /** @return the set of certs that can be used to sign an OTA package. */
95 | private static HashSet getTrustedCerts(File keystore)
96 | throws IOException, GeneralSecurityException {
97 | HashSet trusted = new HashSet();
98 | if (keystore == null) {
99 | keystore = DEFAULT_KEYSTORE;
100 | }
101 | ZipFile zip = new ZipFile(keystore);
102 | try {
103 | CertificateFactory cf = CertificateFactory.getInstance("X.509");
104 | Enumeration extends ZipEntry> entries = zip.entries();
105 | while (entries.hasMoreElements()) {
106 | ZipEntry entry = entries.nextElement();
107 | trusted.add(cf.generateCertificate(zip.getInputStream(entry)));
108 | }
109 | } finally {
110 | zip.close();
111 | }
112 | return trusted;
113 | }
114 |
115 | /**
116 | * Verify the cryptographic signature of a system update package
117 | * before installing it. Note that the package is also verified
118 | * separately by the installer once the device is rebooted into
119 | * the recovery system. This function will return only if the
120 | * package was successfully verified; otherwise it will throw an
121 | * exception.
122 | *
123 | * Verification of a package can take significant time, so this
124 | * function should not be called from a UI thread. Interrupting
125 | * the thread while this function is in progress will result in a
126 | * SecurityException being thrown (and the thread's interrupt flag
127 | * will be cleared).
128 | *
129 | * @param packageFile the package to be verified
130 | * @param listener an object to receive periodic progress
131 | * updates as verification proceeds. May be null.
132 | * @param deviceCertsZipFile the zip file of certificates whose
133 | * public keys we will accept. Verification succeeds if the
134 | * package is signed by the private key corresponding to any
135 | * public key in this file. May be null to use the system default
136 | * file (currently "/system/etc/security/otacerts.zip").
137 | *
138 | * @throws IOException if there were any errors reading the
139 | * package or certs files.
140 | * @throws GeneralSecurityException if verification failed
141 | */
142 | public static void verifyPackage(File packageFile,
143 | ProgressListener listener,
144 | File deviceCertsZipFile)
145 | throws IOException, GeneralSecurityException {
146 | long fileLen = packageFile.length();
147 |
148 | RandomAccessFile raf = new RandomAccessFile(packageFile, "r");
149 | try {
150 | int lastPercent = 0;
151 | long lastPublishTime = System.currentTimeMillis();
152 | if (listener != null) {
153 | listener.onProgress(lastPercent);
154 | }
155 |
156 | raf.seek(fileLen - 6);
157 | byte[] footer = new byte[6];
158 | raf.readFully(footer);
159 |
160 | if (footer[2] != (byte)0xff || footer[3] != (byte)0xff) {
161 | throw new SignatureException("no signature in file (no footer)");
162 | }
163 |
164 | int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8);
165 | int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8);
166 | Log.v(TAG, String.format("comment size %d; signature start %d",
167 | commentSize, signatureStart));
168 |
169 | byte[] eocd = new byte[commentSize + 22];
170 | raf.seek(fileLen - (commentSize + 22));
171 | raf.readFully(eocd);
172 |
173 | // Check that we have found the start of the
174 | // end-of-central-directory record.
175 | if (eocd[0] != (byte)0x50 || eocd[1] != (byte)0x4b ||
176 | eocd[2] != (byte)0x05 || eocd[3] != (byte)0x06) {
177 | throw new SignatureException("no signature in file (bad footer)");
178 | }
179 |
180 | for (int i = 4; i < eocd.length-3; ++i) {
181 | if (eocd[i ] == (byte)0x50 && eocd[i+1] == (byte)0x4b &&
182 | eocd[i+2] == (byte)0x05 && eocd[i+3] == (byte)0x06) {
183 | throw new SignatureException("EOCD marker found after start of EOCD");
184 | }
185 | }
186 |
187 | // The following code is largely copied from
188 | // JarUtils.verifySignature(). We could just *call* that
189 | // method here if that function didn't read the entire
190 | // input (ie, the whole OTA package) into memory just to
191 | // compute its message digest.
192 |
193 | BerInputStream bis = new BerInputStream(
194 | new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart));
195 | ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis);
196 | SignedData signedData = info.getSignedData();
197 | if (signedData == null) {
198 | throw new IOException("signedData is null");
199 | }
200 | Collection encCerts = signedData.getCertificates();
201 | if (encCerts.isEmpty()) {
202 | throw new IOException("encCerts is empty");
203 | }
204 | // Take the first certificate from the signature (packages
205 | // should contain only one).
206 | Iterator it = encCerts.iterator();
207 | X509Certificate cert = null;
208 | if (it.hasNext()) {
209 | cert = new X509CertImpl((org.apache.harmony.security.x509.Certificate)it.next());
210 | } else {
211 | throw new SignatureException("signature contains no certificates");
212 | }
213 |
214 | List sigInfos = signedData.getSignerInfos();
215 | SignerInfo sigInfo;
216 | if (!sigInfos.isEmpty()) {
217 | sigInfo = (SignerInfo)sigInfos.get(0);
218 | } else {
219 | throw new IOException("no signer infos!");
220 | }
221 |
222 | // Check that the public key of the certificate contained
223 | // in the package equals one of our trusted public keys.
224 |
225 | HashSet trusted = getTrustedCerts(
226 | deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile);
227 |
228 | PublicKey signatureKey = cert.getPublicKey();
229 | boolean verified = false;
230 | for (Certificate c : trusted) {
231 | if (c.getPublicKey().equals(signatureKey)) {
232 | verified = true;
233 | break;
234 | }
235 | }
236 | if (!verified) {
237 | throw new SignatureException("signature doesn't match any trusted key");
238 | }
239 |
240 | // The signature cert matches a trusted key. Now verify that
241 | // the digest in the cert matches the actual file data.
242 |
243 | // The verifier in recovery *only* handles SHA1withRSA
244 | // signatures. SignApk.java always uses SHA1withRSA, no
245 | // matter what the cert says to use. Ignore
246 | // cert.getSigAlgName(), and instead use whatever
247 | // algorithm is used by the signature (which should be
248 | // SHA1withRSA).
249 |
250 | String da = sigInfo.getDigestAlgorithm();
251 | String dea = sigInfo.getDigestEncryptionAlgorithm();
252 | String alg = null;
253 | if (da == null || dea == null) {
254 | // fall back to the cert algorithm if the sig one
255 | // doesn't look right.
256 | alg = cert.getSigAlgName();
257 | } else {
258 | alg = da + "with" + dea;
259 | }
260 | Signature sig = Signature.getInstance(alg);
261 | sig.initVerify(cert);
262 |
263 | // The signature covers all of the OTA package except the
264 | // archive comment and its 2-byte length.
265 | long toRead = fileLen - commentSize - 2;
266 | long soFar = 0;
267 | raf.seek(0);
268 | byte[] buffer = new byte[4096];
269 | boolean interrupted = false;
270 | while (soFar < toRead) {
271 | interrupted = Thread.interrupted();
272 | if (interrupted) break;
273 | int size = buffer.length;
274 | if (soFar + size > toRead) {
275 | size = (int)(toRead - soFar);
276 | }
277 | int read = raf.read(buffer, 0, size);
278 | sig.update(buffer, 0, read);
279 | soFar += read;
280 |
281 | if (listener != null) {
282 | long now = System.currentTimeMillis();
283 | int p = (int)(soFar * 100 / toRead);
284 | if (p > lastPercent &&
285 | now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {
286 | lastPercent = p;
287 | lastPublishTime = now;
288 | listener.onProgress(lastPercent);
289 | }
290 | }
291 | }
292 | if (listener != null) {
293 | listener.onProgress(100);
294 | }
295 |
296 | if (interrupted) {
297 | throw new SignatureException("verification was interrupted");
298 | }
299 |
300 | if (!sig.verify(sigInfo.getEncryptedDigest())) {
301 | throw new SignatureException("signature digest verification failed");
302 | }
303 | } finally {
304 | raf.close();
305 | }
306 | }
307 |
308 | /**
309 | * Reboots the device in order to install the given update
310 | * package.
311 | * Requires the {@link android.Manifest.permission#REBOOT} permission.
312 | *
313 | * @param context the Context to use
314 | * @param packageFile the update package to install. Currently
315 | * must be on the /cache or /data partitions.
316 | *
317 | * @throws IOException if writing the recovery command file
318 | * fails, or if the reboot itself fails.
319 | */
320 | public static void installPackage(Context context, File packageFile)
321 | throws IOException {
322 | String filename = packageFile.getPath();
323 |
324 | Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
325 | String arg = "--update_package=" + filename;
326 | writeFlagCommand(filename);
327 | bootCommand(context, arg);
328 | }
329 |
330 | public static void installRKimage(Context context, String imagePath)
331 | throws IOException {
332 |
333 | Log.w(TAG, "!!! REBOOTING TO INSTALL rkimage " + imagePath + " !!!");
334 | String arg = "--update_rkimage=" + imagePath;
335 | writeFlagCommand(imagePath);
336 | bootCommand(context, arg);
337 | }
338 |
339 | public static String readFlagCommand() {
340 | if (UPDATE_FLAG_FILE.exists()) {
341 | char[] buf = new char[128];
342 | int readCount = 0;
343 | try {
344 | FileReader reader = new FileReader(UPDATE_FLAG_FILE);
345 | readCount = reader.read(buf, 0, buf.length);
346 | Log.d(TAG, "readCount = " + readCount + " buf.length = " + buf.length);
347 | } catch (IOException e) {
348 | Log.e(TAG, "can not read /cache/recovery/flag!");
349 | } finally {
350 | UPDATE_FLAG_FILE.delete();
351 | }
352 | StringBuilder sBuilder = new StringBuilder();
353 | for (int i = 0; i < readCount; i++) {
354 | if (buf[i] == 0) break;
355 | sBuilder.append(buf[i]);
356 | }
357 | return sBuilder.toString();
358 | }
359 | return null;
360 | }
361 |
362 | public static void writeFlagCommand(String path)
363 | throws IOException {
364 |
365 | RECOVERY_DIR.mkdirs();
366 | UPDATE_FLAG_FILE.delete();
367 | FileWriter writer = new FileWriter(UPDATE_FLAG_FILE);
368 | try {
369 | writer.write("updating$path=" + path);
370 | } finally {
371 | writer.close();
372 | }
373 | }
374 |
375 | /**
376 | * Reboot into the recovery system with the supplied argument.
377 | * @param arg to pass to the recovery utility.
378 | * @throws IOException if something goes wrong.
379 | */
380 | private static void bootCommand(Context context, String arg) throws IOException {
381 | RECOVERY_DIR.mkdirs(); // In case we need it
382 | COMMAND_FILE.delete(); // In case it's not writable
383 | LOG_FILE.delete();
384 |
385 | FileWriter command = new FileWriter(COMMAND_FILE);
386 | try {
387 | command.write(arg);
388 | command.write("\n");
389 | } finally {
390 | command.close();
391 | }
392 |
393 | // Having written the command file, go ahead and reboot
394 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
395 | pm.reboot("recovery");
396 |
397 | throw new IOException("Reboot failed (no permissions?)");
398 | }
399 | }
400 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/Setting.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.SharedPreferences;
7 | import android.os.Bundle;
8 | import android.view.View;
9 | import android.widget.Button;
10 | import android.widget.CheckBox;
11 | import android.widget.CompoundButton;
12 |
13 | public class Setting extends Activity {
14 | private Context mContext;
15 | private SharedPreferences mAutoCheckSet;
16 | private CheckBox mSwh_AutoCheck;
17 | private Button mBtn_CheckNow;
18 |
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.setting);
22 | mContext = this;
23 | mSwh_AutoCheck = (CheckBox)findViewById(R.id.swh_auto_check);
24 | mBtn_CheckNow = (Button)findViewById(R.id.btn_check_now);
25 | mAutoCheckSet = getSharedPreferences("auto_check", 0);
26 | mSwh_AutoCheck.setChecked(mAutoCheckSet.getBoolean("auto_check", true));
27 | mSwh_AutoCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
28 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
29 | SharedPreferences.Editor e = mAutoCheckSet.edit();
30 | e.putBoolean("auto_check", isChecked);
31 | e.commit();
32 | }
33 | });
34 | mBtn_CheckNow.setOnClickListener(new View.OnClickListener() {
35 | public void onClick(View v) {
36 | Intent serviceIntent = new Intent(RKUpdateService.SERVICE_NAME);
37 | serviceIntent.putExtra("command", RKUpdateService.COMMAND_CHECK_NOW);
38 | mContext.startService(serviceIntent);
39 | }
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/service/UpdateAndRebootActivity.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.service;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.ComponentName;
5 | import android.content.Context;
6 | import android.content.DialogInterface;
7 | import android.content.Intent;
8 | import android.content.ServiceConnection;
9 | import android.os.Bundle;
10 | import android.os.Handler;
11 | import android.os.HandlerThread;
12 | import android.os.IBinder;
13 | import android.os.Looper;
14 | import android.os.Message;
15 | import android.os.Power;
16 | import android.util.Log;
17 | import com.android.internal.app.AlertActivity;
18 | import com.android.internal.app.AlertController;
19 |
20 | public class UpdateAndRebootActivity extends AlertActivity {
21 | private static final String TAG = "UpdateAndRebootActivity";
22 | private static void LOG(String msg) { Log.d(TAG, msg); }
23 |
24 | public final static int COMMAND_START_UPDATING = 1;
25 |
26 | private String mImageFilePath;
27 | private Context mContext;
28 | private UiHandler mUiHandler;
29 | private WorkHandler mWorkHandler;
30 | private RKUpdateService.LocalBinder mBinder;
31 |
32 | private ServiceConnection mConnection = new ServiceConnection() {
33 | public void onServiceConnected(ComponentName className, IBinder service) {
34 | mBinder = (RKUpdateService.LocalBinder) service;
35 | mWorkHandler.sendEmptyMessageDelayed(COMMAND_START_UPDATING, 3000);
36 | }
37 | public void onServiceDisconnected(ComponentName className) {
38 | mBinder = null;
39 | }
40 | };
41 |
42 | protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | mContext = this;
45 | Intent startIntent = getIntent();
46 | mImageFilePath = startIntent.getExtras().getString(RKUpdateService.EXTRA_IMAGE_PATH);
47 |
48 | AlertController.AlertParams params = mAlertParams;
49 | params.mTitle = getString(R.string.updating_title);
50 | params.mIconId = R.drawable.ic_dialog_alert;
51 | String message = getText(R.string.updating_prompt).toString();
52 | if (mImageFilePath.contains("/sdcard")) {
53 | message += getText(R.string.updating_prompt_sdcard).toString();
54 | }
55 | params.mMessage = message;
56 | params.mPositiveButtonText = null;
57 | params.mPositiveButtonListener = null;
58 | params.mNegativeButtonText = null;
59 | params.mNegativeButtonListener = null;
60 | setupAlert();
61 |
62 | LOG("onCreate() : start 'work thread'.");
63 | HandlerThread thread = new HandlerThread("UpdateAndRebootActivity : work thread");
64 | thread.start();
65 | mWorkHandler = new WorkHandler(thread.getLooper());
66 | mUiHandler = new UiHandler();
67 | mContext.bindService(new Intent(mContext, RKUpdateService.class),
68 | mConnection, Context.BIND_AUTO_CREATE);
69 | }
70 |
71 | protected void dialog() {
72 | AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
73 | builder.setMessage(R.string.update_error_summary);
74 | builder.setTitle(R.string.update_error);
75 | builder.setPositiveButton(R.string.NIA_btn_ok, new DialogInterface.OnClickListener() {
76 | public void onClick(DialogInterface dialog, int which) {
77 | dialog.dismiss();
78 | Power.releaseWakeLock("ota-lock");
79 | finish();
80 | }
81 | });
82 | builder.create().show();
83 | }
84 |
85 | protected void onPause() {
86 | super.onPause();
87 | Power.releaseWakeLock("ota-lock");
88 | LOG("onPause() : Entered.");
89 | }
90 |
91 | protected void onResume() {
92 | super.onResume();
93 | Power.acquireWakeLock(1, "ota-lock");
94 | }
95 |
96 | private class UiHandler extends Handler {
97 | public void handleMessage(Message msg) {
98 | switch (msg.what) {
99 | case COMMAND_START_UPDATING:
100 | dialog();
101 | break;
102 | }
103 | }
104 | }
105 |
106 | private class WorkHandler extends Handler {
107 | public WorkHandler(Looper looper) {
108 | super(looper);
109 | }
110 | public void handleMessage(Message msg) {
111 | switch (msg.what) {
112 | case COMMAND_START_UPDATING:
113 | LOG("WorkHandler::handleMessage() : To perform 'COMMAND_START_UPDATING'.");
114 | if (mBinder != null) {
115 | if (mImageFilePath.endsWith("img")) {
116 | mBinder.updateFirmware(mImageFilePath,
117 | RKUpdateService.COMMAND_INSTALL_RKIMAGE);
118 | } else if (!mBinder.doesOtaPackageMatchProduct(mImageFilePath)) {
119 | mUiHandler.sendEmptyMessage(COMMAND_START_UPDATING);
120 | } else {
121 | mBinder.updateFirmware(mImageFilePath,
122 | RKUpdateService.COMMAND_INSTALL_PACKAGE);
123 | }
124 | } else {
125 | Log.d(TAG, "service have not connected!");
126 | }
127 | break;
128 | }
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/android/rockchip/update/util/RegetInfoUtil.java:
--------------------------------------------------------------------------------
1 | package android.rockchip.update.util;
2 |
3 | import android.rockchip.update.service.*;
4 |
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.io.FileOutputStream;
8 | import java.io.IOException;
9 | import java.net.URI;
10 | import java.net.URISyntaxException;
11 | import org.xmlpull.v1.XmlPullParser;
12 | import org.xmlpull.v1.XmlPullParserException;
13 | import org.xmlpull.v1.XmlSerializer;
14 | import android.util.Log;
15 | import android.util.Xml;
16 |
17 | public class RegetInfoUtil {
18 | private static final String TAG = "RegetInfoUtil";
19 |
20 | public static void writeFileInfoXml(File target, FileInfo fileInfo)
21 | throws IllegalArgumentException, IllegalStateException, IOException {
22 |
23 | FileOutputStream fout;
24 | if(!target.exists()) {
25 | target.createNewFile();
26 | }
27 | fout = new FileOutputStream(target);
28 | XmlSerializer xmlSerializer = Xml.newSerializer();
29 | xmlSerializer.setOutput(fout, "UTF-8");
30 | xmlSerializer.startDocument("UTF-8", true);
31 |
32 | xmlSerializer.startTag(null, "FileInfo");
33 | xmlSerializer.attribute(null, "Name", fileInfo.getFileName());
34 | xmlSerializer.attribute(null, "Length", String.valueOf(fileInfo.getFileLength()));
35 | xmlSerializer.attribute(null, "ReceiveLength", String.valueOf(fileInfo.getReceivedLength()));
36 | xmlSerializer.attribute(null, "URI", fileInfo.getURI().toString());
37 |
38 |
39 | xmlSerializer.startTag(null, "Pieces");
40 | for(int id = 0; id < fileInfo.getPieceNum(); id++) {
41 | FileInfo.Piece p = fileInfo.getPieceById(id);
42 | xmlSerializer.startTag(null, "Piece");
43 | xmlSerializer.attribute(null, "Start", String.valueOf(p.getStart()));
44 | xmlSerializer.attribute(null, "End", String.valueOf(p.getEnd()));
45 | xmlSerializer.attribute(null, "PosNow", String.valueOf(p.getPosNow()));
46 | xmlSerializer.endTag(null, "Piece");
47 | }
48 | xmlSerializer.endTag(null, "Pieces");
49 | xmlSerializer.endTag(null, "FileInfo");
50 | xmlSerializer.endDocument();
51 | fout.flush();
52 | fout.close();
53 | Log.i(TAG, fout.toString());
54 | }
55 |
56 | public static FileInfo parseFileInfoXml(File target)
57 | throws XmlPullParserException, URISyntaxException, IOException {
58 | FileInfo fileInfo = new FileInfo();
59 | FileInputStream fin = new FileInputStream(target);
60 | XmlPullParser xmlPullParser = Xml.newPullParser();
61 | xmlPullParser.setInput(fin, "UTF-8");
62 | int eventType = xmlPullParser.getEventType();
63 |
64 | while (eventType != XmlPullParser.END_DOCUMENT) {
65 | String tag = xmlPullParser.getName();
66 | Log.i(TAG, tag + "====>");
67 | switch (eventType) {
68 | case XmlPullParser.START_DOCUMENT:
69 | break;
70 | case XmlPullParser.START_TAG:
71 | if ("FileInfo".equals(tag)) {
72 | fileInfo.setFileName(xmlPullParser.getAttributeValue(0));
73 | fileInfo.setFileLength(Integer.valueOf(xmlPullParser.getAttributeValue(1)));
74 | fileInfo.setReceivedLength(Integer.valueOf(xmlPullParser.getAttributeValue(2)));
75 | fileInfo.setmURI(new URI(xmlPullParser.getAttributeValue(3)));
76 | } else if("Piece".equals(tag)) {
77 | fileInfo.addPiece((long)Integer.valueOf(xmlPullParser.getAttributeValue(0)),
78 | (long)Integer.valueOf(xmlPullParser.getAttributeValue(1)),
79 | (long)Integer.valueOf(xmlPullParser.getAttributeValue(2)));
80 | Log.d(TAG, "add a Piece");
81 | }
82 | break;
83 | case XmlPullParser.END_TAG:
84 | break;
85 | }
86 | eventType = xmlPullParser.next();
87 | }
88 |
89 | fileInfo.printDebug();
90 | return fileInfo;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------