RecoverySystem.java revision 1af33d0ddc2f50ade146e4d48e2feb6f1d553427
11af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker/*
21af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Copyright (C) 2010 The Android Open Source Project
31af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker *
41af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Licensed under the Apache License, Version 2.0 (the "License");
51af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * you may not use this file except in compliance with the License.
61af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * You may obtain a copy of the License at
71af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker *
81af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker *      http://www.apache.org/licenses/LICENSE-2.0
91af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker *
101af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Unless required by applicable law or agreed to in writing, software
111af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * distributed under the License is distributed on an "AS IS" BASIS,
121af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * See the License for the specific language governing permissions and
141af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * limitations under the License.
151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */
161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerpackage android.os;
181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.ByteArrayInputStream;
201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.File;
211af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.FileNotFoundException;
221af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.FileWriter;
231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.IOException;
241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.RandomAccessFile;
251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.GeneralSecurityException;
261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.PublicKey;
271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.Signature;
281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.SignatureException;
291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.cert.Certificate;
301af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.cert.CertificateFactory;
311af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.cert.X509Certificate;
321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.Collection;
331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.Enumeration;
341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.HashSet;
351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.Iterator;
361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.List;
371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.zip.ZipEntry;
381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.zip.ZipFile;
391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport android.content.Context;
411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport android.util.Log;
421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.asn1.BerInputStream;
441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.pkcs7.ContentInfo;
451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.pkcs7.SignedData;
461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.pkcs7.SignerInfo;
471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.provider.cert.X509CertImpl;
481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker/**
501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * RecoverySystem contains methods for interacting with the Android
511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * recovery system (the separate partition that can be used to install
521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * system updates, wipe user data, etc.)
531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker *
541af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @pending
551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */
561af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerpublic class RecoverySystem {
571af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    private static final String TAG = "RecoverySystem";
581af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
591af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /**
601af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Default location of zip file containing public keys (X509
611af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * certs) authorized to sign OTA updates.
621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     */
631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    private static final File DEFAULT_KEYSTORE =
641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        new File("/system/etc/security/otacerts.zip");
651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /** Send progress to listeners no more often than this (in ms). */
671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500;
681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /** Used to communicate with recovery.  See bootable/recovery/recovery.c. */
701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    private static File RECOVERY_DIR = new File("/cache/recovery");
711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    private static File LOG_FILE = new File(RECOVERY_DIR, "log");
731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    // Length limits for reading files.
751af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    private static int LOG_FILE_MAX_LENGTH = 8 * 1024;
761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
771af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /**
781af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Interface definition for a callback to be invoked regularly as
791af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * verification proceeds.
801af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     */
811af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    public interface ProgressListener {
821af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        /**
831af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker         * Called periodically as the verification progresses.
841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker         *
851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker         * @param progress  the approximate percentage of the
861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker         *        verification that has been completed, ranging from 0
871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker         *        to 100 (inclusive).
881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker         */
891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        public void onProgress(int progress);
901af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    }
911af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /** @return the set of certs that can be used to sign an OTA package. */
931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    private static HashSet<Certificate> getTrustedCerts(File keystore)
941af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        throws IOException, GeneralSecurityException {
951af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        HashSet<Certificate> trusted = new HashSet<Certificate>();
961af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        if (keystore == null) {
971af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            keystore = DEFAULT_KEYSTORE;
981af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        }
991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        ZipFile zip = new ZipFile(keystore);
1001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        try {
1011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            CertificateFactory cf = CertificateFactory.getInstance("X.509");
1021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            Enumeration<? extends ZipEntry> entries = zip.entries();
1031af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            while (entries.hasMoreElements()) {
1041af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                ZipEntry entry = entries.nextElement();
1051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                trusted.add(cf.generateCertificate(zip.getInputStream(entry)));
1061af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
1071af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        } finally {
1081af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            zip.close();
1091af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        }
1101af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        return trusted;
1111af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    }
1121af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
1131af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /**
1141af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Verify the cryptographic signature of a system update package
1151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * before installing it.  Note that the package is also verified
1161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * separately by the installer once the device is rebooted into
1171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * the recovery system.  This function will return only if the
1181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * package was successfully verified; otherwise it will throw an
1191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * exception.
1201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     *
1211af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Verification of a package can take significant time, so this
1221af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * function should not be called from a UI thread.
1231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     *
1241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @param packageFile  the package to be verified
1251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @param listener     an object to receive periodic progress
1261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * updates as verification proceeds.  May be null.
1271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @param deviceCertsZipFile  the zip file of certificates whose
1281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * public keys we will accept.  Verification succeeds if the
1291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * package is signed by the private key corresponding to any
1301af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * public key in this file.  May be null to use the system default
1311af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * file (currently "/system/etc/security/otacerts.zip").
1321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     *
1331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @throws IOException if there were any errors reading the
1341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * package or certs files.
1351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @throws GeneralSecurityException if verification failed
1361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     */
1371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    public static void verifyPackage(File packageFile,
1381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                                     ProgressListener listener,
1391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                                     File deviceCertsZipFile)
1401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        throws IOException, GeneralSecurityException {
1411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        long fileLen = packageFile.length();
1421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
1431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        RandomAccessFile raf = new RandomAccessFile(packageFile, "r");
1441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        try {
1451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            int lastPercent = 0;
1461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            long lastPublishTime = System.currentTimeMillis();
1471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (listener != null) {
1481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                listener.onProgress(lastPercent);
1491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
1501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
1511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            raf.seek(fileLen - 6);
1521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            byte[] footer = new byte[6];
1531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            raf.readFully(footer);
1541af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
1551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (footer[2] != (byte)0xff || footer[3] != (byte)0xff) {
1561af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                throw new SignatureException("no signature in file (no footer)");
1571af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
1581af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
1591af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8);
1601af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8);
1611af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            Log.v(TAG, String.format("comment size %d; signature start %d",
1621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                                     commentSize, signatureStart));
1631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
1641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            byte[] eocd = new byte[commentSize + 22];
1651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            raf.seek(fileLen - (commentSize + 22));
1661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            raf.readFully(eocd);
1671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
1681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // Check that we have found the start of the
1691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // end-of-central-directory record.
1701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (eocd[0] != (byte)0x50 || eocd[1] != (byte)0x4b ||
1711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                eocd[2] != (byte)0x05 || eocd[3] != (byte)0x06) {
1721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                throw new SignatureException("no signature in file (bad footer)");
1731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
1741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
1751af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            for (int i = 4; i < eocd.length-3; ++i) {
1761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                if (eocd[i  ] == (byte)0x50 && eocd[i+1] == (byte)0x4b &&
1771af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                    eocd[i+2] == (byte)0x05 && eocd[i+3] == (byte)0x06) {
1781af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                    throw new SignatureException("EOCD marker found after start of EOCD");
1791af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                }
1801af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
1811af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
1821af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // The following code is largely copied from
1831af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // JarUtils.verifySignature().  We could just *call* that
1841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // method here if that function didn't read the entire
1851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // input (ie, the whole OTA package) into memory just to
1861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // compute its message digest.
1871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
1881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            BerInputStream bis = new BerInputStream(
1891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart));
1901af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis);
1911af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            SignedData signedData = info.getSignedData();
1921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (signedData == null) {
1931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                throw new IOException("signedData is null");
1941af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
1951af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            Collection encCerts = signedData.getCertificates();
1961af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (encCerts.isEmpty()) {
1971af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                throw new IOException("encCerts is empty");
1981af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
1991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // Take the first certificate from the signature (packages
2001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // should contain only one).
2011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            Iterator it = encCerts.iterator();
2021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            X509Certificate cert = null;
2031af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (it.hasNext()) {
2041af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                cert = new X509CertImpl((org.apache.harmony.security.x509.Certificate)it.next());
2051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            } else {
2061af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                throw new SignatureException("signature contains no certificates");
2071af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
2081af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2091af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            List sigInfos = signedData.getSignerInfos();
2101af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            SignerInfo sigInfo;
2111af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (!sigInfos.isEmpty()) {
2121af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                sigInfo = (SignerInfo)sigInfos.get(0);
2131af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            } else {
2141af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                throw new IOException("no signer infos!");
2151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
2161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // Check that the public key of the certificate contained
2181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // in the package equals one of our trusted public keys.
2191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            HashSet<Certificate> trusted = getTrustedCerts(
2211af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile);
2221af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            PublicKey signatureKey = cert.getPublicKey();
2241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            boolean verified = false;
2251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            for (Certificate c : trusted) {
2261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                if (c.getPublicKey().equals(signatureKey)) {
2271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                    verified = true;
2281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                    break;
2291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                }
2301af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
2311af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (!verified) {
2321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                throw new SignatureException("signature doesn't match any trusted key");
2331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
2341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // The signature cert matches a trusted key.  Now verify that
2361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // the digest in the cert matches the actual file data.
2371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // The verifier in recovery *only* handles SHA1withRSA
2391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // signatures.  SignApk.java always uses SHA1withRSA, no
2401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // matter what the cert says to use.  Ignore
2411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // cert.getSigAlgName(), and instead use whatever
2421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // algorithm is used by the signature (which should be
2431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // SHA1withRSA).
2441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            String da = sigInfo.getdigestAlgorithm();
2461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            String dea = sigInfo.getDigestEncryptionAlgorithm();
2471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            String alg = null;
2481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (da == null || dea == null) {
2491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                // fall back to the cert algorithm if the sig one
2501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                // doesn't look right.
2511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                alg = cert.getSigAlgName();
2521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            } else {
2531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                alg = da + "with" + dea;
2541af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
2551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            Signature sig = Signature.getInstance(alg);
2561af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            sig.initVerify(cert);
2571af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2581af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // The signature covers all of the OTA package except the
2591af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            // archive comment and its 2-byte length.
2601af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            long toRead = fileLen - commentSize - 2;
2611af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            long soFar = 0;
2621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            raf.seek(0);
2631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            byte[] buffer = new byte[4096];
2641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            while (soFar < toRead) {
2651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                int size = buffer.length;
2661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                if (soFar + size > toRead) {
2671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                    size = (int)(toRead - soFar);
2681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                }
2691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                int read = raf.read(buffer, 0, size);
2701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                sig.update(buffer, 0, read);
2711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                soFar += read;
2721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                if (listener != null) {
2741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                    long now = System.currentTimeMillis();
2751af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                    int p = (int)(soFar * 100 / toRead);
2761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                    if (p > lastPercent &&
2771af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                        now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {
2781af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                        lastPercent = p;
2791af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                        lastPublishTime = now;
2801af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                        listener.onProgress(lastPercent);
2811af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                    }
2821af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                }
2831af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
2841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (listener != null) {
2851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                listener.onProgress(100);
2861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
2871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (!sig.verify(sigInfo.getEncryptedDigest())) {
2891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                throw new SignatureException("signature digest verification failed");
2901af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
2911af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        } finally {
2921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            raf.close();
2931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        }
2941af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    }
2951af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
2961af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /**
2971af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Reboots the device in order to install the given update
2981af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * package.
2991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Requires the {@link android.Manifest.permission#REBOOT}
3001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * and {@link android.Manifest.permission#ACCESS_CACHE_FILESYSTEM}
3011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * permissions.
3021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     *
3031af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @param context      the Context to use
3041af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @param packageFile  the update package to install.  Currently
3051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * must be on the /cache or /data partitions.
3061af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     *
3071af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @throws IOException  if writing the recovery command file
3081af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * fails, or if the reboot itself fails.
3091af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     */
3101af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    public static void installPackage(Context context, File packageFile)
3111af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        throws IOException {
3121af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        String filename = packageFile.getCanonicalPath();
3131af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
3141af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        if (filename.startsWith("/cache/")) {
3151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            filename = "CACHE:" + filename.substring(7);
3161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        } else if (filename.startsWith("/data/")) {
3171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            filename = "DATA:" + filename.substring(6);
3181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        } else {
3191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            throw new IllegalArgumentException(
3201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                "Must start with /cache or /data: " + filename);
3211af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        }
3221af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
3231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        String arg = "--update_package=" + filename;
3241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        bootCommand(context, arg);
3251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    }
3261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
3271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /**
3281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Reboots the device and wipes the user data partition.  This is
3291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * sometimes called a "factory reset", which is something of a
3301af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * misnomer because the system partition is not restored to its
3311af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * factory state.
3321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Requires the {@link android.Manifest.permission#REBOOT}
3331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * and {@link android.Manifest.permission#ACCESS_CACHE_FILESYSTEM}
3341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * permissions.
3351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     *
3361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @param context  the Context to use
3371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     *
3381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @throws IOException  if writing the recovery command file
3391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * fails, or if the reboot itself fails.
3401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     */
3411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    public static void rebootWipeUserData(Context context)
3421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        throws IOException {
3431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        bootCommand(context, "--wipe_data");
3441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    }
3451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
3461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /**
3471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Reboot into the recovery system to wipe the /data partition and toggle
3481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Encrypted File Systems on/off.
3491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @param extras to add to the RECOVERY_COMPLETED intent after rebooting.
3501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @throws IOException if something goes wrong.
3511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     *
3521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @hide
3531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     */
3541af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    public static void rebootToggleEFS(Context context, boolean efsEnabled)
3551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        throws IOException {
3561af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        if (efsEnabled) {
3571af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            bootCommand(context, "--set_encrypted_filesystem=on");
3581af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        } else {
3591af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            bootCommand(context, "--set_encrypted_filesystem=off");
3601af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        }
3611af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    }
3621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
3631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /**
3641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Reboot into the recovery system with the supplied argument.
3651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @param arg to pass to the recovery utility.
3661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @throws IOException if something goes wrong.
3671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     */
3681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    private static void bootCommand(Context context, String arg) throws IOException {
3691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        RECOVERY_DIR.mkdirs();  // In case we need it
3701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        COMMAND_FILE.delete();  // In case it's not writable
3711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        LOG_FILE.delete();
3721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
3731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        FileWriter command = new FileWriter(COMMAND_FILE);
3741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        try {
3751af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            command.write(arg);
3761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            command.write("\n");
3771af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        } finally {
3781af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            command.close();
3791af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        }
3801af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
3811af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        // Having written the command file, go ahead and reboot
3821af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
3831af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        pm.reboot("recovery");
3841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
3851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        throw new IOException("Reboot failed (no permissions?)");
3861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    }
3871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
3881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    /**
3891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * Called after booting to process and remove recovery-related files.
3901af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @return the log file from recovery, or null if none was found.
3911af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     *
3921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     * @hide
3931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker     */
3941af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    public static String handleAftermath() {
3951af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        // Record the tail of the LOG_FILE
3961af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        String log = null;
3971af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        try {
3981af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n");
3991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        } catch (FileNotFoundException e) {
4001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            Log.i(TAG, "No recovery log file");
4011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        } catch (IOException e) {
4021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            Log.e(TAG, "Error reading recovery log", e);
4031af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        }
4041af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
4051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        // Delete everything in RECOVERY_DIR
4061af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        String[] names = RECOVERY_DIR.list();
4071af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        for (int i = 0; names != null && i < names.length; i++) {
4081af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            File f = new File(RECOVERY_DIR, names[i]);
4091af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            if (!f.delete()) {
4101af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                Log.e(TAG, "Can't delete: " + f);
4111af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            } else {
4121af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker                Log.i(TAG, "Deleted: " + f);
4131af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker            }
4141af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        }
4151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
4161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker        return log;
4171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    }
4181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker
4191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker    private void RecoverySystem() { }  // Do not instantiate
4201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker}
421