Add OTA upgrade interface in Android11.0 system for application layer call

Add OTA upgrade interface in Android11.0 system for application layer call

Add OTA upgrade interface

This article mainly describes how to add an OTA upgrade interface to your own system App in RK3568 Android11. Upper-layer application developers can call this interface in their own applications to upgrade the system.
How to add and develop your own system App in the system source code, you can view: Add and develop system App in Android11.0 system

References and instructions

OTA system upgrades are mainly divided into AB system upgrades and non-AB system upgrades. This article implements the upgrade of non-AB systems, mainly referring to the relevant logic of the services.devicepolicy source code. The path is: /frameworks/base/services/devicepolicy.
Insert image description here

  1. AB system: Mainly based on UpdateEngine and UpdateEngineCallback, path: /frameworks/base/core/java/android/os/UpdateEngine.java /frameworks/base/core/java/android/os/UpdateEngineCallback.java
  2. For non-AB systems, the main method is to call the RecoverySystem.installPackage(mContext, mCopiedUpdateFile) method. The path is /frameworks/base/core/java/android/os/RecoverySystem.java;
  3. In RK3568 Android11.0, upload the OTA upgrade package adb to /storage/emulated/0/update.zip, restart the system, and the RKUpdateService service in the system will automatically detect the upgrade package and prompt for firmware upgrade.

Add OTA function to Demo app

The Demo application obtains the OTA upgrade package file through the Uri sent by other applications, saves it as /storage/emulated/0/update.zip, and then calls the RecoverySystem.installPackage() method to upgrade.

  1. Add UpdateActivity, the path is /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 and Version, in order to facilitate the introduction of packages in the source code, some logic classes are implemented in /frameworks/base/core/java/com/yjz/study;
    Note: OTA upgrade Package path/storage/emulated/0/update.zip When passing in the RecoverySystem.installPackage(Context context, File packageFile) method, you need to change the path to /data/media/0/update.zip, otherwise the system will restart and enter recovery mode. The upgrade package will not be found later. The path in recovery mode is /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. Register UpdateActivity in AndroidManifest.xml, the path is /vendor/yjz/demo/app/src/main/AndroidManifest.xml;

    Call RecoverySystem.installPackage() must add permissions:
    android.permission.RECOVERY;
    android.permission.REBOOT;

    If you do not need users to see the OTA upgrade interface and want to proceed silently, set the theme of UpdateActivity to
    android:theme="@android:style/Theme.Translucent.NoTitleBar"< /span>


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

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

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

App requests OTA upgrade

  1. Developers can make upgrade requests in their applications through the requestOTAUpdate() method example.

    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;
    }

Guess you like

Origin blog.csdn.net/yjz_0314/article/details/133124329