RecoverySystem.java revision 183415e521d599ca5e33e5022fec5ec7dfe1c055
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 194ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parksimport android.content.BroadcastReceiver; 204ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parksimport android.content.Context; 214ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parksimport android.content.Intent; 22fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynoldsimport android.os.UserManager; 234ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parksimport android.util.Log; 244ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks 251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.ByteArrayInputStream; 261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.File; 271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.FileNotFoundException; 281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.FileWriter; 291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.IOException; 30e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongkerimport java.io.InputStream; 311af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.RandomAccessFile; 321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.GeneralSecurityException; 331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.PublicKey; 341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.Signature; 351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.SignatureException; 361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.cert.CertificateFactory; 371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.cert.X509Certificate; 381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.Enumeration; 391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.HashSet; 401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.Iterator; 411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.List; 42e33b4007ee56e843d5e99cfb92627425a551058dDoug Zongkerimport java.util.Locale; 431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.zip.ZipEntry; 441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.zip.ZipFile; 451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.asn1.BerInputStream; 471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.pkcs7.ContentInfo; 481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.pkcs7.SignedData; 491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.pkcs7.SignerInfo; 5027e549428eb5ae77a0ae536f778b204430f8c743Kenny Rootimport org.apache.harmony.security.x509.Certificate; 511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker/** 531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * RecoverySystem contains methods for interacting with the Android 541af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * recovery system (the separate partition that can be used to install 551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * system updates, wipe user data, etc.) 561af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 571af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerpublic class RecoverySystem { 581af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static final String TAG = "RecoverySystem"; 591af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 601af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 611af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Default location of zip file containing public keys (X509 621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * certs) authorized to sign OTA updates. 631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static final File DEFAULT_KEYSTORE = 651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker new File("/system/etc/security/otacerts.zip"); 661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** Send progress to listeners no more often than this (in ms). */ 681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500; 691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** Used to communicate with recovery. See bootable/recovery/recovery.c. */ 711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static File RECOVERY_DIR = new File("/cache/recovery"); 721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static File COMMAND_FILE = new File(RECOVERY_DIR, "command"); 731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static File LOG_FILE = new File(RECOVERY_DIR, "log"); 743d5040f8d474713a1e148b0d64f16bb0435d6388Doug Zongker private static String LAST_PREFIX = "last_"; 751af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Length limits for reading files. 77c95142d4a0ab7bebb899167da17c70c3196abbe4Dan Egnor private static int LOG_FILE_MAX_LENGTH = 64 * 1024; 781af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 791af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 801af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Interface definition for a callback to be invoked regularly as 811af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * verification proceeds. 821af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 831af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker public interface ProgressListener { 841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Called periodically as the verification progresses. 861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param progress the approximate percentage of the 881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * verification that has been completed, ranging from 0 891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * to 100 (inclusive). 901af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 911af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker public void onProgress(int progress); 921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 941af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** @return the set of certs that can be used to sign an OTA package. */ 9527e549428eb5ae77a0ae536f778b204430f8c743Kenny Root private static HashSet<X509Certificate> getTrustedCerts(File keystore) 961af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throws IOException, GeneralSecurityException { 9727e549428eb5ae77a0ae536f778b204430f8c743Kenny Root HashSet<X509Certificate> trusted = new HashSet<X509Certificate>(); 981af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (keystore == null) { 991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker keystore = DEFAULT_KEYSTORE; 1001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker ZipFile zip = new ZipFile(keystore); 1021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker try { 1031af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker CertificateFactory cf = CertificateFactory.getInstance("X.509"); 1041af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Enumeration<? extends ZipEntry> entries = zip.entries(); 1051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker while (entries.hasMoreElements()) { 1061af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker ZipEntry entry = entries.nextElement(); 107e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongker InputStream is = zip.getInputStream(entry); 108e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongker try { 10927e549428eb5ae77a0ae536f778b204430f8c743Kenny Root trusted.add((X509Certificate) cf.generateCertificate(is)); 110e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongker } finally { 111e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongker is.close(); 112e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongker } 1131af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1141af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } finally { 1151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker zip.close(); 1161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker return trusted; 1181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 1211af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Verify the cryptographic signature of a system update package 1221af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * before installing it. Note that the package is also verified 1231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * separately by the installer once the device is rebooted into 1241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * the recovery system. This function will return only if the 1251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * package was successfully verified; otherwise it will throw an 1261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * exception. 1271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 1281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Verification of a package can take significant time, so this 129cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker * function should not be called from a UI thread. Interrupting 130cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker * the thread while this function is in progress will result in a 131cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker * SecurityException being thrown (and the thread's interrupt flag 132cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker * will be cleared). 1331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 1341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param packageFile the package to be verified 1351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param listener an object to receive periodic progress 1361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * updates as verification proceeds. May be null. 1371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param deviceCertsZipFile the zip file of certificates whose 1381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * public keys we will accept. Verification succeeds if the 1391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * package is signed by the private key corresponding to any 1401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * public key in this file. May be null to use the system default 1411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * file (currently "/system/etc/security/otacerts.zip"). 1421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 1431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws IOException if there were any errors reading the 1441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * package or certs files. 1451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws GeneralSecurityException if verification failed 1461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 1471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker public static void verifyPackage(File packageFile, 1481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker ProgressListener listener, 1491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker File deviceCertsZipFile) 1501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throws IOException, GeneralSecurityException { 1511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker long fileLen = packageFile.length(); 1521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker RandomAccessFile raf = new RandomAccessFile(packageFile, "r"); 1541af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker try { 1551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int lastPercent = 0; 1561af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker long lastPublishTime = System.currentTimeMillis(); 1571af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (listener != null) { 1581af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker listener.onProgress(lastPercent); 1591af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1601af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1611af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.seek(fileLen - 6); 1621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker byte[] footer = new byte[6]; 1631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.readFully(footer); 1641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (footer[2] != (byte)0xff || footer[3] != (byte)0xff) { 1661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("no signature in file (no footer)"); 1671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8); 1701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8); 1711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker byte[] eocd = new byte[commentSize + 22]; 1731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.seek(fileLen - (commentSize + 22)); 1741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.readFully(eocd); 1751af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Check that we have found the start of the 1771af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // end-of-central-directory record. 1781af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (eocd[0] != (byte)0x50 || eocd[1] != (byte)0x4b || 1791af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker eocd[2] != (byte)0x05 || eocd[3] != (byte)0x06) { 1801af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("no signature in file (bad footer)"); 1811af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1821af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1831af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker for (int i = 4; i < eocd.length-3; ++i) { 1841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (eocd[i ] == (byte)0x50 && eocd[i+1] == (byte)0x4b && 1851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker eocd[i+2] == (byte)0x05 && eocd[i+3] == (byte)0x06) { 1861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("EOCD marker found after start of EOCD"); 1871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1901af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // The following code is largely copied from 1911af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // JarUtils.verifySignature(). We could just *call* that 1921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // method here if that function didn't read the entire 1931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // input (ie, the whole OTA package) into memory just to 1941af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // compute its message digest. 1951af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1961af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker BerInputStream bis = new BerInputStream( 1971af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart)); 1981af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis); 1991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker SignedData signedData = info.getSignedData(); 2001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (signedData == null) { 2011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new IOException("signedData is null"); 2021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 20327e549428eb5ae77a0ae536f778b204430f8c743Kenny Root List<Certificate> encCerts = signedData.getCertificates(); 2041af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (encCerts.isEmpty()) { 2051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new IOException("encCerts is empty"); 2061af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2071af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Take the first certificate from the signature (packages 2081af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // should contain only one). 20927e549428eb5ae77a0ae536f778b204430f8c743Kenny Root Iterator<Certificate> it = encCerts.iterator(); 2101af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker X509Certificate cert = null; 2111af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (it.hasNext()) { 21227e549428eb5ae77a0ae536f778b204430f8c743Kenny Root CertificateFactory cf = CertificateFactory.getInstance("X.509"); 21327e549428eb5ae77a0ae536f778b204430f8c743Kenny Root InputStream is = new ByteArrayInputStream(it.next().getEncoded()); 21427e549428eb5ae77a0ae536f778b204430f8c743Kenny Root cert = (X509Certificate) cf.generateCertificate(is); 2151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } else { 2161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("signature contains no certificates"); 2171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 21927e549428eb5ae77a0ae536f778b204430f8c743Kenny Root List<SignerInfo> sigInfos = signedData.getSignerInfos(); 2201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker SignerInfo sigInfo; 2211af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (!sigInfos.isEmpty()) { 2221af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker sigInfo = (SignerInfo)sigInfos.get(0); 2231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } else { 2241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new IOException("no signer infos!"); 2251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 2271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Check that the public key of the certificate contained 2281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // in the package equals one of our trusted public keys. 2291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 23027e549428eb5ae77a0ae536f778b204430f8c743Kenny Root HashSet<X509Certificate> trusted = getTrustedCerts( 2311af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile); 2321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 2331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker PublicKey signatureKey = cert.getPublicKey(); 2341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker boolean verified = false; 23527e549428eb5ae77a0ae536f778b204430f8c743Kenny Root for (X509Certificate c : trusted) { 2361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (c.getPublicKey().equals(signatureKey)) { 2371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker verified = true; 2381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker break; 2391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (!verified) { 2421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("signature doesn't match any trusted key"); 2431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 2451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // The signature cert matches a trusted key. Now verify that 2461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // the digest in the cert matches the actual file data. 2471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 248c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // The verifier in recovery only handles SHA1withRSA and 249c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // SHA256withRSA signatures. SignApk chooses which to use 250c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // based on the signature algorithm of the cert: 251c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // 252c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // "SHA256withRSA" cert -> "SHA256withRSA" signature 253c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // "SHA1withRSA" cert -> "SHA1withRSA" signature 254c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // "MD5withRSA" cert -> "SHA1withRSA" signature (for backwards compatibility) 255c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // any other cert -> SignApk fails 256c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // 257c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // Here we ignore whatever the cert says, and instead use 258c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // whatever algorithm is used by the signature. 2591af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 26066e40c36b3145f4d2f3ddd547bd66f27b12f6324Jesse Wilson String da = sigInfo.getDigestAlgorithm(); 2611af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker String dea = sigInfo.getDigestEncryptionAlgorithm(); 2621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker String alg = null; 2631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (da == null || dea == null) { 2641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // fall back to the cert algorithm if the sig one 2651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // doesn't look right. 2661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker alg = cert.getSigAlgName(); 2671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } else { 2681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker alg = da + "with" + dea; 2691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Signature sig = Signature.getInstance(alg); 2711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker sig.initVerify(cert); 2721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 2731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // The signature covers all of the OTA package except the 2741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // archive comment and its 2-byte length. 2751af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker long toRead = fileLen - commentSize - 2; 2761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker long soFar = 0; 2771af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.seek(0); 2781af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker byte[] buffer = new byte[4096]; 279cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker boolean interrupted = false; 2801af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker while (soFar < toRead) { 281cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker interrupted = Thread.interrupted(); 282cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker if (interrupted) break; 2831af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int size = buffer.length; 2841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (soFar + size > toRead) { 2851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker size = (int)(toRead - soFar); 2861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int read = raf.read(buffer, 0, size); 2881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker sig.update(buffer, 0, read); 2891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker soFar += read; 2901af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 2911af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (listener != null) { 2921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker long now = System.currentTimeMillis(); 2931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int p = (int)(soFar * 100 / toRead); 2941af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (p > lastPercent && 2951af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) { 2961af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker lastPercent = p; 2971af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker lastPublishTime = now; 2981af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker listener.onProgress(lastPercent); 2991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (listener != null) { 3031af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker listener.onProgress(100); 3041af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 306cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker if (interrupted) { 307cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker throw new SignatureException("verification was interrupted"); 308cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker } 309cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker 3101af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (!sig.verify(sigInfo.getEncryptedDigest())) { 3111af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("signature digest verification failed"); 3121af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3131af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } finally { 3141af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.close(); 3151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 3181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 3191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Reboots the device in order to install the given update 3201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * package. 32164010e835057d4b85e2d90cf75cc562f3b5eb552Jeff Brown * Requires the {@link android.Manifest.permission#REBOOT} permission. 3221af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 3231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param context the Context to use 3244baf641e7d96375eba3f9a3aff5400b9e3d28cd6Doug Zongker * @param packageFile the update package to install. Must be on 3254baf641e7d96375eba3f9a3aff5400b9e3d28cd6Doug Zongker * a partition mountable by recovery. (The set of partitions 3264baf641e7d96375eba3f9a3aff5400b9e3d28cd6Doug Zongker * known to recovery may vary from device to device. Generally, 3274baf641e7d96375eba3f9a3aff5400b9e3d28cd6Doug Zongker * /cache and /data are safe.) 3281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 3291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws IOException if writing the recovery command file 3301af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * fails, or if the reboot itself fails. 3311af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 3321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker public static void installPackage(Context context, File packageFile) 3331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throws IOException { 3341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker String filename = packageFile.getCanonicalPath(); 3351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); 336e33b4007ee56e843d5e99cfb92627425a551058dDoug Zongker String arg = "--update_package=" + filename + 337e33b4007ee56e843d5e99cfb92627425a551058dDoug Zongker "\n--locale=" + Locale.getDefault().toString(); 3381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker bootCommand(context, arg); 3391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 3411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 342cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * Reboots the device and wipes the user data and cache 343cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * partitions. This is sometimes called a "factory reset", which 344cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * is something of a misnomer because the system partition is not 345cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * restored to its factory state. Requires the 346cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * {@link android.Manifest.permission#REBOOT} permission. 3471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 3481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param context the Context to use 3491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 3501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws IOException if writing the recovery command file 3511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * fails, or if the reboot itself fails. 352fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds * @throws SecurityException if the current user is not allowed to wipe data. 3531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 3544ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks public static void rebootWipeUserData(Context context) throws IOException { 355cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker rebootWipeUserData(context, false); 356cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker } 357cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker 358cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker /** 359cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * Reboots the device and wipes the user data and cache 360cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * partitions. This is sometimes called a "factory reset", which 361cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * is something of a misnomer because the system partition is not 362cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * restored to its factory state. Requires the 363cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * {@link android.Manifest.permission#REBOOT} permission. 364cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * 365cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * @param context the Context to use 366cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * @param shutdown if true, the device will be powered down after 367cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * the wipe completes, rather than being rebooted 368cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * back to the regular system. 369cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * 370cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * @throws IOException if writing the recovery command file 371cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * fails, or if the reboot itself fails. 372fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds * @throws SecurityException if the current user is not allowed to wipe data. 373cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * 374cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * @hide 375cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker */ 376cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker public static void rebootWipeUserData(Context context, boolean shutdown) 377cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker throws IOException { 378fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 379fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds if (um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) { 380fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds throw new SecurityException("Wiping data is not allowed for this user."); 381fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds } 3824ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks final ConditionVariable condition = new ConditionVariable(); 3834ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks 3844ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION"); 385e27ae55d6db8b5f80fb76c3e7637a834a14f5f0dChristopher Tate intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 3865ac72a29593ab9a20337a2225df52bdf4754be02Dianne Hackborn context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER, 3875ac72a29593ab9a20337a2225df52bdf4754be02Dianne Hackborn android.Manifest.permission.MASTER_CLEAR, 3884ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks new BroadcastReceiver() { 3894ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks @Override 3904ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks public void onReceive(Context context, Intent intent) { 3914ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks condition.open(); 3924ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks } 3934ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks }, null, 0, null, null); 3944ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks 3954ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks // Block until the ordered broadcast has completed. 3964ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks condition.block(); 3974ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks 398cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker String shutdownArg = ""; 399cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker if (shutdown) { 400cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker shutdownArg = "--shutdown_after\n"; 401cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker } 402cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker 403cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker bootCommand(context, shutdownArg + "--wipe_data\n--locale=" + 404cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker Locale.getDefault().toString()); 4051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4061af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4071af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 40833651201375d3670672964503994c410b8eeed7bDoug Zongker * Reboot into the recovery system to wipe the /cache partition. 4091af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws IOException if something goes wrong. 4101af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 41133651201375d3670672964503994c410b8eeed7bDoug Zongker public static void rebootWipeCache(Context context) throws IOException { 412e33b4007ee56e843d5e99cfb92627425a551058dDoug Zongker bootCommand(context, "--wipe_cache\n--locale=" + Locale.getDefault().toString()); 4131af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4141af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 4161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Reboot into the recovery system with the supplied argument. 4171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param arg to pass to the recovery utility. 4181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws IOException if something goes wrong. 4191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 4201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static void bootCommand(Context context, String arg) throws IOException { 4211af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker RECOVERY_DIR.mkdirs(); // In case we need it 4221af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker COMMAND_FILE.delete(); // In case it's not writable 4231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker LOG_FILE.delete(); 4241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker FileWriter command = new FileWriter(COMMAND_FILE); 4261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker try { 4271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker command.write(arg); 4281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker command.write("\n"); 4291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } finally { 4301af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker command.close(); 4311af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Having written the command file, go ahead and reboot 4341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 435183415e521d599ca5e33e5022fec5ec7dfe1c055Doug Zongker pm.reboot(PowerManager.REBOOT_RECOVERY); 4361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new IOException("Reboot failed (no permissions?)"); 4381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 4411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Called after booting to process and remove recovery-related files. 4421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @return the log file from recovery, or null if none was found. 4431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 4441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @hide 4451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 4461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker public static String handleAftermath() { 4471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Record the tail of the LOG_FILE 4481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker String log = null; 4491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker try { 4501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n"); 4511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } catch (FileNotFoundException e) { 4521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Log.i(TAG, "No recovery log file"); 4531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } catch (IOException e) { 4541af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Log.e(TAG, "Error reading recovery log", e); 4551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4561af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4573d5040f8d474713a1e148b0d64f16bb0435d6388Doug Zongker // Delete everything in RECOVERY_DIR except those beginning 4583d5040f8d474713a1e148b0d64f16bb0435d6388Doug Zongker // with LAST_PREFIX 4591af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker String[] names = RECOVERY_DIR.list(); 4601af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker for (int i = 0; names != null && i < names.length; i++) { 4613d5040f8d474713a1e148b0d64f16bb0435d6388Doug Zongker if (names[i].startsWith(LAST_PREFIX)) continue; 4621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker File f = new File(RECOVERY_DIR, names[i]); 4631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (!f.delete()) { 4641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Log.e(TAG, "Can't delete: " + f); 4651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } else { 4661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Log.i(TAG, "Deleted: " + f); 4671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker return log; 4711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private void RecoverySystem() { } // Do not instantiate 4741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker} 475