Android 上层RecoverySystem类

尝试将imx51使用OTA方式进行recovery,将android_recovery.img放入/cache/分区下然后再下一次重启的时候进行分区更新,但发现放入的文件会被莫名删除,logcat中搜索到recovery相关log,tag为recoverysystem,尝试在android工程framework的os代码中寻找代码,找到相关代码,代码最后有将cache分区进行删除仅保留last_log的操作,这才解开心中的疑惑,而且发现,以前本地升级时写文件的代码其实不需要自己写的,android自己已经做好了类的函数,需要的只是调用。代码很简单,和recovery的代码几乎一样,java话而已,无须一条条解释代码。List as below:

部分中文解释为下:(参考http://www.elexcon.com/news/54208.html)

从Android 2.2开始新增RecoverySystem类,可以帮助我们调用系统还原等操作,使用RecoverySystem必须是API Level最小为为8,该类位于android.os.RecoverySystem,提供了三个静态方法

  static void  installPackage(Context context, File packageFile)   //重启设备,安装一个更新包

  static void  rebootWipeUserData(Context context)  //重启设备,清除用户数据分区类似恢复出厂设置
 
  static void  verifyPackage(File packageFile, RecoverySystem.ProgressListener listener, File deviceCertsZipFile)  //验证加密签名的系统更新包在安装前,其中第二个数接口的具体定义为 android.os.RecoverySystem.ProgressListener  其中只有一个回调方法  abstract void  onProgress(int progress)   来显示效验的进度。

android/frameworks/base/core/java/android/os/RecoverySystem.java

[java]  view plain copy
  1. /* 
  2.  * Copyright (C) 2010 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package android.os;  
  18.   
  19. import android.content.BroadcastReceiver;  
  20. import android.content.Context;  
  21. import android.content.Intent;  
  22. import android.util.Log;  
  23.   
  24. import java.io.ByteArrayInputStream;  
  25. import java.io.File;  
  26. import java.io.FileNotFoundException;  
  27. import java.io.FileWriter;  
  28. import java.io.IOException;  
  29. import java.io.RandomAccessFile;  
  30. import java.security.GeneralSecurityException;  
  31. import java.security.PublicKey;  
  32. import java.security.Signature;  
  33. import java.security.SignatureException;  
  34. import java.security.cert.Certificate;  
  35. import java.security.cert.CertificateFactory;  
  36. import java.security.cert.X509Certificate;  
  37. import java.util.Collection;  
  38. import java.util.Enumeration;  
  39. import java.util.HashSet;  
  40. import java.util.Iterator;  
  41. import java.util.List;  
  42. import java.util.zip.ZipEntry;  
  43. import java.util.zip.ZipFile;  
  44.   
  45. import org.apache.harmony.security.asn1.BerInputStream;  
  46. import org.apache.harmony.security.pkcs7.ContentInfo;  
  47. import org.apache.harmony.security.pkcs7.SignedData;  
  48. import org.apache.harmony.security.pkcs7.SignerInfo;  
  49. import org.apache.harmony.security.provider.cert.X509CertImpl;  
  50.   
  51. /** 
  52.  * RecoverySystem contains methods for interacting with the Android 
  53.  * recovery system (the separate partition that can be used to install 
  54.  * system updates, wipe user data, etc.) 
  55.  */  
  56. public class RecoverySystem {  
  57.     private static final String TAG = "RecoverySystem";  
  58.   
  59.     /** 
  60.      * Default location of zip file containing public keys (X509 
  61.      * certs) authorized to sign OTA updates. 
  62.      */  
  63.     private static final File DEFAULT_KEYSTORE =  
  64.         new File("/system/etc/security/otacerts.zip");  
  65.   
  66.     /** Send progress to listeners no more often than this (in ms). */  
  67.     private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500;  
  68.   
  69.     /** Used to communicate with recovery.  See bootable/recovery/recovery.c. */  
  70.     private static File RECOVERY_DIR = new File("/cache/recovery");  
  71.     private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");  
  72.     private static File LOG_FILE = new File(RECOVERY_DIR, "log");  
  73.     private static String LAST_LOG_FILENAME = "last_log";  
  74.   
  75.     // Length limits for reading files.  
  76.     private static int LOG_FILE_MAX_LENGTH = 64 * 1024;  
  77.   
  78.     /** 
  79.      * Interface definition for a callback to be invoked regularly as 
  80.      * verification proceeds. 
  81.      */  
  82.     public interface ProgressListener {  
  83.         /** 
  84.          * Called periodically as the verification progresses. 
  85.          * 
  86.          * @param progress  the approximate percentage of the 
  87.          *        verification that has been completed, ranging from 0 
  88.          *        to 100 (inclusive). 
  89.          */  
  90.         public void onProgress(int progress);  
  91.     }  
  92.   
  93.     /** @return the set of certs that can be used to sign an OTA package. */  
  94.     private static HashSet<Certificate> getTrustedCerts(File keystore)  
  95.         throws IOException, GeneralSecurityException {  
  96.         HashSet<Certificate> trusted = new HashSet<Certificate>();  
  97.         if (keystore == null) {  
  98.             keystore = DEFAULT_KEYSTORE;  
  99.         }  
  100.         ZipFile zip = new ZipFile(keystore);  
  101.         try {  
  102.             CertificateFactory cf = CertificateFactory.getInstance("X.509");  
  103.             Enumeration<? extends ZipEntry> entries = zip.entries();  
  104.             while (entries.hasMoreElements()) {  
  105.                 ZipEntry entry = entries.nextElement();  
  106.                 trusted.add(cf.generateCertificate(zip.getInputStream(entry)));  
  107.             }  
  108.         } finally {  
  109.             zip.close();  
  110.         }  
  111.         return trusted;  
  112.     }  
  113.   
  114.     /** 
  115.      * Verify the cryptographic signature of a system update package 
  116.      * before installing it.  Note that the package is also verified 
  117.      * separately by the installer once the device is rebooted into 
  118.      * the recovery system.  This function will return only if the 
  119.      * package was successfully verified; otherwise it will throw an 
  120.      * exception. 
  121.      * 
  122.      * Verification of a package can take significant time, so this 
  123.      * function should not be called from a UI thread.  Interrupting 
  124.      * the thread while this function is in progress will result in a 
  125.      * SecurityException being thrown (and the thread's interrupt flag 
  126.      * will be cleared). 
  127.      * 
  128.      * @param packageFile  the package to be verified 
  129.      * @param listener     an object to receive periodic progress 
  130.      * updates as verification proceeds.  May be null. 
  131.      * @param deviceCertsZipFile  the zip file of certificates whose 
  132.      * public keys we will accept.  Verification succeeds if the 
  133.      * package is signed by the private key corresponding to any 
  134.      * public key in this file.  May be null to use the system default 
  135.      * file (currently "/system/etc/security/otacerts.zip"). 
  136.      * 
  137.      * @throws IOException if there were any errors reading the 
  138.      * package or certs files. 
  139.      * @throws GeneralSecurityException if verification failed 
  140.      */  
  141.     public static void verifyPackage(File packageFile,  
  142.                                      ProgressListener listener,  
  143.                                      File deviceCertsZipFile)  
  144.         throws IOException, GeneralSecurityException {  
  145.         long fileLen = packageFile.length();  
  146.   
  147.         RandomAccessFile raf = new RandomAccessFile(packageFile, "r");  
  148.         try {  
  149.             int lastPercent = 0;  
  150.             long lastPublishTime = System.currentTimeMillis();  
  151.             if (listener != null) {  
  152.                 listener.onProgress(lastPercent);  
  153.             }  
  154.   
  155.             raf.seek(fileLen - 6);  
  156.             byte[] footer = new byte[6];  
  157.             raf.readFully(footer);  
  158.   
  159.             if (footer[2] != (byte)0xff || footer[3] != (byte)0xff) {  
  160.                 throw new SignatureException("no signature in file (no footer)");  
  161.             }  
  162.   
  163.             int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8);  
  164.             int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8);  
  165.             Log.v(TAG, String.format("comment size %d; signature start %d",  
  166.                                      commentSize, signatureStart));  
  167.   
  168.             byte[] eocd = new byte[commentSize + 22];  
  169.             raf.seek(fileLen - (commentSize + 22));  
  170.             raf.readFully(eocd);  
  171.   
  172.             // Check that we have found the start of the  
  173.             // end-of-central-directory record.  
  174.             if (eocd[0] != (byte)0x50 || eocd[1] != (byte)0x4b ||  
  175.                 eocd[2] != (byte)0x05 || eocd[3] != (byte)0x06) {  
  176.                 throw new SignatureException("no signature in file (bad footer)");  
  177.             }  
  178.   
  179.             for (int i = 4; i < eocd.length-3; ++i) {  
  180.                 if (eocd[i  ] == (byte)0x50 && eocd[i+1] == (byte)0x4b &&  
  181.                     eocd[i+2] == (byte)0x05 && eocd[i+3] == (byte)0x06) {  
  182.                     throw new SignatureException("EOCD marker found after start of EOCD");  
  183.                 }  
  184.             }  
  185.   
  186.             // The following code is largely copied from  
  187.             // JarUtils.verifySignature().  We could just *call* that  
  188.             // method here if that function didn't read the entire  
  189.             // input (ie, the whole OTA package) into memory just to  
  190.             // compute its message digest.  
  191.   
  192.             BerInputStream bis = new BerInputStream(  
  193.                 new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart));  
  194.             ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis);  
  195.             SignedData signedData = info.getSignedData();  
  196.             if (signedData == null) {  
  197.                 throw new IOException("signedData is null");  
  198.             }  
  199.             Collection encCerts = signedData.getCertificates();  
  200.             if (encCerts.isEmpty()) {  
  201.                 throw new IOException("encCerts is empty");  
  202.             }  
  203.             // Take the first certificate from the signature (packages  
  204.             // should contain only one).  
  205.             Iterator it = encCerts.iterator();  
  206.             X509Certificate cert = null;  
  207.             if (it.hasNext()) {  
  208.                 cert = new X509CertImpl((org.apache.harmony.security.x509.Certificate)it.next());  
  209.             } else {  
  210.                 throw new SignatureException("signature contains no certificates");  
  211.             }  
  212.   
  213.             List sigInfos = signedData.getSignerInfos();  
  214.             SignerInfo sigInfo;  
  215.             if (!sigInfos.isEmpty()) {  
  216.                 sigInfo = (SignerInfo)sigInfos.get(0);  
  217.             } else {  
  218.                 throw new IOException("no signer infos!");  
  219.             }  
  220.   
  221.             // Check that the public key of the certificate contained  
  222.             // in the package equals one of our trusted public keys.  
  223.   
  224.             HashSet<Certificate> trusted = getTrustedCerts(  
  225.                 deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile);  
  226.   
  227.             PublicKey signatureKey = cert.getPublicKey();  
  228.             boolean verified = false;  
  229.             for (Certificate c : trusted) {  
  230.                 if (c.getPublicKey().equals(signatureKey)) {  
  231.                     verified = true;  
  232.                     break;  
  233.                 }  
  234.             }  
  235.             if (!verified) {  
  236.                 throw new SignatureException("signature doesn't match any trusted key");  
  237.             }  
  238.   
  239.             // The signature cert matches a trusted key.  Now verify that  
  240.             // the digest in the cert matches the actual file data.  
  241.   
  242.             // The verifier in recovery *only* handles SHA1withRSA  
  243.             // signatures.  SignApk.java always uses SHA1withRSA, no  
  244.             // matter what the cert says to use.  Ignore  
  245.             // cert.getSigAlgName(), and instead use whatever  
  246.             // algorithm is used by the signature (which should be  
  247.             // SHA1withRSA).  
  248.   
  249.             String da = sigInfo.getdigestAlgorithm();  
  250.             String dea = sigInfo.getDigestEncryptionAlgorithm();  
  251.             String alg = null;  
  252.             if (da == null || dea == null) {  
  253.                 // fall back to the cert algorithm if the sig one  
  254.                 // doesn't look right.  
  255.                 alg = cert.getSigAlgName();  
  256.             } else {  
  257.                 alg = da + "with" + dea;  
  258.             }  
  259.             Signature sig = Signature.getInstance(alg);  
  260.             sig.initVerify(cert);  
  261.   
  262.             // The signature covers all of the OTA package except the  
  263.             // archive comment and its 2-byte length.  
  264.             long toRead = fileLen - commentSize - 2;  
  265.             long soFar = 0;  
  266.             raf.seek(0);  
  267.             byte[] buffer = new byte[4096];  
  268.             boolean interrupted = false;  
  269.             while (soFar < toRead) {  
  270.                 interrupted = Thread.interrupted();  
  271.                 if (interrupted) break;  
  272.                 int size = buffer.length;  
  273.                 if (soFar + size > toRead) {  
  274.                     size = (int)(toRead - soFar);  
  275.                 }  
  276.                 int read = raf.read(buffer, 0, size);  
  277.                 sig.update(buffer, 0, read);  
  278.                 soFar += read;  
  279.   
  280.                 if (listener != null) {  
  281.                     long now = System.currentTimeMillis();  
  282.                     int p = (int)(soFar * 100 / toRead);  
  283.                     if (p > lastPercent &&  
  284.                         now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {  
  285.                         lastPercent = p;  
  286.                         lastPublishTime = now;  
  287.                         listener.onProgress(lastPercent);  
  288.                     }  
  289.                 }  
  290.             }  
  291.             if (listener != null) {  
  292.                 listener.onProgress(100);  
  293.             }  
  294.   
  295.             if (interrupted) {  
  296.                 throw new SignatureException("verification was interrupted");  
  297.             }  
  298.   
  299.             if (!sig.verify(sigInfo.getEncryptedDigest())) {  
  300.                 throw new SignatureException("signature digest verification failed");  
  301.             }  
  302.         } finally {  
  303.             raf.close();  
  304.         }  
  305.     }  
  306.   
  307.     /** 
  308.      * Reboots the device in order to install the given update 
  309.      * package. 
  310.      * Requires the {@link android.Manifest.permission#REBOOT} permission. 
  311.      * 
  312.      * @param context      the Context to use 
  313.      * @param packageFile  the update package to install.  Must be on 
  314.      * a partition mountable by recovery.  (The set of partitions 
  315.      * known to recovery may vary from device to device.  Generally, 
  316.      * /cache and /data are safe.) 
  317.      * 
  318.      * @throws IOException  if writing the recovery command file 
  319.      * fails, or if the reboot itself fails. 
  320.      */  
  321.     public static void installPackage(Context context, File packageFile)  
  322.         throws IOException {  
  323.         String filename = packageFile.getCanonicalPath();  
  324.         Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");  
  325.         String arg = "--update_package=" + filename;  
  326.         bootCommand(context, arg);  
  327.     }  
  328.   
  329.     /** 
  330.      * Reboots the device and wipes the user data partition.  This is 
  331.      * sometimes called a "factory reset", which is something of a 
  332.      * misnomer because the system partition is not restored to its 
  333.      * factory state. 
  334.      * Requires the {@link android.Manifest.permission#REBOOT} permission. 
  335.      * 
  336.      * @param context  the Context to use 
  337.      * 
  338.      * @throws IOException  if writing the recovery command file 
  339.      * fails, or if the reboot itself fails. 
  340.      */  
  341.     public static void rebootWipeUserData(Context context) throws IOException {  
  342.         final ConditionVariable condition = new ConditionVariable();  
  343.   
  344.         Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");  
  345.         context.sendOrderedBroadcast(intent, android.Manifest.permission.MASTER_CLEAR,  
  346.                 new BroadcastReceiver() {  
  347.                     @Override  
  348.                     public void onReceive(Context context, Intent intent) {  
  349.                         condition.open();  
  350.                     }  
  351.                 }, null0nullnull);  
  352.   
  353.         // Block until the ordered broadcast has completed.  
  354.         condition.block();  
  355.   
  356.         bootCommand(context, "--wipe_data");  
  357.     }  
  358.   
  359.     /** 
  360.      * Reboot into the recovery system to wipe the /data partition and toggle 
  361.      * Encrypted File Systems on/off. 
  362.      * @param extras to add to the RECOVERY_COMPLETED intent after rebooting. 
  363.      * @throws IOException if something goes wrong. 
  364.      * 
  365.      * @hide 
  366.      */  
  367.     public static void rebootToggleEFS(Context context, boolean efsEnabled)  
  368.         throws IOException {  
  369.         if (efsEnabled) {  
  370.             bootCommand(context, "--set_encrypted_filesystem=on");  
  371.         } else {  
  372.             bootCommand(context, "--set_encrypted_filesystem=off");  
  373.         }  
  374.     }  
  375.   
  376.     /** 
  377.      * Reboot into the recovery system with the supplied argument. 
  378.      * @param arg to pass to the recovery utility. 
  379.      * @throws IOException if something goes wrong. 
  380.      */  
  381.     private static void bootCommand(Context context, String arg) throws IOException {  
  382.         RECOVERY_DIR.mkdirs();  // In case we need it  
  383.         COMMAND_FILE.delete();  // In case it's not writable  
  384.         LOG_FILE.delete();  
  385.   
  386.         FileWriter command = new FileWriter(COMMAND_FILE);  
  387.         try {  
  388.             command.write(arg);  
  389.             command.write("\n");  
  390.         } finally {  
  391.             command.close();  
  392.         }  
  393.   
  394.         // Having written the command file, go ahead and reboot  
  395.         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);  
  396.         pm.reboot("recovery");  
  397.   
  398.         throw new IOException("Reboot failed (no permissions?)");  
  399.     }  
  400.   
  401.     /** 
  402.      * Called after booting to process and remove recovery-related files. 
  403.      * @return the log file from recovery, or null if none was found. 
  404.      * 
  405.      * @hide 
  406.      */  
  407.     public static String handleAftermath() {  
  408.         // Record the tail of the LOG_FILE  
  409.         String log = null;  
  410.         try {  
  411.             log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n");  
  412.         } catch (FileNotFoundException e) {  
  413.             Log.i(TAG, "No recovery log file");  
  414.         } catch (IOException e) {  
  415.             Log.e(TAG, "Error reading recovery log", e);  
  416.         }  
  417.   
  418.         // Delete everything in RECOVERY_DIR except LAST_LOG_FILENAME  
  419.         String[] names = RECOVERY_DIR.list();  
  420.         for (int i = 0; names != null && i < names.length; i++) {  
  421.             if (names[i].equals(LAST_LOG_FILENAME)) continue;  
  422.             File f = new File(RECOVERY_DIR, names[i]);  
  423.             if (!f.delete()) {  
  424.                 Log.e(TAG, "Can't delete: " + f);  
  425.             } else {  
  426.                 Log.i(TAG, "Deleted: " + f);  
  427.             }  
  428.         }  
  429.   
  430.         return log;  
  431.     }  
  432.   
  433.     private void RecoverySystem() { }  // Do not instantiate  
  434. }  

你可能感兴趣的:(Android 上层RecoverySystem类)