Android 上層RecoverySystem類

來源:互聯網
上載者:User

嘗試將imx51使用OTA方式進行recovery,將android_recovery.img放入/cache/分區下然後再下一次重啟的時候進行分區更新,但發現放入的檔案會被莫名刪除,logcat中搜尋到recovery相關log,tag為recoverysystem,嘗試在android工程framework的os代碼中尋找代碼,找到相關代碼,代碼最後有將cache分區進行刪除僅保留last_log的操作,這才解開心中的疑惑,而且發現,以前本地升級時寫檔案的代碼其實不需要自己寫的,android自己已經做好了類的函數,需要的只是調用。代碼很簡單,和recovery的代碼幾乎一樣,java話而已,無須一條條解釋代碼。List
as below:

部分中文解釋為下:(參考http://www.elexcon.com/news/54208.html)

從Android 2.2開始新增RecoverySystem類,可以協助我們調用系統還原等操作,使用RecoverySystem必須是API
Level最小為為8,該類位於android.os.RecoverySystem,提供了三個靜態方法

  static void  installPackage(Context context, File packageFile)   //重啟裝置,安裝一個更新包

  static void  rebootWipeUserData(Context context)  //重啟裝置,清除使用者資料分區類似恢復出廠預設值

 

  static void  verifyPackage(File packageFile, RecoverySystem.ProgressListener listener, File deviceCertsZipFile)  //驗證加密簽名的系統更新包在安裝前,其中第二個數介面的具體定義為 android.os.RecoverySystem.ProgressListener  其中只有一個回調方法  abstract void  onProgress(int progress)   來顯示效驗的進度。

android/frameworks/base/core/java/android/os/RecoverySystem.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.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log;import java.io.ByteArrayInputStream;import java.io.File;import java.io.FileNotFoundException;import java.io.FileWriter;import java.io.IOException;import java.io.RandomAccessFile;import java.security.GeneralSecurityException;import java.security.PublicKey;import java.security.Signature;import java.security.SignatureException;import java.security.cert.Certificate;import java.security.cert.CertificateFactory;import java.security.cert.X509Certificate;import java.util.Collection;import java.util.Enumeration;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.zip.ZipEntry;import java.util.zip.ZipFile;import org.apache.harmony.security.asn1.BerInputStream;import org.apache.harmony.security.pkcs7.ContentInfo;import org.apache.harmony.security.pkcs7.SignedData;import org.apache.harmony.security.pkcs7.SignerInfo;import org.apache.harmony.security.provider.cert.X509CertImpl;/** * 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.c. */    private static File RECOVERY_DIR = new File("/cache/recovery");    private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");    private static File LOG_FILE = new File(RECOVERY_DIR, "log");    private static String LAST_LOG_FILENAME = "last_log";    // Length limits for reading files.    private static int LOG_FILE_MAX_LENGTH = 64 * 1024;    /**     * 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<Certificate> getTrustedCerts(File keystore)        throws IOException, GeneralSecurityException {        HashSet<Certificate> trusted = new HashSet<Certificate>();        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();                trusted.add(cf.generateCertificate(zip.getInputStream(entry)));            }        } 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 {        long fileLen = packageFile.length();        RandomAccessFile raf = new RandomAccessFile(packageFile, "r");        try {            int lastPercent = 0;            long lastPublishTime = System.currentTimeMillis();            if (listener != null) {                listener.onProgress(lastPercent);            }            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)");            }            int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8);            int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8);            Log.v(TAG, String.format("comment size %d; signature start %d",                                     commentSize, signatureStart));            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");                }            }            // The following code is largely copied from            // JarUtils.verifySignature().  We could just *call* that            // method here if that function didn't read the entire            // input (ie, the whole OTA package) into memory just to            // compute its message digest.            BerInputStream bis = new BerInputStream(                new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart));            ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis);            SignedData signedData = info.getSignedData();            if (signedData == null) {                throw new IOException("signedData is null");            }            Collection encCerts = signedData.getCertificates();            if (encCerts.isEmpty()) {                throw new IOException("encCerts is empty");            }            // Take the first certificate from the signature (packages            // should contain only one).            Iterator it = encCerts.iterator();            X509Certificate cert = null;            if (it.hasNext()) {                cert = new X509CertImpl((org.apache.harmony.security.x509.Certificate)it.next());            } else {                throw new SignatureException("signature contains no certificates");            }            List sigInfos = signedData.getSignerInfos();            SignerInfo sigInfo;            if (!sigInfos.isEmpty()) {                sigInfo = (SignerInfo)sigInfos.get(0);            } else {                throw new IOException("no signer infos!");            }            // Check that the public key of the certificate contained            // in the package equals one of our trusted public keys.            HashSet<Certificate> trusted = getTrustedCerts(                deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile);            PublicKey signatureKey = cert.getPublicKey();            boolean verified = false;            for (Certificate 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.            // The verifier in recovery *only* handles SHA1withRSA            // signatures.  SignApk.java always uses SHA1withRSA, no            // matter what the cert says to use.  Ignore            // cert.getSigAlgName(), and instead use whatever            // algorithm is used by the signature (which should be            // SHA1withRSA).            String da = sigInfo.getdigestAlgorithm();            String dea = sigInfo.getDigestEncryptionAlgorithm();            String alg = null;            if (da == null || dea == null) {                // fall back to the cert algorithm if the sig one                // doesn't look right.                alg = cert.getSigAlgName();            } else {                alg = da + "with" + dea;            }            Signature sig = Signature.getInstance(alg);            sig.initVerify(cert);            // 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;            raf.seek(0);            byte[] buffer = new byte[4096];            boolean interrupted = false;            while (soFar < toRead) {                interrupted = Thread.interrupted();                if (interrupted) break;                int size = buffer.length;                if (soFar + size > toRead) {                    size = (int)(toRead - soFar);                }                int read = raf.read(buffer, 0, size);                sig.update(buffer, 0, read);                soFar += read;                if (listener != null) {                    long now = System.currentTimeMillis();                    int p = (int)(soFar * 100 / toRead);                    if (p > lastPercent &&                        now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {                        lastPercent = p;                        lastPublishTime = now;                        listener.onProgress(lastPercent);                    }                }            }            if (listener != null) {                listener.onProgress(100);            }            if (interrupted) {                throw new SignatureException("verification was interrupted");            }            if (!sig.verify(sigInfo.getEncryptedDigest())) {                throw new SignatureException("signature digest verification failed");            }        } finally {            raf.close();        }    }    /**     * 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 {        String filename = packageFile.getCanonicalPath();        Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");        String arg = "--update_package=" + filename;        bootCommand(context, arg);    }    /**     * Reboots the device and wipes the user data partition.  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.     */    public static void rebootWipeUserData(Context context) throws IOException {        final ConditionVariable condition = new ConditionVariable();        Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");        context.sendOrderedBroadcast(intent, android.Manifest.permission.MASTER_CLEAR,                new BroadcastReceiver() {                    @Override                    public void onReceive(Context context, Intent intent) {                        condition.open();                    }                }, null, 0, null, null);        // Block until the ordered broadcast has completed.        condition.block();        bootCommand(context, "--wipe_data");    }    /**     * Reboot into the recovery system to wipe the /data partition and toggle     * Encrypted File Systems on/off.     * @param extras to add to the RECOVERY_COMPLETED intent after rebooting.     * @throws IOException if something goes wrong.     *     * @hide     */    public static void rebootToggleEFS(Context context, boolean efsEnabled)        throws IOException {        if (efsEnabled) {            bootCommand(context, "--set_encrypted_filesystem=on");        } else {            bootCommand(context, "--set_encrypted_filesystem=off");        }    }    /**     * Reboot into the recovery system with the supplied argument.     * @param arg to pass to the recovery utility.     * @throws IOException if something goes wrong.     */    private static void bootCommand(Context context, String arg) throws IOException {        RECOVERY_DIR.mkdirs();  // In case we need it        COMMAND_FILE.delete();  // In case it's not writable        LOG_FILE.delete();        FileWriter command = new FileWriter(COMMAND_FILE);        try {            command.write(arg);            command.write("\n");        } finally {            command.close();        }        // Having written the command file, go ahead and reboot        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);        pm.reboot("recovery");        throw new IOException("Reboot failed (no permissions?)");    }    /**     * 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() {        // Record the tail of the LOG_FILE        String 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);        }        // Delete everything in RECOVERY_DIR except LAST_LOG_FILENAME        String[] names = RECOVERY_DIR.list();        for (int i = 0; names != null && i < names.length; i++) {            if (names[i].equals(LAST_LOG_FILENAME)) continue;            File f = new File(RECOVERY_DIR, names[i]);            if (!f.delete()) {                Log.e(TAG, "Can't delete: " + f);            } else {                Log.i(TAG, "Deleted: " + f);            }        }        return log;    }    private void RecoverySystem() { }  // Do not instantiate}

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.