【Android OTA】系統的更新升級

timg-3.jpeg

原文鏈接

上一篇說了Android應用的升級,這篇來介紹下關于整個系統的升級。公司的車載系統使用了MTK的板子,深度定制的Android系統,平時開發過程中的修改可以直接重新燒錄固件,但設備量產投入市場之后的修改只能通過OTA的方式進行更新。

系統升級一般通過差分包的方式,因為整包更新需要的安裝包有幾百M,一般的小更新打個差分包的話也就幾M十幾M而已,能省不少流量。

設備檢測更新的流程基本和上一篇文章一樣,系統可以在設置界面上增加檢測升級的入口。

系統升級流程

  1. 向服務器請求系統更新信息;
  2. 獲取到返回信息后,判斷是否有更新版本,若有更新,進入步驟3;
  3. 下載服務器對應的更新文件;
  4. 下載完成后驗證更新文件;
  5. 系統重啟進入recovery模式進行升級,升級完成后設備重啟。

更新信息的請求和返回信息的處理過程與應用的更新升級一致。

更新文件的下載這次使用了系統自帶的類 DownloadManager

DownloadManager 能自動管理系統的下載任務,在聯網發生變化、系統開關機、斷點續傳等情況自行進行處理。

創建下載任務,設置指定下載目錄

mDownloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);

DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDestinationInExternalPublicDir(OTAUtil.otaFolderName(), OTAUtil.otaFileName());
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
request.setVisibleInDownloadsUi(false);
mDownloadId = mDownloadManager.enqueue(request);

DownloadManager 下載進度可以顯示在通知欄中,也可以選擇只在特定聯網情況下(如wifi、移動信號)進行任務。

監聽下載狀態

class CompleteReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        long completeDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
        if (completeDownloadId == mDownloadId) {
            // download successful
            String msg;
            switch (OTAUtil.getOTADownloadStatus(completeDownloadId, mDownloadManager)) {
                case DownloadManager.STATUS_FAILED:
                    msg = "Download failed!";
                    break;

                case DownloadManager.STATUS_PAUSED:
                    msg = "Download paused!";
                    break;

                case DownloadManager.STATUS_PENDING:
                    msg = "Download pending!";
                    break;

                case DownloadManager.STATUS_RUNNING:
                    msg = "Download in progress!";
                    break;

                case DownloadManager.STATUS_SUCCESSFUL:
                    break;

                default:
                    msg = "Download is nowhere in sight";
                    break;
            }

            Log.i(TAG, "CompleteReceiver onReceive...." + msg);
        }
    }
}

用來更新界面的進度條

class DownloadChangeObserver extends ContentObserver {

    public DownloadChangeObserver() {
        super(mHandler);
    }

    @Override
    public void onChange(boolean selfChange) {

        int progress = OTAUtil.getOTADownloadPro(mDownloadId, mDownloadManager);

        Message msg = mHandler.obtainMessage();
        msg.what = UPDATE_DOWNLOAD_PROGRESS;
        msg.arg1 = progress;
        mHandler.sendMessage(msg);
    }
}

其中getOTADownloadPro 方法通過id獲取指定下載任務的進度值

public static int getOTADownloadPro(long id, DownloadManager downloadManager) {
    double progress = 0.0;

    DownloadManager.Query q = new DownloadManager.Query();
    q.setFilterById(id);

    Cursor c = downloadManager.query(q);
    if (c.moveToFirst()) {
        int sizeIndex = c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
        int downloadedIndex = c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
        long size = c.getInt(sizeIndex);
        long downloaded = c.getInt(downloadedIndex);

        if (size != -1) {
            progress = downloaded * 100.0 / size;
        }
    }
    c.close();

    return (int)progress;
}

這里有一個注意問題,正常情況下使用DownloadManager下載的文件只能保存在系統的外部存儲中(系統級的DownloadManager有個私有方法能將文件保存在內部存儲),而調用系統的接口更新升級時,recovery模式下系統文件路徑會重定向,此時無法找到原來保存在外部存儲的文件。所以為了保證能正常安裝,下載好的更新文件要保存到內部存儲中。

將更新文件從外部存儲拷貝到/data/recovery/ota.zip路徑下,這里使用了第三方庫 RootTools,能夠以命令行的方式,將文件從源目錄拷貝到指定目錄。

因為要對內部存儲進行讀寫,所以記得加上對應的權限

<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE"/>

安裝更新的函數具體實現

public static boolean installOtaPackageAuto(final Context context, File file) {

    String internalPath = "/data/recovery/ota.zip";
    String cmd = "cat " + file.getAbsolutePath() + " > " + internalPath;

    String res = Tools.shell(cmd, false);
    Log.i(TAG, "shell: " + res);

    File otaPackageFile = new File(internalPath);

    try {
        RecoverySystem.verifyPackage(otaPackageFile , null , null);
    }
    catch ( IOException e ) {
        ((HDUpdateActivity)context).runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, "RecoverySystem verifyPackage failed, file doesn't exist", Toast.LENGTH_LONG).show();
            }
        });
        e.printStackTrace( );
        return false;
    }
    catch ( GeneralSecurityException e ) {
        ((HDUpdateActivity)context).runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, "RecoverySystem verifyPackage failed, invalid package", Toast.LENGTH_LONG).show();
            }
        });
        e.printStackTrace( );
        return false;
    }

    try {
        RecoverySystem.installPackage( context , otaPackageFile );
    }
    catch ( IOException e ) {
        ((HDUpdateActivity)context).runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, "RecoverySystem installPackage error, failed to install", Toast.LENGTH_LONG).show();
            }
        });
        e.printStackTrace( );
        return false;
    }
    return true;
}

傳入的參數 fileDownloadManager已經下載到外部存儲的OTA更新文件,通過執行cat命令,將更新文件保存到內部存儲/data/recovery/ota.zip

String internalPath = "/data/recovery/ota.zip";
String cmd = "cat " + file.getAbsolutePath() + " > " + internalPath;

String res = Tools.shell(cmd, false);

調用了系統方法verifyPackage驗證更新包是否合法,若驗證失敗則安裝過程終止

RecoverySystem.verifyPackage(otaPackageFile , null , null);

接下來直接調用系統安裝更新的方法

RecoverySystem.installPackage( context , otaPackageFile );

若成功則設備重啟,并進入recovery模式更新升級,升級完成后設備重啟。

制作差分包

MTK系統的目錄

1.jpg

可以看到每次通過make命令編譯后生成的image文件既是 SP Flash Tool 燒錄需要用到的固件。

ota打包

make otapackage

打包完成后命令行打印的結果

2.jpg

這里有兩個文件需要注意的,一個在out/target/product/CM01B目錄下,一個在out/target/product/CM01B/obj/PACKAGING/target_files_intermediates目錄下(CM01B為產品編號,每個項目不一樣),雖然文件名一樣,但前者有400多M,后者800多M,和image固件大小差不多。

out/target/product/CM01B目錄下的文件為完全更新的包,可以用來給系統進行完全升級。

out/target/product/CM01B/obj/PACKAGING/target_files_intermediates目錄下的文件主要用來生成差分包(具體能不能直接用來升級沒嘗試過),通過兩個這種包可以生成差分包文件。

系統生成差分包的工具在這個位置,官方標準工具,里面包含了制作差分包的腳本

3.jpg

這里需要注意,執行差分包命令時必須在根目錄下執行,因為腳本里面寫定了相對路徑的引用文件。

制作差分包需要準備兩個不同版本的文件(out/target/product/CM01B/obj/PACKAGING/target_files_intermediates目錄下的),假設分別為a.zipb.zip

將兩個文件拷貝到根目錄下,然后運行命令

./build/tools/releasetools/ota_from_target_files -i a.zip b.zip ota.zip

最終生成的差分包文件為ota.zip,即b相對于a修改了的內容,只能用在a上將a升級成b。ota.zip差分包的升級使用要求當前系統必須是a,否則無法進行升級。

系統升級

系統通過差分包升級,安裝時進入刷機界面,完成后自動重啟,升級完成

4.jpg

如果差分包有不正確或者沒保存到內部存儲中會報錯

5.jpg

以上

源碼和設備相關聯的,這次就不給了。完。:)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,661評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,785評論 18 139
  • 原文鏈接 Android應用經常會內置檢測版本更新的功能,在有版本更新的時候,通過下載更新文件進行本地的升級。本文...
    msq3閱讀 10,903評論 0 19
  • 【作者】唐志燕 【指導老師】劉艷老師 【內容解說】這是金庫老師今天給家長上的V導師課程筆記。 主要講了兩個板塊的內...
    靜心和閱讀 201評論 0 0
  • 春來花香 雨來草長 晨來鳥唱 花香四溢只春一季 草長鶯飛皆因潤雨 鳥聲歡啼以慰晨意 我一心一念獨為等你 你來我喜 ...
    笨笨的平凡生活閱讀 173評論 0 0