因为项目需求,要自己添加一个OTA升级的接口验证是否能正常升级,所以自己在设置下加了个Receiver,收到广播后验证升级,也可以自己在设置随便哪个界面添加一个菜单,点击升级。
下面来看具体实现的代码
1.alps/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/ota/OtaUpdateReceiver.java
package com.android.settings.ota;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.os.UpdateEngine;
import android.os.UpdateEngineCallback;
import android.os.PowerManager;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.FileInputStream;
import android.os.Build;
import com.android.settings.ota.UpdateParser;
public class OtaUpdateReceiver extends BroadcastReceiver {
private static final String TAG = "OtaUpdateReceiver";
Context mContext;
@Override
public void onReceive(final Context context, final Intent intent) {
Log.d(TAG, " OtaUpdateReceiver");
mContext = context;
//收到升级广播"com.android.action.updateOta"后开始升级
if (("com.android.action.updateOta").equals(intent.getAction())) {
String path = intent.getStringExtra("path"); //升级所需的update.zip具体放的位置
Log.d("TAG", "intent.getAction():" + intent.getAction()+"; path:"+path);
startUpdate(path); //开始升级
}
}
private UpdateEngine mUpdateEngine;
String updateFilePath;
private void startUpdate(String path) {
mUpdateEngine = new UpdateEngine();
UpdateParser.ParsedUpdate parsedUpdate = null;
copyFile(path,"/cache/recovery/update.zip"); //copy升级的update.zip到cache分区
try{
parsedUpdate = UpdateParser.parse(new File("/cache/recovery/update.zip"));
Log.e(TAG,"parsedUpdate:"+parsedUpdate.toString());
}catch(Exception e){
Log.e(TAG,"e="+e.toString());
e.printStackTrace();
}
Log.d(TAG, "startUpdate: start");
mUpdateEngine.bind(new UpdateEngineCallback() {
public void onStatusUpdate(int status, float percent) {
// 处理升级状态更新
Log.d(TAG, "onStatusUpdate: status :" + status + "; percent" + percent + "%");
switch (status) {
case UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT:
rebootNow();
break;
case UpdateEngine.UpdateStatusConstants.DOWNLOADING:
break;
default:
// noop
}
}
public void onPayloadApplicationComplete(int errorCode) {
// 处理升级完成
Log.d(TAG, "onPayloadApplicationComplete: errorCode :" + errorCode);
}
public void onVerificationComplete(int errorCode) {
// 处理升级包验证完成
Log.d(TAG, "onVerificationComplete: errorCode :" + errorCode);
}
});
Log.d(TAG, "startUpdate: parsedUpdate:"+parsedUpdate);
if(parsedUpdate != null) {
try {
Log.d(TAG, "startUpdate: applyPayload:");
mUpdateEngine.applyPayload(parsedUpdate.mUrl, parsedUpdate.mOffset, parsedUpdate.mSize, parsedUpdate.mProps);
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "startUpdate: printStackTrace:");
}
}
}
public void copyFile(String srcPath, String destPath) {
File srcFile = new File(srcPath); // 源文件
if (!srcFile.exists()) {
Log.e("TAG", "源文件不存在");
return;
}
try {
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
android.os.FileUtils.copy(fis, fos);
} else {
byte[] buf = new byte[1024];
int c;
while ((c = fis.read(buf)) != -1) {
fos.write(buf, 0, c);
}
fis.close();
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private void rebootNow() {
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
powerManager.reboot(null);
}
}
2.alps/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/ota/UpdateParser.java
package com.android.settings.ota;
import android.annotation.SuppressLint;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Preconditions;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/** Parse an A/B update zip file. */
class UpdateParser {
private static final String TAG = "UpdateParser";
private static final String PAYLOAD_BIN_FILE = "payload.bin";
private static final String PAYLOAD_PROPERTIES = "payload_properties.txt";
private static final String FILE_URL_PREFIX = "file://";
private static final int ZIP_FILE_HEADER = 30;
private UpdateParser() {
}
/**
* Parse a zip file containing a system update and return a non null ParsedUpdate.
*/
@SuppressLint("RestrictedApi")
@Nullable
static ParsedUpdate parse(@NonNull File file) throws IOException {
Preconditions.checkNotNull(file);
long payloadOffset = 0;
long payloadSize = 0;
boolean payloadFound = false;
String[] props = null;
try (ZipFile zipFile = new ZipFile(file)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
long fileSize = entry.getCompressedSize();
if (!payloadFound) {
payloadOffset += ZIP_FILE_HEADER + entry.getName().length();
if (entry.getExtra() != null) {
payloadOffset += entry.getExtra().length;
}
}
if (entry.isDirectory()) {
continue;
} else if (entry.getName().equals(PAYLOAD_BIN_FILE)) {
payloadSize = fileSize;
payloadFound = true;
} else if (entry.getName().equals(PAYLOAD_PROPERTIES)) {
InputStreamReader is =new InputStreamReader(zipFile.getInputStream(entry));
BufferedReader br = new BufferedReader(is);
List<String> lines = new ArrayList<String>();
String line = null;
while ((line = br.readLine()) != null) {
Log.e(TAG, "getPayloadProperties line: " + line);
lines.add(line);
}
props = lines.toArray(new String[lines.size()]);
br.close();
is.close();
}
if (!payloadFound) {
payloadOffset += fileSize;
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, String.format("Entry %s", entry.getName()));
}
}
}
return new ParsedUpdate(file, payloadOffset, payloadSize, props);
}
/** Information parsed from an update file. */
static class ParsedUpdate {
final String mUrl;
final long mOffset;
final long mSize;
final String[] mProps;
ParsedUpdate(File file, long offset, long size, String[] props) {
mUrl = FILE_URL_PREFIX + file.getAbsolutePath();
mOffset = offset;
mSize = size;
mProps = props;
}
/** Verify the update information is correct. */
boolean isValid() {
return mOffset >= 0 && mSize > 0 && mProps != null;
}
@Override
public String toString() {
return String.format(Locale.getDefault(),
"ParsedUpdate: URL=%s, offset=%d, size=%s, props=%s",
mUrl, mOffset, mSize, Arrays.toString(mProps));
}
}
}
可能还会有一些te权限问题,自己添加下权限即可
alps/device/mediatek/sepolicy/basic/plat_private/system_app.te
alps/device/mediatek/sepolicy/basic/plat_private/system_server.te
主要的升级逻辑就是执行 startUpdate(path)方法,通过调用系统UpdateEngine来进行升级。