Recoverysystem class on Android

Source: Internet
Author: User
Tags pkcs7

Try to use OTA for recovery imx51, put android_recovery.img in/Cache/partition, and then update the partition during the next restart. However, it is found that the file is deleted inexplicably, logcat searches for the recovery related log, and the tag is recoverysystem. It tries to find the code in the OS code of the android framework and find the relevant code, the Code finally deletes the cache partition and only keeps the last_log. This solves the problem and finds that you do not need to write the file code during local upgrade, android has completed the class functions, and all you need is to call them. The code is very simple. It is almost the same as the code of recovery. Java is nothing more than an explanation of the Code. List
As below:

(Refer to http://www.elexcon.com/news/54208.html)

The recoverysystem class has been added since Android 2.2, which can help us call system restoration and other operations. The recoverysystem must be an API
The minimum level is 8, which is located in Android. OS. recoverysystem and provides three static methods.

Static void installpackage (context, file packagefile) // restart the device and install an update package

Static void rebootwipeuserdata (context) // restart the device. Clearing user data partitions is similar to restoring factory settings.

 

Static void verifypackage (File packagefile, recoverysystem. progresslistener listener, file devicecertszipfile) // The system update package for verifying the encrypted signature is defined as Android before installation. OS. recoverysystem. progresslistener only has one callback method abstract void onprogress (INT progress) to display the verification 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}

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.