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; 23004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkeyimport android.text.TextUtils; 244ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parksimport android.util.Log; 254ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks 261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.ByteArrayInputStream; 271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.File; 281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.FileNotFoundException; 291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.FileWriter; 301af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.IOException; 31e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongkerimport java.io.InputStream; 321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.io.RandomAccessFile; 331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.GeneralSecurityException; 341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.PublicKey; 351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.Signature; 361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.SignatureException; 371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.cert.CertificateFactory; 381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.security.cert.X509Certificate; 391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.Enumeration; 401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.HashSet; 411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.Iterator; 421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.List; 43e33b4007ee56e843d5e99cfb92627425a551058dDoug Zongkerimport java.util.Locale; 441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.zip.ZipEntry; 451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport java.util.zip.ZipFile; 461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.asn1.BerInputStream; 481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.pkcs7.ContentInfo; 491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.pkcs7.SignedData; 501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerimport org.apache.harmony.security.pkcs7.SignerInfo; 5127e549428eb5ae77a0ae536f778b204430f8c743Kenny Rootimport org.apache.harmony.security.x509.Certificate; 521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker/** 541af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * RecoverySystem contains methods for interacting with the Android 551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * recovery system (the separate partition that can be used to install 561af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * system updates, wipe user data, etc.) 571af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 581af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongkerpublic class RecoverySystem { 591af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static final String TAG = "RecoverySystem"; 601af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 611af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Default location of zip file containing public keys (X509 631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * certs) authorized to sign OTA updates. 641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static final File DEFAULT_KEYSTORE = 661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker new File("/system/etc/security/otacerts.zip"); 671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** Send progress to listeners no more often than this (in ms). */ 691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500; 701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** Used to communicate with recovery. See bootable/recovery/recovery.c. */ 721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static File RECOVERY_DIR = new File("/cache/recovery"); 731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static File COMMAND_FILE = new File(RECOVERY_DIR, "command"); 741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private static File LOG_FILE = new File(RECOVERY_DIR, "log"); 753d5040f8d474713a1e148b0d64f16bb0435d6388Doug Zongker private static String LAST_PREFIX = "last_"; 761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 771af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Length limits for reading files. 78c95142d4a0ab7bebb899167da17c70c3196abbe4Dan Egnor private static int LOG_FILE_MAX_LENGTH = 64 * 1024; 791af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 801af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 811af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Interface definition for a callback to be invoked regularly as 821af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * verification proceeds. 831af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker public interface ProgressListener { 851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Called periodically as the verification progresses. 871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param progress the approximate percentage of the 891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * verification that has been completed, ranging from 0 901af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * to 100 (inclusive). 911af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker public void onProgress(int progress); 931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 941af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 951af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** @return the set of certs that can be used to sign an OTA package. */ 9627e549428eb5ae77a0ae536f778b204430f8c743Kenny Root private static HashSet<X509Certificate> getTrustedCerts(File keystore) 971af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throws IOException, GeneralSecurityException { 9827e549428eb5ae77a0ae536f778b204430f8c743Kenny Root HashSet<X509Certificate> trusted = new HashSet<X509Certificate>(); 991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (keystore == null) { 1001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker keystore = DEFAULT_KEYSTORE; 1011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker ZipFile zip = new ZipFile(keystore); 1031af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker try { 1041af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker CertificateFactory cf = CertificateFactory.getInstance("X.509"); 1051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Enumeration<? extends ZipEntry> entries = zip.entries(); 1061af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker while (entries.hasMoreElements()) { 1071af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker ZipEntry entry = entries.nextElement(); 108e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongker InputStream is = zip.getInputStream(entry); 109e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongker try { 11027e549428eb5ae77a0ae536f778b204430f8c743Kenny Root trusted.add((X509Certificate) cf.generateCertificate(is)); 111e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongker } finally { 112e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongker is.close(); 113e2d58e95a09590a63f1c597bb808b925bcab9a69Doug Zongker } 1141af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } finally { 1161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker zip.close(); 1171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker return trusted; 1191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1211af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 1221af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Verify the cryptographic signature of a system update package 1231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * before installing it. Note that the package is also verified 1241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * separately by the installer once the device is rebooted into 1251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * the recovery system. This function will return only if the 1261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * package was successfully verified; otherwise it will throw an 1271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * exception. 1281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 1291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Verification of a package can take significant time, so this 130cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker * function should not be called from a UI thread. Interrupting 131cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker * the thread while this function is in progress will result in a 132cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker * SecurityException being thrown (and the thread's interrupt flag 133cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker * will be cleared). 1341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 1351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param packageFile the package to be verified 1361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param listener an object to receive periodic progress 1371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * updates as verification proceeds. May be null. 1381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param deviceCertsZipFile the zip file of certificates whose 1391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * public keys we will accept. Verification succeeds if the 1401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * package is signed by the private key corresponding to any 1411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * public key in this file. May be null to use the system default 1421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * file (currently "/system/etc/security/otacerts.zip"). 1431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 1441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws IOException if there were any errors reading the 1451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * package or certs files. 1461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws GeneralSecurityException if verification failed 1471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 1481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker public static void verifyPackage(File packageFile, 1491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker ProgressListener listener, 1501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker File deviceCertsZipFile) 1511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throws IOException, GeneralSecurityException { 1521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker long fileLen = packageFile.length(); 1531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1541af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker RandomAccessFile raf = new RandomAccessFile(packageFile, "r"); 1551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker try { 1561af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int lastPercent = 0; 1571af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker long lastPublishTime = System.currentTimeMillis(); 1581af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (listener != null) { 1591af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker listener.onProgress(lastPercent); 1601af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1611af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.seek(fileLen - 6); 1631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker byte[] footer = new byte[6]; 1641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.readFully(footer); 1651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (footer[2] != (byte)0xff || footer[3] != (byte)0xff) { 1671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("no signature in file (no footer)"); 1681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8); 1711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8); 1721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker byte[] eocd = new byte[commentSize + 22]; 1741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.seek(fileLen - (commentSize + 22)); 1751af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.readFully(eocd); 1761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1771af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Check that we have found the start of the 1781af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // end-of-central-directory record. 1791af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (eocd[0] != (byte)0x50 || eocd[1] != (byte)0x4b || 1801af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker eocd[2] != (byte)0x05 || eocd[3] != (byte)0x06) { 1811af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("no signature in file (bad footer)"); 1821af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1831af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker for (int i = 4; i < eocd.length-3; ++i) { 1851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (eocd[i ] == (byte)0x50 && eocd[i+1] == (byte)0x4b && 1861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker eocd[i+2] == (byte)0x05 && eocd[i+3] == (byte)0x06) { 1871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("EOCD marker found after start of EOCD"); 1881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 1901af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1911af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // The following code is largely copied from 1921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // JarUtils.verifySignature(). We could just *call* that 1931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // method here if that function didn't read the entire 1941af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // input (ie, the whole OTA package) into memory just to 1951af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // compute its message digest. 1961af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 1971af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker BerInputStream bis = new BerInputStream( 1981af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart)); 1991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis); 2001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker SignedData signedData = info.getSignedData(); 2011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (signedData == null) { 2021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new IOException("signedData is null"); 2031af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 20427e549428eb5ae77a0ae536f778b204430f8c743Kenny Root List<Certificate> encCerts = signedData.getCertificates(); 2051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (encCerts.isEmpty()) { 2061af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new IOException("encCerts is empty"); 2071af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2081af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Take the first certificate from the signature (packages 2091af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // should contain only one). 21027e549428eb5ae77a0ae536f778b204430f8c743Kenny Root Iterator<Certificate> it = encCerts.iterator(); 2111af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker X509Certificate cert = null; 2121af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (it.hasNext()) { 21327e549428eb5ae77a0ae536f778b204430f8c743Kenny Root CertificateFactory cf = CertificateFactory.getInstance("X.509"); 21427e549428eb5ae77a0ae536f778b204430f8c743Kenny Root InputStream is = new ByteArrayInputStream(it.next().getEncoded()); 21527e549428eb5ae77a0ae536f778b204430f8c743Kenny Root cert = (X509Certificate) cf.generateCertificate(is); 2161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } else { 2171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("signature contains no certificates"); 2181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 22027e549428eb5ae77a0ae536f778b204430f8c743Kenny Root List<SignerInfo> sigInfos = signedData.getSignerInfos(); 2211af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker SignerInfo sigInfo; 2221af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (!sigInfos.isEmpty()) { 2231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker sigInfo = (SignerInfo)sigInfos.get(0); 2241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } else { 2251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new IOException("no signer infos!"); 2261af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 2281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Check that the public key of the certificate contained 2291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // in the package equals one of our trusted public keys. 2301af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 23127e549428eb5ae77a0ae536f778b204430f8c743Kenny Root HashSet<X509Certificate> trusted = getTrustedCerts( 2321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile); 2331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 2341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker PublicKey signatureKey = cert.getPublicKey(); 2351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker boolean verified = false; 23627e549428eb5ae77a0ae536f778b204430f8c743Kenny Root for (X509Certificate c : trusted) { 2371af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (c.getPublicKey().equals(signatureKey)) { 2381af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker verified = true; 2391af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker break; 2401af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (!verified) { 2431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("signature doesn't match any trusted key"); 2441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 2461af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // The signature cert matches a trusted key. Now verify that 2471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // the digest in the cert matches the actual file data. 2481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 249c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // The verifier in recovery only handles SHA1withRSA and 250c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // SHA256withRSA signatures. SignApk chooses which to use 251c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // based on the signature algorithm of the cert: 252c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // 253c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // "SHA256withRSA" cert -> "SHA256withRSA" signature 254c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // "SHA1withRSA" cert -> "SHA1withRSA" signature 255c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // "MD5withRSA" cert -> "SHA1withRSA" signature (for backwards compatibility) 256c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // any other cert -> SignApk fails 257c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // 258c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // Here we ignore whatever the cert says, and instead use 259c9a9ffc5264c2c9405b7b98e1e993279e10f994fDoug Zongker // whatever algorithm is used by the signature. 2601af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 26166e40c36b3145f4d2f3ddd547bd66f27b12f6324Jesse Wilson String da = sigInfo.getDigestAlgorithm(); 2621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker String dea = sigInfo.getDigestEncryptionAlgorithm(); 2631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker String alg = null; 2641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (da == null || dea == null) { 2651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // fall back to the cert algorithm if the sig one 2661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // doesn't look right. 2671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker alg = cert.getSigAlgName(); 2681af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } else { 2691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker alg = da + "with" + dea; 2701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Signature sig = Signature.getInstance(alg); 2721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker sig.initVerify(cert); 2731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 2741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // The signature covers all of the OTA package except the 2751af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // archive comment and its 2-byte length. 2761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker long toRead = fileLen - commentSize - 2; 2771af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker long soFar = 0; 2781af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.seek(0); 2791af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker byte[] buffer = new byte[4096]; 280cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker boolean interrupted = false; 2811af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker while (soFar < toRead) { 282cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker interrupted = Thread.interrupted(); 283cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker if (interrupted) break; 2841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int size = buffer.length; 2851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (soFar + size > toRead) { 2861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker size = (int)(toRead - soFar); 2871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 2881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int read = raf.read(buffer, 0, size); 2891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker sig.update(buffer, 0, read); 2901af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker soFar += read; 2911af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 2921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (listener != null) { 2931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker long now = System.currentTimeMillis(); 2941af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker int p = (int)(soFar * 100 / toRead); 2951af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (p > lastPercent && 2961af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) { 2971af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker lastPercent = p; 2981af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker lastPublishTime = now; 2991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker listener.onProgress(lastPercent); 3001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3031af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (listener != null) { 3041af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker listener.onProgress(100); 3051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3061af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 307cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker if (interrupted) { 308cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker throw new SignatureException("verification was interrupted"); 309cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker } 310cb95657326add53f81cd2f8a0ae0a1a0527ae799Doug Zongker 3111af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (!sig.verify(sigInfo.getEncryptedDigest())) { 3121af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new SignatureException("signature digest verification failed"); 3131af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3141af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } finally { 3151af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker raf.close(); 3161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3181af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 3191af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 3201af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Reboots the device in order to install the given update 3211af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * package. 32264010e835057d4b85e2d90cf75cc562f3b5eb552Jeff Brown * Requires the {@link android.Manifest.permission#REBOOT} permission. 3231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 3241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param context the Context to use 3254baf641e7d96375eba3f9a3aff5400b9e3d28cd6Doug Zongker * @param packageFile the update package to install. Must be on 3264baf641e7d96375eba3f9a3aff5400b9e3d28cd6Doug Zongker * a partition mountable by recovery. (The set of partitions 3274baf641e7d96375eba3f9a3aff5400b9e3d28cd6Doug Zongker * known to recovery may vary from device to device. Generally, 3284baf641e7d96375eba3f9a3aff5400b9e3d28cd6Doug Zongker * /cache and /data are safe.) 3291af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 3301af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws IOException if writing the recovery command file 3311af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * fails, or if the reboot itself fails. 3321af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 3331af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker public static void installPackage(Context context, File packageFile) 3341af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throws IOException { 3351af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker String filename = packageFile.getCanonicalPath(); 3361af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); 337004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey 338004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey final String filenameArg = "--update_package=" + filename; 339004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey final String localeArg = "--locale=" + Locale.getDefault().toString(); 340004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey bootCommand(context, filenameArg, localeArg); 3411af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 3421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 3431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 344cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * Reboots the device and wipes the user data and cache 345cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * partitions. This is sometimes called a "factory reset", which 346cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * is something of a misnomer because the system partition is not 347cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * restored to its factory state. Requires the 348cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * {@link android.Manifest.permission#REBOOT} permission. 3491af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 3501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @param context the Context to use 3511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 3521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws IOException if writing the recovery command file 3531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * fails, or if the reboot itself fails. 354fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds * @throws SecurityException if the current user is not allowed to wipe data. 3551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 3564ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks public static void rebootWipeUserData(Context context) throws IOException { 357004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey rebootWipeUserData(context, false, context.getPackageName()); 358004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey } 359004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey 360004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey /** {@hide} */ 361004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey public static void rebootWipeUserData(Context context, String reason) throws IOException { 362004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey rebootWipeUserData(context, false, reason); 363004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey } 364004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey 365004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey /** {@hide} */ 366004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey public static void rebootWipeUserData(Context context, boolean shutdown) 367004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey throws IOException { 368004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey rebootWipeUserData(context, shutdown, context.getPackageName()); 369cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker } 370cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker 371cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker /** 372cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * Reboots the device and wipes the user data and cache 373cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * partitions. This is sometimes called a "factory reset", which 374cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * is something of a misnomer because the system partition is not 375cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * restored to its factory state. Requires the 376cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * {@link android.Manifest.permission#REBOOT} permission. 377cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * 378cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * @param context the Context to use 379cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * @param shutdown if true, the device will be powered down after 380cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * the wipe completes, rather than being rebooted 381cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * back to the regular system. 382cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * 383cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * @throws IOException if writing the recovery command file 384cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * fails, or if the reboot itself fails. 385fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds * @throws SecurityException if the current user is not allowed to wipe data. 386cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * 387cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker * @hide 388cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker */ 389004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey public static void rebootWipeUserData(Context context, boolean shutdown, String reason) 390004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey throws IOException { 391fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 392fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds if (um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) { 393fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds throw new SecurityException("Wiping data is not allowed for this user."); 394fe0538098403b49ebd9219bf77236471bb5ca63bJulia Reynolds } 3954ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks final ConditionVariable condition = new ConditionVariable(); 3964ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks 3974ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION"); 398e27ae55d6db8b5f80fb76c3e7637a834a14f5f0dChristopher Tate intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 3995ac72a29593ab9a20337a2225df52bdf4754be02Dianne Hackborn context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER, 4005ac72a29593ab9a20337a2225df52bdf4754be02Dianne Hackborn android.Manifest.permission.MASTER_CLEAR, 4014ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks new BroadcastReceiver() { 4024ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks @Override 4034ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks public void onReceive(Context context, Intent intent) { 4044ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks condition.open(); 4054ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks } 4064ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks }, null, 0, null, null); 4074ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks 4084ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks // Block until the ordered broadcast has completed. 4094ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks condition.block(); 4104ca74dc4c2e0c68803e777cf47ed8e01b8e8444eJason parks 411004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey String shutdownArg = null; 412cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker if (shutdown) { 413004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey shutdownArg = "--shutdown_after"; 414004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey } 415004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey 416004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey String reasonArg = null; 417004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey if (!TextUtils.isEmpty(reason)) { 418004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey reasonArg = "--reason=" + sanitizeArg(reason); 419cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker } 420cdf008883921c2eb7daf10c82687e9a36461eb16Doug Zongker 421004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey final String localeArg = "--locale=" + Locale.getDefault().toString(); 422004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg); 4231af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4241af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4251af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 42633651201375d3670672964503994c410b8eeed7bDoug Zongker * Reboot into the recovery system to wipe the /cache partition. 4271af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws IOException if something goes wrong. 4281af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 42933651201375d3670672964503994c410b8eeed7bDoug Zongker public static void rebootWipeCache(Context context) throws IOException { 430004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey rebootWipeCache(context, context.getPackageName()); 431004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey } 432004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey 433004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey /** {@hide} */ 434004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey public static void rebootWipeCache(Context context, String reason) throws IOException { 435004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey String reasonArg = null; 436004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey if (!TextUtils.isEmpty(reason)) { 437004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey reasonArg = "--reason=" + sanitizeArg(reason); 438004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey } 439004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey 440004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey final String localeArg = "--locale=" + Locale.getDefault().toString(); 441004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey bootCommand(context, "--wipe_cache", reasonArg, localeArg); 4421af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4431af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4441af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 4451af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Reboot into the recovery system with the supplied argument. 446004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey * @param args to pass to the recovery utility. 4471af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @throws IOException if something goes wrong. 4481af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 449004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey private static void bootCommand(Context context, String... args) throws IOException { 4501af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker RECOVERY_DIR.mkdirs(); // In case we need it 4511af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker COMMAND_FILE.delete(); // In case it's not writable 4521af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker LOG_FILE.delete(); 4531af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4541af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker FileWriter command = new FileWriter(COMMAND_FILE); 4551af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker try { 456004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey for (String arg : args) { 457004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey if (!TextUtils.isEmpty(arg)) { 458004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey command.write(arg); 459004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey command.write("\n"); 460004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey } 461004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey } 4621af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } finally { 4631af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker command.close(); 4641af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4651af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4661af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Having written the command file, go ahead and reboot 4671af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 468183415e521d599ca5e33e5022fec5ec7dfe1c055Doug Zongker pm.reboot(PowerManager.REBOOT_RECOVERY); 4691af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4701af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker throw new IOException("Reboot failed (no permissions?)"); 4711af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4721af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4731af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker /** 4741af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * Called after booting to process and remove recovery-related files. 4751af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @return the log file from recovery, or null if none was found. 4761af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * 4771af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker * @hide 4781af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker */ 4791af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker public static String handleAftermath() { 4801af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker // Record the tail of the LOG_FILE 4811af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker String log = null; 4821af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker try { 4831af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n"); 4841af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } catch (FileNotFoundException e) { 4851af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Log.i(TAG, "No recovery log file"); 4861af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } catch (IOException e) { 4871af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Log.e(TAG, "Error reading recovery log", e); 4881af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 4891af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 4903d5040f8d474713a1e148b0d64f16bb0435d6388Doug Zongker // Delete everything in RECOVERY_DIR except those beginning 4913d5040f8d474713a1e148b0d64f16bb0435d6388Doug Zongker // with LAST_PREFIX 4921af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker String[] names = RECOVERY_DIR.list(); 4931af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker for (int i = 0; names != null && i < names.length; i++) { 4943d5040f8d474713a1e148b0d64f16bb0435d6388Doug Zongker if (names[i].startsWith(LAST_PREFIX)) continue; 4951af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker File f = new File(RECOVERY_DIR, names[i]); 4961af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker if (!f.delete()) { 4971af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Log.e(TAG, "Can't delete: " + f); 4981af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } else { 4991af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker Log.i(TAG, "Deleted: " + f); 5001af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 5011af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 5021af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 5031af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker return log; 5041af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker } 5051af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker 506004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey /** 507004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey * Internally, recovery treats each line of the command file as a separate 508004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey * argv, so we only need to protect against newlines and nulls. 509004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey */ 510004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey private static String sanitizeArg(String arg) { 511004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey arg = arg.replace('\0', '?'); 512004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey arg = arg.replace('\n', '?'); 513004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey return arg; 514004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey } 515004a4b20f8d3116e6a711525960d433fcfea4ee4Jeff Sharkey 5161af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker private void RecoverySystem() { } // Do not instantiate 5171af33d0ddc2f50ade146e4d48e2feb6f1d553427Doug Zongker} 518