Android11.0系统中添加OTA升级接口供应用层调用

Android11.0系统中添加OTA升级接口供应用层调用

添加OTA升级接口

本文主要描述在RK3568 Android11中通过在自已的系统App中添加OTA升级接口,上层应用开发者在自己的应用中调用此接口进行系统升级。
如何在系统源码中添加开发自己的系统App,可以查看: Android11.0系统中添加开发系统App

参考及说明

OTA系统升级主要分AB系统升级及非AB系统升级,本文实现的是非AB系统的升级,主要参考了services.devicepolicy源码的相关逻辑,路径为:/frameworks/base/services/devicepolicy。
在这里插入图片描述

  1. AB系统:主要是基于UpdateEngine及UpdateEngineCallback,路径:/frameworks/base/core/java/android/os/UpdateEngine.java /frameworks/base/core/java/android/os/UpdateEngineCallback.java
  2. 非AB系统,主要是调用RecoverySystem.installPackage(mContext, mCopiedUpdateFile)方法,路径为 /frameworks/base/core/java/android/os/RecoverySystem.java;
  3. RK3568 Android11.0中将OTA升级包adb上传到/storage/emulated/0/update.zip,重启系统,系统中的RKUpdateService服务也会自动检测到升级包,并提示固件升级。

Demo app中添加OTA功能

Demo应用通过其它应用传送的Uri获取OTA升级包文件,并保存为/storage/emulated/0/update.zip,之后调用RecoverySystem.installPackage()方法升级。

  1. 添加UpdateActivity,路径为 /vendor/yjz/demo/app/src/main/java/com/yjz/demo/update/UpdateActivity.java;

package com.yjz.demo.update;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.WindowManager;

import androidx.annotation.Nullable;
import com.yjz.demo.util.SpHelper;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class UpdateActivity extends Activity {
    
    
    private static final String TAG = "UpdateActivity";

    private String mOtaFileSavePath;
    private int mOtaVersion;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
        layoutParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

        super.onCreate(savedInstanceState);

        // 保存文件路径:/storage/emulated/0/update.zip
        mOtaFileSavePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "update.zip";

        Intent intent = getIntent();
        mOtaVersion = intent.getIntExtra("ota_version", -1);


        StagingAsyncAppTask mStagingTask = new StagingAsyncAppTask();
        mStagingTask.execute(getIntent().getData());
    }

    private void startInstall() {
    
    
        new Thread() {
    
    
            @Override
            public void run() {
    
    
                UpdateManager.getInstance(UpdateActivity.this).startOTAUpdate(mOtaVersion, mOtaFileSavePath, new UpdateManager.OTAUpdateReadyListener() {
    
    
                    @Override
                    public void onReady() {
    
    
                        SpHelper.getInstance(UpdateActivity.this).setOTAVersion(Version.SYSTEM_OTA_VERSION);
                    }
                });
            }
        }.start();
    }

    @SuppressLint("NewApi")
    private final class StagingAsyncAppTask extends AsyncTask<Uri, Void, File> {
    
    

        @Override
        protected File doInBackground(Uri... params) {
    
    
            Log.d(TAG, "copy file from user app start");
            if (params == null || params.length <= 0) {
    
    
                return null;
            }
            Uri packageUri = params[0];
            if (null == packageUri) {
    
    
                return null;
            }
            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
    
    
                // Despite the comments in ContentResolver#openInputStream the returned stream can
                // be null.
                if (in == null) {
    
    
                    return null;
                }

                File mStagedFile = new File(mOtaFileSavePath);

                try (OutputStream out = new FileOutputStream(mStagedFile)) {
    
    
                    byte[] buffer = new byte[1024 * 1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
    
    
                        // Be nice and respond to a cancellation
                        out.write(buffer, 0, bytesRead);
                    }
                }
                return mStagedFile;
            } catch (IOException | SecurityException | IllegalStateException e) {
    
    
                Log.w(TAG, "Error staging apk from content URI", e);
            }
            return null;
        }

        @Override
        protected void onPostExecute(File installFile) {
    
    
            if (null != installFile) {
    
    
                // Now start the installation again from a file
                Log.d(TAG, "copy file from user app finish");

                startInstall();

                Log.d(TAG, "send to install");
            } else {
    
    
                Log.d(TAG, "copy file from user app fail");
            }

            finish();
        }
    }
}

  1. UpdateManager、Version,在源码中为了便于引包,一些逻辑类实现在/frameworks/base/core/java/com/yjz/study;
    注意:OTA升级包路径/storage/emulated/0/update.zip 在传入RecoverySystem.installPackage(Context context, File packageFile)方法时,需要将路径修改为/data/media/0/update.zip,否则系统重启进入recovery模式后会找不到升级包,recovery模式下路径是/data/media/0/

package com.yjz.study.core.update;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Environment;
import android.os.RecoverySystem;
import android.util.Log;
import com.yjz.study.core.Version;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;

public final class UpdateManager {
    
    
    private static final String TAG = "UpdateManager";

    private Context mContext;

    private Object mLock = new byte[0];
    private boolean mHasOTAUpdate;

    private static volatile UpdateManager INSTANCE;

    private UpdateManager(Context context) {
    
    
        mContext = context;
    }

    public static UpdateManager getInstance(Context context) {
    
    
        if (null == INSTANCE) {
    
    
            synchronized (UpdateManager.class) {
    
    
                if (null == INSTANCE) {
    
    
                    INSTANCE = new UpdateManager(context.getApplicationContext());
                }
            }
        }
        return INSTANCE;
    }

    @SuppressLint("NewApi")
    public void startOTAUpdate(int otaVersion, String otaPath, OTAUpdateReadyListener listener) {
    
    
        Log.d(TAG, "startOTAUpdate--->" + otaPath);

        if (otaVersion < 1) {
    
    
            Log.e(TAG, "ota version not found");
            return;
        }

        if (otaVersion <= Version.SYSTEM_OTA_VERSION) {
    
    
            Log.e(TAG, "has new version, not need update");
            return;
        }

        File f = new File(otaPath);
        if (!f.exists()) {
    
    
            Log.e(TAG, "ota file not found");
            return;
        }

        if (!"update.zip".equals(f.getName())) {
    
    
            Log.e(TAG, "ota file name error");
            return;
        }

        if (!otaPath.contains(Environment.getExternalStorageDirectory().getAbsolutePath())) {
    
    
            Log.e(TAG, "ota file path error");
            return;
        }

        try {
    
    
            RecoverySystem.verifyPackage(f, new RecoverySystem.ProgressListener() {
    
    
                @Override
                public void onProgress(int progress) {
    
    
                    Log.d(TAG, "verifyPackage progress--->" + progress);
                }
            }, null);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } catch (GeneralSecurityException e) {
    
    
            Log.e(TAG, "ota升级包验证失败--->");
            e.printStackTrace();
            return;
        }

        String realPath = "/data/media/0/" + f.getName();

        synchronized (mLock) {
    
    
            if (mHasOTAUpdate) {
    
    
                Log.d(TAG, "OTA升级任务进行中");
                return;
            }
            mHasOTAUpdate = true;
        }

        if (null != listener) {
    
    
            listener.onReady();
        }

        //不是A/B系统,直接使用此方法升级
        try {
    
    
            RecoverySystem.installPackage(mContext, new File(realPath));
        } catch (IOException e) {
    
    
            Log.w(TAG, "IO error while trying to install non AB update.", e);
            return;
        }

        if (null != f && f.exists()) {
    
    
            f.delete();
        }

    }

    public interface OTAUpdateReadyListener {
    
    
        void onReady();
    }
}


package com.yjz.study.core;

public interface Version {
    
    
    int SYSTEM_OTA_VERSION = 1;
}

  1. 在AndroidManifest.xml中注册UpdateActivity,路径为 /vendor/yjz/demo/app/src/main/AndroidManifest.xml;

    调用RecoverySystem.installPackage()必须要添加权限:
    android.permission.RECOVERY;
    android.permission.REBOOT;

    如果不需要用户看到OTA升级界面,想静默进行,UpdateActivity的主题设置为
    android:theme=“@android:style/Theme.Translucent.NoTitleBar”


//********省略代码******

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECOVERY" />
    <uses-permission android:name="android.permission.REBOOT" />
    <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    
     <activity
         android:name=".update.UpdateActivity"
         android:theme="@android:style/Theme.Translucent.NoTitleBar"
         android:exported="true">

         <intent-filter android:priority="1">
            <action android:name="android.intent.action.OTA_UPDATE" />
            <category android:name="android.intent.category.DEFAULT" />

          </intent-filter>

          <intent-filter android:priority="1">
             <action android:name="android.intent.action.OTA_UPDATE" />
             <category android:name="android.intent.category.DEFAULT" />

             <data android:scheme="content" />
             <data android:mimeType="application/vnd.android.package-archive" />
         </intent-filter>

    </activity>

//********省略代码******

应用请求进行OTA升级

  1. 开发者在应用可以通过requestOTAUpdate()方法示例进行升级请求。

    public int requestOTAUpdate(Context context, Uri uri, int otaVersion, int currentOtaVersion) {
    
    
        if (otaVersion <= currentOtaVersion) {
    
    
            return ERROR_CODE_NOT_UPDATE;
        }

        try {
    
    
            PackageInfo packinfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
            String[] list = packinfo.requestedPermissions;
            boolean hasPermission = false;
            if (null != list) {
    
    
                for (int i = 0; i < list.length; i++) {
    
    
                    if (Manifest.permission.QUERY_ALL_PACKAGES.equals(list[i])) {
    
    
                        hasPermission = true;
                        break;
                    }
                }
            }
            if (!hasPermission) {
    
    
                throw new RuntimeException("need permission " + Manifest.permission.QUERY_ALL_PACKAGES);
            }
        } catch (PackageManager.NameNotFoundException e) {
    
    
            throw new RuntimeException(e);
        }

        if (!checkExistForApp(context, "com.yjz.demo")) {
    
    
            return ERROR_CODE_NOT_SUPPORTED;
        }

        Intent intent = new Intent("android.intent.action.OTA_UPDATE");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        intent.putExtra("ota_version", otaVersion);
        intent.putExtra("allowed_Background", true);
        context.startActivity(intent);
        return CODE_SUCCESS;
    }

猜你喜欢

转载自blog.csdn.net/yjz_0314/article/details/133124329