很多时候都需要通过第三方的apk去做FOTA升级,在Android源码中其中有相关的接口类可以参考或者调用,路径如下:

frameworks/base/core/java/android/os/RecoverySystem.java
frameworks/base/services/core/java/com/android/server/RecoverySystemService.java 
/** Copyright (C) 2010 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package android.os;import android.annotation.SystemApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;import java.io.ByteArrayInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;import com.android.internal.logging.MetricsLogger;import sun.security.pkcs.PKCS7;
import sun.security.pkcs.SignerInfo;/*** RecoverySystem contains methods for interacting with the Android* recovery system (the separate partition that can be used to install* system updates, wipe user data, etc.)*/
public class RecoverySystem {private static final String TAG = "RecoverySystem";/*** Default location of zip file containing public keys (X509* certs) authorized to sign OTA updates.*/private static final File DEFAULT_KEYSTORE =new File("/system/etc/security/otacerts.zip");/** Send progress to listeners no more often than this (in ms). */private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500;/** Used to communicate with recovery.  See bootable/recovery/recovery.cpp. */private static final File RECOVERY_DIR = new File("/cache/recovery");private static final File LOG_FILE = new File(RECOVERY_DIR, "log");private static final File LAST_INSTALL_FILE = new File(RECOVERY_DIR, "last_install");private static final String LAST_PREFIX = "last_";/*** The recovery image uses this file to identify the location (i.e. blocks)* of an OTA package on the /data partition. The block map file is* generated by uncrypt.** @hide*/public static final File BLOCK_MAP_FILE = new File(RECOVERY_DIR, "block.map");/*** UNCRYPT_PACKAGE_FILE stores the filename to be uncrypt'd, which will be* read by uncrypt.** @hide*/public static final File UNCRYPT_PACKAGE_FILE = new File(RECOVERY_DIR, "uncrypt_file");// Length limits for reading files.private static final int LOG_FILE_MAX_LENGTH = 64 * 1024;// Prevent concurrent execution of requests.private static final Object sRequestLock = new Object();private final IRecoverySystem mService;/*** Interface definition for a callback to be invoked regularly as* verification proceeds.*/public interface ProgressListener {/*** Called periodically as the verification progresses.** @param progress  the approximate percentage of the*        verification that has been completed, ranging from 0*        to 100 (inclusive).*/public void onProgress(int progress);}/** @return the set of certs that can be used to sign an OTA package. */private static HashSet<X509Certificate> getTrustedCerts(File keystore)throws IOException, GeneralSecurityException {HashSet<X509Certificate> trusted = new HashSet<X509Certificate>();if (keystore == null) {keystore = DEFAULT_KEYSTORE;}ZipFile zip = new ZipFile(keystore);try {CertificateFactory cf = CertificateFactory.getInstance("X.509");Enumeration<? extends ZipEntry> entries = zip.entries();while (entries.hasMoreElements()) {ZipEntry entry = entries.nextElement();InputStream is = zip.getInputStream(entry);try {trusted.add((X509Certificate) cf.generateCertificate(is));} finally {is.close();}}} finally {zip.close();}return trusted;}/*** Verify the cryptographic signature of a system update package* before installing it.  Note that the package is also verified* separately by the installer once the device is rebooted into* the recovery system.  This function will return only if the* package was successfully verified; otherwise it will throw an* exception.** Verification of a package can take significant time, so this* function should not be called from a UI thread.  Interrupting* the thread while this function is in progress will result in a* SecurityException being thrown (and the thread's interrupt flag* will be cleared).** @param packageFile  the package to be verified* @param listener     an object to receive periodic progress* updates as verification proceeds.  May be null.* @param deviceCertsZipFile  the zip file of certificates whose* public keys we will accept.  Verification succeeds if the* package is signed by the private key corresponding to any* public key in this file.  May be null to use the system default* file (currently "/system/etc/security/otacerts.zip").** @throws IOException if there were any errors reading the* package or certs files.* @throws GeneralSecurityException if verification failed*/public static void verifyPackage(File packageFile,ProgressListener listener,File deviceCertsZipFile)throws IOException, GeneralSecurityException {final long fileLen = packageFile.length();final RandomAccessFile raf = new RandomAccessFile(packageFile, "r");try {final long startTimeMillis = System.currentTimeMillis();if (listener != null) {listener.onProgress(0);}raf.seek(fileLen - 6);byte[] footer = new byte[6];raf.readFully(footer);if (footer[2] != (byte)0xff || footer[3] != (byte)0xff) {throw new SignatureException("no signature in file (no footer)");}final int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8);final int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8);byte[] eocd = new byte[commentSize + 22];raf.seek(fileLen - (commentSize + 22));raf.readFully(eocd);// Check that we have found the start of the// end-of-central-directory record.if (eocd[0] != (byte)0x50 || eocd[1] != (byte)0x4b ||eocd[2] != (byte)0x05 || eocd[3] != (byte)0x06) {throw new SignatureException("no signature in file (bad footer)");}for (int i = 4; i < eocd.length-3; ++i) {if (eocd[i  ] == (byte)0x50 && eocd[i+1] == (byte)0x4b &&eocd[i+2] == (byte)0x05 && eocd[i+3] == (byte)0x06) {throw new SignatureException("EOCD marker found after start of EOCD");}}// Parse the signaturePKCS7 block =new PKCS7(new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart));// Take the first certificate from the signature (packages// should contain only one).X509Certificate[] certificates = block.getCertificates();if (certificates == null || certificates.length == 0) {throw new SignatureException("signature contains no certificates");}X509Certificate cert = certificates[0];PublicKey signatureKey = cert.getPublicKey();SignerInfo[] signerInfos = block.getSignerInfos();if (signerInfos == null || signerInfos.length == 0) {throw new SignatureException("signature contains no signedData");}SignerInfo signerInfo = signerInfos[0];// Check that the public key of the certificate contained// in the package equals one of our trusted public keys.boolean verified = false;HashSet<X509Certificate> trusted = getTrustedCerts(deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile);for (X509Certificate c : trusted) {if (c.getPublicKey().equals(signatureKey)) {verified = true;break;}}if (!verified) {throw new SignatureException("signature doesn't match any trusted key");}// The signature cert matches a trusted key.  Now verify that// the digest in the cert matches the actual file data.raf.seek(0);final ProgressListener listenerForInner = listener;SignerInfo verifyResult = block.verify(signerInfo, new InputStream() {// The signature covers all of the OTA package except the// archive comment and its 2-byte length.long toRead = fileLen - commentSize - 2;long soFar = 0;int lastPercent = 0;long lastPublishTime = startTimeMillis;@Overridepublic int read() throws IOException {throw new UnsupportedOperationException();}@Overridepublic int read(byte[] b, int off, int len) throws IOException {if (soFar >= toRead) {return -1;}if (Thread.currentThread().isInterrupted()) {return -1;}int size = len;if (soFar + size > toRead) {size = (int)(toRead - soFar);}int read = raf.read(b, off, size);soFar += read;if (listenerForInner != null) {long now = System.currentTimeMillis();int p = (int)(soFar * 100 / toRead);if (p > lastPercent &&now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {lastPercent = p;lastPublishTime = now;listenerForInner.onProgress(lastPercent);}}return read;}});final boolean interrupted = Thread.interrupted();if (listener != null) {listener.onProgress(100);}if (interrupted) {throw new SignatureException("verification was interrupted");}if (verifyResult == null) {throw new SignatureException("signature digest verification failed");}} finally {raf.close();}}/*** Process a given package with uncrypt. No-op if the package is not on the* /data partition.** @param Context      the Context to use* @param packageFile  the package to be processed* @param listener     an object to receive periodic progress updates as*                     processing proceeds.  May be null.* @param handler      the Handler upon which the callbacks will be*                     executed.** @throws IOException if there were any errors processing the package file.** @hide*/@SystemApipublic static void processPackage(Context context,File packageFile,final ProgressListener listener,final Handler handler)throws IOException {String filename = packageFile.getCanonicalPath();if (!filename.startsWith("/data/")) {return;}RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);IRecoverySystemProgressListener progressListener = null;if (listener != null) {final Handler progressHandler;if (handler != null) {progressHandler = handler;} else {progressHandler = new Handler(context.getMainLooper());}progressListener = new IRecoverySystemProgressListener.Stub() {int lastProgress = 0;long lastPublishTime = System.currentTimeMillis();@Overridepublic void onProgress(final int progress) {final long now = System.currentTimeMillis();progressHandler.post(new Runnable() {@Overridepublic void run() {if (progress > lastProgress &&now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {lastProgress = progress;lastPublishTime = now;listener.onProgress(progress);}}});}};}if (!rs.uncrypt(filename, progressListener)) {throw new IOException("process package failed");}}/*** Process a given package with uncrypt. No-op if the package is not on the* /data partition.** @param Context      the Context to use* @param packageFile  the package to be processed* @param listener     an object to receive periodic progress updates as*                     processing proceeds.  May be null.** @throws IOException if there were any errors processing the package file.** @hide*/@SystemApipublic static void processPackage(Context context,File packageFile,final ProgressListener listener)throws IOException {processPackage(context, packageFile, listener, null);}/*** Reboots the device in order to install the given update* package.* Requires the {@link android.Manifest.permission#REBOOT} permission.** @param context      the Context to use* @param packageFile  the update package to install.  Must be on* a partition mountable by recovery.  (The set of partitions* known to recovery may vary from device to device.  Generally,* /cache and /data are safe.)** @throws IOException  if writing the recovery command file* fails, or if the reboot itself fails.*/public static void installPackage(Context context, File packageFile)throws IOException {installPackage(context, packageFile, false);}/*** If the package hasn't been processed (i.e. uncrypt'd), set up* UNCRYPT_PACKAGE_FILE and delete BLOCK_MAP_FILE to trigger uncrypt during the* reboot.** @param context      the Context to use* @param packageFile  the update package to install.  Must be on a* partition mountable by recovery.* @param processed    if the package has been processed (uncrypt'd).** @throws IOException if writing the recovery command file fails, or if* the reboot itself fails.** @hide*/@SystemApipublic static void installPackage(Context context, File packageFile, boolean processed)throws IOException {synchronized (sRequestLock) {LOG_FILE.delete();// Must delete the file in case it was created by system server.UNCRYPT_PACKAGE_FILE.delete();String filename = packageFile.getCanonicalPath();Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");// If the package name ends with "_s.zip", it's a security update.boolean securityUpdate = filename.endsWith("_s.zip");// If the package is on the /data partition, the package needs to// be processed (i.e. uncrypt'd). The caller specifies if that has// been done in 'processed' parameter.if (filename.startsWith("/data/")) {if (processed) {if (!BLOCK_MAP_FILE.exists()) {Log.e(TAG, "Package claimed to have been processed but failed to find "+ "the block map file.");throw new IOException("Failed to find block map file");}} else {FileWriter uncryptFile = new FileWriter(UNCRYPT_PACKAGE_FILE);try {uncryptFile.write(filename + "\n");} finally {uncryptFile.close();}// UNCRYPT_PACKAGE_FILE needs to be readable and writable// by system server.if (!UNCRYPT_PACKAGE_FILE.setReadable(true, false)|| !UNCRYPT_PACKAGE_FILE.setWritable(true, false)) {Log.e(TAG, "Error setting permission for " + UNCRYPT_PACKAGE_FILE);}BLOCK_MAP_FILE.delete();}// If the package is on the /data partition, use the block map// file as the package name instead.filename = "@/cache/recovery/block.map";}final String filenameArg = "--update_package=" + filename + "\n";final String localeArg = "--locale=" + Locale.getDefault().toString() + "\n";final String securityArg = "--security\n";String command = filenameArg + localeArg;if (securityUpdate) {command += securityArg;}RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);if (!rs.setupBcb(command)) {throw new IOException("Setup BCB failed");}// Having set up the BCB (bootloader control block), go ahead and rebootPowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);pm.reboot(PowerManager.REBOOT_RECOVERY_UPDATE);throw new IOException("Reboot failed (no permissions?)");}}/*** Schedule to install the given package on next boot. The caller needs to* ensure that the package must have been processed (uncrypt'd) if needed.* It sets up the command in BCB (bootloader control block), which will* be read by the bootloader and the recovery image.** @param Context      the Context to use.* @param packageFile  the package to be installed.** @throws IOException if there were any errors setting up the BCB.** @hide*/@SystemApipublic static void scheduleUpdateOnBoot(Context context, File packageFile)throws IOException {String filename = packageFile.getCanonicalPath();boolean securityUpdate = filename.endsWith("_s.zip");// If the package is on the /data partition, use the block map file as// the package name instead.if (filename.startsWith("/data/")) {filename = "@/cache/recovery/block.map";}final String filenameArg = "--update_package=" + filename + "\n";final String localeArg = "--locale=" + Locale.getDefault().toString() + "\n";final String securityArg = "--security\n";String command = filenameArg + localeArg;if (securityUpdate) {command += securityArg;}RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);if (!rs.setupBcb(command)) {throw new IOException("schedule update on boot failed");}}/*** Cancel any scheduled update by clearing up the BCB (bootloader control* block).** @param Context      the Context to use.** @throws IOException if there were any errors clearing up the BCB.** @hide*/@SystemApipublic static void cancelScheduledUpdate(Context context)throws IOException {RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);if (!rs.clearBcb()) {throw new IOException("cancel scheduled update failed");}}/*** Reboots the device and wipes the user data and cache* partitions.  This is sometimes called a "factory reset", which* is something of a misnomer because the system partition is not* restored to its factory state.  Requires the* {@link android.Manifest.permission#REBOOT} permission.** @param context  the Context to use** @throws IOException  if writing the recovery command file* fails, or if the reboot itself fails.* @throws SecurityException if the current user is not allowed to wipe data.*/public static void rebootWipeUserData(Context context) throws IOException {rebootWipeUserData(context, false, context.getPackageName());}/** {@hide} */public static void rebootWipeUserData(Context context, String reason) throws IOException {rebootWipeUserData(context, false, reason);}/** {@hide} */public static void rebootWipeUserData(Context context, boolean shutdown)throws IOException {rebootWipeUserData(context, shutdown, context.getPackageName());}/*** Reboots the device and wipes the user data and cache* partitions.  This is sometimes called a "factory reset", which* is something of a misnomer because the system partition is not* restored to its factory state.  Requires the* {@link android.Manifest.permission#REBOOT} permission.** @param context   the Context to use* @param shutdown  if true, the device will be powered down after*                  the wipe completes, rather than being rebooted*                  back to the regular system.** @throws IOException  if writing the recovery command file* fails, or if the reboot itself fails.* @throws SecurityException if the current user is not allowed to wipe data.** @hide*/public static void rebootWipeUserData(Context context, boolean shutdown, String reason)throws IOException {UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);if (um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {throw new SecurityException("Wiping data is not allowed for this user.");}final ConditionVariable condition = new ConditionVariable();Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,android.Manifest.permission.MASTER_CLEAR,new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {condition.open();}}, null, 0, null, null);// Block until the ordered broadcast has completed.condition.block();String shutdownArg = null;if (shutdown) {shutdownArg = "--shutdown_after";}String reasonArg = null;if (!TextUtils.isEmpty(reason)) {reasonArg = "--reason=" + sanitizeArg(reason);}final String localeArg = "--locale=" + Locale.getDefault().toString();bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);}/*** Reboot into the recovery system to wipe the /cache partition.* @throws IOException if something goes wrong.*/public static void rebootWipeCache(Context context) throws IOException {rebootWipeCache(context, context.getPackageName());}/** {@hide} */public static void rebootWipeCache(Context context, String reason) throws IOException {String reasonArg = null;if (!TextUtils.isEmpty(reason)) {reasonArg = "--reason=" + sanitizeArg(reason);}final String localeArg = "--locale=" + Locale.getDefault().toString();bootCommand(context, "--wipe_cache", reasonArg, localeArg);}/*** Reboot into the recovery system with the supplied argument.* @param args to pass to the recovery utility.* @throws IOException if something goes wrong.*/private static void bootCommand(Context context, String... args) throws IOException {LOG_FILE.delete();StringBuilder command = new StringBuilder();for (String arg : args) {if (!TextUtils.isEmpty(arg)) {command.append(arg);command.append("\n");}}// Write the command into BCB (bootloader control block) and boot from// there. Will not return unless failed.RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);rs.rebootRecoveryWithCommand(command.toString());throw new IOException("Reboot failed (no permissions?)");}// Read last_install; then report time (in seconds) and I/O (in MiB) for// this update to tron.// Only report on the reboots immediately after an OTA update.private static void parseLastInstallLog(Context context) {try (BufferedReader in = new BufferedReader(new FileReader(LAST_INSTALL_FILE))) {String line = null;int bytesWrittenInMiB = -1, bytesStashedInMiB = -1;int timeTotal = -1;int sourceVersion = -1;while ((line = in.readLine()) != null) {// Here is an example of lines in last_install:// ...// time_total: 101// bytes_written_vendor: 51074// bytes_stashed_vendor: 200int numIndex = line.indexOf(':');if (numIndex == -1 || numIndex + 1 >= line.length()) {continue;}String numString = line.substring(numIndex + 1).trim();long parsedNum;try {parsedNum = Long.parseLong(numString);} catch (NumberFormatException ignored) {Log.e(TAG, "Failed to parse numbers in " + line);continue;}final int MiB = 1024 * 1024;int scaled;try {if (line.startsWith("bytes")) {scaled = Math.toIntExact(parsedNum / MiB);} else {scaled = Math.toIntExact(parsedNum);}} catch (ArithmeticException ignored) {Log.e(TAG, "Number overflows in " + line);continue;}if (line.startsWith("time")) {timeTotal = scaled;} else if (line.startsWith("source_build")) {sourceVersion = scaled;} else if (line.startsWith("bytes_written")) {bytesWrittenInMiB = (bytesWrittenInMiB == -1) ? scaled :bytesWrittenInMiB + scaled;} else if (line.startsWith("bytes_stashed")) {bytesStashedInMiB = (bytesStashedInMiB == -1) ? scaled :bytesStashedInMiB + scaled;}}// Don't report data to tron if corresponding entry isn't found in last_install.if (timeTotal != -1) {MetricsLogger.histogram(context, "ota_time_total", timeTotal);}if (sourceVersion != -1) {MetricsLogger.histogram(context, "ota_source_version", sourceVersion);}if (bytesWrittenInMiB != -1) {MetricsLogger.histogram(context, "ota_written_in_MiBs", bytesWrittenInMiB);}if (bytesStashedInMiB != -1) {MetricsLogger.histogram(context, "ota_stashed_in_MiBs", bytesStashedInMiB);}} catch (IOException e) {Log.e(TAG, "Failed to read lines in last_install", e);}}/*** Called after booting to process and remove recovery-related files.* @return the log file from recovery, or null if none was found.** @hide*/public static String handleAftermath(Context context) {// Record the tail of the LOG_FILEString log = null;try {log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n");} catch (FileNotFoundException e) {Log.i(TAG, "No recovery log file");} catch (IOException e) {Log.e(TAG, "Error reading recovery log", e);}if (log != null) {parseLastInstallLog(context);}// Only remove the OTA package if it's partially processed (uncrypt'd).boolean reservePackage = BLOCK_MAP_FILE.exists();if (!reservePackage && UNCRYPT_PACKAGE_FILE.exists()) {String filename = null;try {filename = FileUtils.readTextFile(UNCRYPT_PACKAGE_FILE, 0, null);} catch (IOException e) {Log.e(TAG, "Error reading uncrypt file", e);}// Remove the OTA package on /data that has been (possibly// partially) processed. (Bug: 24973532)if (filename != null && filename.startsWith("/data")) {if (UNCRYPT_PACKAGE_FILE.delete()) {Log.i(TAG, "Deleted: " + filename);} else {Log.e(TAG, "Can't delete: " + filename);}}}// We keep the update logs (beginning with LAST_PREFIX), and optionally// the block map file (BLOCK_MAP_FILE) for a package. BLOCK_MAP_FILE// will be created at the end of a successful uncrypt. If seeing this// file, we keep the block map file and the file that contains the// package name (UNCRYPT_PACKAGE_FILE). This is to reduce the work for// GmsCore to avoid re-downloading everything again.String[] names = RECOVERY_DIR.list();for (int i = 0; names != null && i < names.length; i++) {if (names[i].startsWith(LAST_PREFIX)) continue;if (reservePackage && names[i].equals(BLOCK_MAP_FILE.getName())) continue;if (reservePackage && names[i].equals(UNCRYPT_PACKAGE_FILE.getName())) continue;recursiveDelete(new File(RECOVERY_DIR, names[i]));}return log;}/*** Internally, delete a given file or directory recursively.*/private static void recursiveDelete(File name) {if (name.isDirectory()) {String[] files = name.list();for (int i = 0; files != null && i < files.length; i++) {File f = new File(name, files[i]);recursiveDelete(f);}}if (!name.delete()) {Log.e(TAG, "Can't delete: " + name);} else {Log.i(TAG, "Deleted: " + name);}}/*** Talks to RecoverySystemService via Binder to trigger uncrypt.*/private boolean uncrypt(String packageFile, IRecoverySystemProgressListener listener) {try {return mService.uncrypt(packageFile, listener);} catch (RemoteException unused) {}return false;}/*** Talks to RecoverySystemService via Binder to set up the BCB.*/private boolean setupBcb(String command) {try {return mService.setupBcb(command);} catch (RemoteException unused) {}return false;}/*** Talks to RecoverySystemService via Binder to clear up the BCB.*/private boolean clearBcb() {try {return mService.clearBcb();} catch (RemoteException unused) {}return false;}/*** Internally, recovery treats each line of the command file as a separate* argv, so we only need to protect against newlines and nulls.*/private static String sanitizeArg(String arg) {arg = arg.replace('\0', '?');arg = arg.replace('\n', '?');return arg;}/*** Talks to RecoverySystemService via Binder to set up the BCB command and* reboot into recovery accordingly.*/private void rebootRecoveryWithCommand(String command) {try {mService.rebootRecoveryWithCommand(command);} catch (RemoteException ignored) {}}/*** @removed Was previously made visible by accident.*/public RecoverySystem() {mService = null;}/*** @hide*/public RecoverySystem(IRecoverySystem service) {mService = service;}
}

在之前的项目中经常遇到客户的OTA apk内置进去后就报错升不了级的情况。这面我们可以简单总结一下。
第一是参照广升的FOTA修改seplicy下的权限文件让apk有recovery权限

2d74a390b3691748c8af8a51371ef4db88e957d1system/sepolicy/domain.te     | 16 +++++++-system/sepolicy/recovery.te   | 90 +++++++++++++++++++++++++++++++++++++++++--system/sepolicy/system_app.te |  4 ++3 files changed, 106 insertions(+), 4 deletions(-)diff --git a/system/sepolicy/domain.te b/system/sepolicy/domain.te
index 939c0510ae..c8aebf03d5 100755
--- a/system/sepolicy/domain.te
+++ b/system/sepolicy/domain.te
@@ -379,8 +379,10 @@ neverallow {neverallow { domain -servicemanager } *:binder set_context_mgr;# Only authorized processes should be writing to files in /data/dalvik-cache
+#fota startneverallow {domain
+  -recovery-init # TODO: limit init to relabelfrom for files-zygote-installd
@@ -390,12 +392,14 @@ neverallow {neverallow {domain
+  -recovery-init-installd-postinstall_dexopt-dex2oat-zygote} dalvikcache_data_file:dir no_w_dir_perms;
+#fota end# Only system_server should be able to send commands via the zygote socketneverallow { domain -zygote -system_server } zygote:unix_stream_socket connectto;
@@ -468,13 +472,16 @@ neverallow ~domain domain:process { transition dyntransition };# Example type transition:#  mydomain.te:file_type_auto_trans(mydomain, system_data_file, new_file_type)#
+#fota startneverallow {domain
+  -recovery-system_server-system_app-init-installd # for relabelfrom and unlink, check for this in explicit neverallow} system_data_file:file no_w_file_perms;
+#fota end# do not grant anything greater than r_file_perms and relabelfrom unlink# to installdneverallow installd system_data_file:file ~{ r_file_perms relabelfrom unlink };
@@ -494,8 +501,10 @@ neverallow {# Minimize read access to shell- or app-writable symlinks.# This is to prevent malicious symlink attacks.
+#fota startneverallow {domain
+  -recovery-appdomain-installd-uncrypt  # TODO: see if we can remove
@@ -503,17 +512,21 @@ neverallow {neverallow {domain
+  -recovery-shelluserdebug_or_eng(`-uncrypt')-installd} shell_data_file:lnk_file read;
+#fota end# In addition to the symlink reading restrictions above, restrict# write access to shell owned directories. The /data/local/tmp# directory is untrustworthy, and non-whitelisted domains should# not be trusting any content in those directories.
+#fota startneverallow {domain
+  -recovery-adbd-dumpstate-installd
@@ -524,6 +537,7 @@ neverallow {neverallow {domain
+  -recovery-adbd-appdomain-dumpstate
@@ -570,7 +584,7 @@ neverallow * domain:file { execute execute_no_trans entrypoint };# Instead, if access to part of debugfs is desired, it should have a# more specific label.# TODO: fix system_server and dumpstate
-neverallow { domain -init -system_server -cmd_services -dumpstate userdebug_or_eng(`-ylog') } debugfs:file no_rw_file_perms;
+neverallow { domain -init -system_server -dumpstate } debugfs:file no_rw_file_perms;neverallow {domain
diff --git a/system/sepolicy/recovery.te b/system/sepolicy/recovery.te
old mode 100644
new mode 100755
index d5767ed004..e30516c072
--- a/system/sepolicy/recovery.te
+++ b/system/sepolicy/recovery.te
@@ -2,7 +2,11 @@# Declare the domain unconditionally so we can always reference it# in neverallow rules.
-type recovery, domain, domain_deprecated;
+#fota start
+#type recovery, domain, domain_deprecated;
+type recovery, domain, domain_deprecated, mlstrustedsubject;
+typeattribute recovery mlstrustedsubject;
+#fota end# But the allow rules are only included in the recovery policy.# Otherwise recovery is only allowed the domain rules.
@@ -101,6 +105,84 @@ recovery_only(`# This line seems suspect, as it should not really need to# set scheduling parameters for a kernel domain task.allow recovery kernel:process setsched;
+
+  #fota start
+  allow recovery proc:file write;
+  allow recovery system_prop:property_service set;
+
+  allow recovery rootfs:dir { add_name create };
+  allow recovery rootfs:dir write;
+
+  allow recovery { adb_keys_file keychain_data_file }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { adb_keys_file keychain_data_file }:file { getattr unlink };
+
+  #allow recovery keystore_data_file:dir { open read getattr search };
+  #allow recovery keystore_data_file:file { getattr };
+
+  allow recovery { shell_data_file bluetooth_data_file net_data_file }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { shell_data_file bluetooth_data_file net_data_file }:file { getattr unlink };
+
+  allow recovery { apk_private_data_file vpn_data_file zoneinfo_data_file shared_relro_file }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { apk_private_data_file vpn_data_file zoneinfo_data_file shared_relro_file }:file { getattr unlink };
+
+  allow recovery { adb_data_file dhcp_data_file misc_user_data_file systemkeys_data_file }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { adb_data_file dhcp_data_file misc_user_data_file systemkeys_data_file }:file { getattr unlink };
+
+  allow recovery { wifi_data_file camera_data_file media_data_file wpa_socket }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { wifi_data_file camera_data_file media_data_file wpa_socket }:file { getattr unlink };
+
+  allow recovery { audio_data_file }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { audio_data_file }:file { getattr unlink };
+
+  allow recovery { anr_data_file asec_image_file backup_data_file }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { anr_data_file asec_image_file backup_data_file }:file { getattr unlink };
+
+  allow recovery { radio_data_file dalvikcache_data_file }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { radio_data_file dalvikcache_data_file }:file { getattr unlink };
+
+  allow recovery { drm_data_file nfc_data_file resourcecache_data_file }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { drm_data_file nfc_data_file resourcecache_data_file }:file { getattr unlink };
+
+  allow recovery property_data_file:dir { open read getattr search };
+  allow recovery property_data_file:file { getattr };
+
+  allow recovery { tombstone_data_file }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { tombstone_data_file }:file { getattr unlink };
+
+  allow recovery security_file:dir { open read getattr search };
+
+  allow recovery { cache_backup_file }:dir { open read write getattr search rmdir remove_name };
+  allow recovery { cache_backup_file }:file { getattr unlink };
+
+  allow recovery { wallpaper_file apk_private_tmp_file gps_data_file cache_file cache_backup_file efs_file }:file { getattr unlink};
+  allow recovery { asec_apk_file asec_public_file bluetooth_efs_file }:file { getattr unlink};
+
+  #allow recovery { rootfs }:file { create write getattr unlink };
+  allow recovery { shell_data_file radio_data_file bluetooth_data_file nfc_data_file }:lnk_file { getattr unlink read};
+
+  allow recovery { camera_data_file system_ndebug_socket system_wpa_socket wpa_socket }:sock_file { getattr unlink };
+
+  allow recovery media_rw_data_file:dir create_dir_perms;
+  allow recovery media_rw_data_file:file create_file_perms;
+  allow recovery media_rw_data_file:lnk_file { getattr unlink read};
+
+  allow recovery app_data_file:dir create_dir_perms;
+  allow recovery app_data_file:file create_file_perms;
+  allow recovery app_data_file:lnk_file { getattr unlink read};
+
+  allow recovery apk_data_file:dir create_dir_perms;
+  allow recovery apk_data_file:file create_file_perms;
+  allow recovery apk_data_file:lnk_file { getattr unlink read};
+
+  allow recovery system_app_data_file:dir create_dir_perms;
+  allow recovery system_app_data_file:file create_file_perms;
+  allow recovery system_app_data_file:lnk_file { getattr unlink read};
+
+  allow recovery system_data_file:dir create_dir_perms;
+  allow recovery system_data_file:file create_file_perms;
+  allow recovery system_data_file:lnk_file { getattr unlink read};
+  allow recovery system_data_file:fifo_file { open read write getattr rw_file_perms unlink };
+  #fota end')###
@@ -117,5 +199,7 @@ recovery_only(`# domains, including recovery.## TODO: tighten this up further.
-neverallow recovery data_file_type:file { no_w_file_perms no_x_file_perms };
-neverallow recovery data_file_type:dir no_w_dir_perms;
+#fota start
+#neverallow recovery data_file_type:file { no_w_file_perms no_x_file_perms };
+#neverallow recovery data_file_type:dir no_w_dir_perms;
+
diff --git a/system/sepolicy/system_app.te b/system/sepolicy/system_app.te
old mode 100644
new mode 100755
index 2d51c5a101..763793cf14
--- a/system/sepolicy/system_app.te
+++ b/system/sepolicy/system_app.te
@@ -73,4 +73,8 @@ allow system_app keystore:keystore_key {allow system_app sysfs_zram:dir search;allow system_app sysfs_zram:file r_file_perms;+#fota start
+allow system_app { cache_file cache_recovery_file }:dir create_dir_perms;
+allow system_app { cache_file cache_recovery_file }:file create_file_perms;
+control_logd(system_app)

第二次是修改sepolicy下的.te文件让system/app下的文件有读写权限:

503fcc824fe04e32ac7d009b0417a60be5a68534device/sprd/scx20/common/sepolicy/platform_app.te | 4 ++++device/sprd/scx20/common/sepolicy/uncrypt.te      | 4 ++++2 files changed, 8 insertions(+)diff --git a/device/sprd/scx20/common/sepolicy/platform_app.te b/device/sprd/scx20/common/sepolicy/platform_app.te
index b8a23ba315..8a5545f11f 100755
--- a/device/sprd/scx20/common/sepolicy/platform_app.te
+++ b/device/sprd/scx20/common/sepolicy/platform_app.te
@@ -8,3 +8,7 @@ allow platform_app slogmodem:unix_stream_socket { connectto sendto };allow platform_app system_app_data_file:file { open read write };allow platform_app mlog_file:file   rw_file_perms;allow platform_app mlog_file:dir  create_dir_perms;
+allow platform_app recovery_service:service_manager { find };
+allow platform_app app_data_file:dir { rmdir remove_name };
+allow platform_app app_data_file:file { unlink };
+
diff --git a/device/sprd/scx20/common/sepolicy/uncrypt.te b/device/sprd/scx20/common/sepolicy/uncrypt.te
index cfc5c1ea58..07dd00380b 100755
--- a/device/sprd/scx20/common/sepolicy/uncrypt.te
+++ b/device/sprd/scx20/common/sepolicy/uncrypt.te
@@ -1,3 +1,7 @@allow uncrypt media_rw_data_file:file { create open setattr getattr read write unlink };allow uncrypt media_rw_data_file:dir { create open setattr getattr read write unlink search };allow uncrypt mmcblk_device:blk_file { open setattr getattr read write };
+allow uncrypt system_app_data_file:dir rw_dir_perms;
+allow uncrypt system_app_data_file:file rw_file_perms;
+allow uncrypt app_data_file:dir rw_dir_perms;
+allow uncrypt app_data_file:file rw_file_perms;

第三次的情况是读不到userdata下的update.zp,查看log是userdata分区没有别挂载上,修改方法是:

1 file changed, 2 insertions(+)diff --git a/bootable/recovery/recovery.cpp b/bootable/recovery/recovery.cpp
index ec8073d21d..da734115ed 100755
--- a/bootable/recovery/recovery.cpp
+++ b/bootable/recovery/recovery.cpp
@@ -1023,6 +1023,8 @@ static int unzip_partition_file(const char* path) {int numKeys;setup_install_mounts();
+      if (strncmp(path, "/data/", 6) == 0)
+               ensure_path_mounted(path);sysMapFile(path, &map);std::vector<Certificate> loadedKeys;

这里有个地方要注意,有的源码里面可能将userdata分区加密了,我们可以在out下面搜userdata 可以看到下面的权限情况

/dev/block/platform/sdio_emmc/by-name/userdata /data        ext4 noatime,nosuid,nodev,nomblk_io_submit,noauto_da_alloc wait,encryptable=footer,check

对应的我们在device下面搜/dev/block/platform/sdio_emmc/by-name/userdata /data
就能找到对应的文件,修改一下即可。我贴出的代码里面userdata的加密情况是encryptable=footer,即默认没加密

Android 源码之Recovery升级的过程和问题分析相关推荐

  1. Android源码配置第三方应用电池白名单流程分析笔记

    这里以MTK6765 Android S举例说明,Android系统在加载客户应用白名单的过程. 首先Android系统可以根据不同手机厂商的需要进行源码的定制,当然定制应用白名单也是可以的,一般情况 ...

  2. android源码下载过程的一些注意事项,windows下载android源码

    网上目前有很多下载android源码的方法,在linux下基本上用 repo 脚本,先repo init 然后在 repo sync一下 最后提取的时候的 使用 repo sync -l 一下. 所有 ...

  3. android系统源码之 系统应用安装过程(下)

    android系统源码之 系统应用安装过程(上) :http://blog.csdn.net/king1425/article/details/70135813 下图是PackageParser主要数 ...

  4. 分享一篇关于“Android源码”下载的过程

    欢迎转载,但请注明出处!!! 转载请注明出处:http://blog.csdn.net/u013474104/article/details/78829847 参考: https://source.a ...

  5. Android原生OTA和Recovery升级过程步骤

    本文介绍了Android原生OTA和Recovery升级过程步骤. 进入升级 - 1.1 正常启动和进入Recovery的区别 下面给出了升级流程的简单示意图. 上图中的上下两个部分,上面一部分是正常 ...

  6. 简诉android源代码编译过程,详解Android源码的编译

    在这里我们将介绍的是Android源码的编译,主要基于Android 1.0环境下.希望对大家有所帮助. 本文将为大家介绍的是如何设置Android源码的编译环境,包括Linux下的配置.主要基于An ...

  7. android源码编译过程

    1.下载好android源码包. 2.装好vm,ubuntu(如果能在实体机装linux更好). 3.安装所需要的deb包 在终端执行如下命令: sudo apt-get install flex b ...

  8. 记一次 Android 源码编译刷机过程

    0 背景   为了能在开发设备上运行 adb root 命令得到 root 权限,获得更加强大的调试能力.开发体验,方便以后阅读源码时 Debug 跟踪.进行定制化开发,需要编译 Android 源码 ...

  9. android 源码开发 关于编译等小知识点总结

    fastboot flash boot out/target/product/generic/boot.img不管用,提示< waiting for device >    原因查找:   ...

  10. android源码编译1

    一.环境说明: 1.liunx系统:Ubuntu12.04 2.jdk:sun-java6-jdk 3.g++4.5 gcc4.5 二.android源码的目录结构 |--Makefile|--bio ...

最新文章

  1. 多线程编程 - GCD
  2. Fedora 32发布时间表(Fedora 32 Schedule)
  3. 不用L约束又不会梯度消失的GAN,了解一下?
  4. truncate 、delete与drop区别
  5. Qt treeWidget 查找指定字段内容的条目并跳转到该条目
  6. 分布式资本沈波:未来区块链杀手级应用将出现在“+区块链”
  7. 转载,handler
  8. DB2 在创建数据库的时候,后面不能加“;”分号
  9. BZOJ5212[ZJOI2018]历史
  10. 数据库上机练习-1-建表
  11. 奥西450的服务器系统,奥西TDS450
  12. 【序列化】Kryo 的几种常见序列化实现方式,及其兼容性
  13. SxSW机器人电视先生小组在节目中使用真实代码
  14. 头脑王者服务器维护,微信头脑王者请求超时及查询服务器失败的解决方法
  15. 又涨了?2023全国程序员薪资最新统计(文末附招聘岗位)
  16. Elasticsearch集成(二)
  17. 一文带你了解200G DAC高速线缆
  18. 知识图谱构建: Neo4j 常见实例应用
  19. 我用的awk的边边角角
  20. 【短视频运营】短视频剪辑 ① ( 下载软件 | 安装软件 | 简单使用 )

热门文章

  1. Verifying dml pool data
  2. jupyter notebook更改默认浏览器
  3. python转化excel数字日期为标准日期
  4. python转换excel 列号 为数字 数字转为列号
  5. Android视频播放器没声,Windows/Android/iOS全平台支持的视频播放器EasyPlayerPro,iOS版播放无音频问题如何解决?...
  6. 随感10-Transecndence超验骇客
  7. 计算机网络中报文摘要概念,报文摘要详细介绍
  8. php富强民主,给你的网站添加“富强民主”点击特效
  9. 小米平板开启位置服务器,小米平板电脑防盗定位方法
  10. rrcf算法的初步理解