Android RecoverSystem 類

    昨天開會討論IP網絡升級時,需要一個後臺服務做升級檢測及下載數據,最後在定義下載的數據存放位置及如何做開機標志時,突然發現一個很好用的上層java類對象。

frameworks\base\core\java\android\os\RecoverySystem.java
那麼這個類做瞭什麼事情呢?又是如何做到的。

 

RecoverySystem類,可以幫助我們調用系統還原等操作:

 

android.os.RecoverySystem,提供瞭如下靜態方法
  static void  installPackage(Context context, File packageFile)   //重啟設備,安裝一個更新包

  static void  rebootWipeUserData(Context context)  //重啟設備,清除用戶數據分區類似恢復出廠設置

  static String handleAftermath() 提供清除recover中相關文件,在開機廣播中被調用

  static void  verifyPackage(File packageFile, RecoverySystem.ProgressListener listener, File deviceCertsZipFile)  //驗證加密簽名的系統更新包在安裝前,其中第二個數接口的具體定義為 android.os.RecoverySystem.ProgressListener  其中隻有一個回調方法  abstract void  onProgress(int progress)   來顯示效驗的進度。
 

下面具體看一下代碼中是如何實現:
1、安裝更新包:

 

 

/**
 * Reboots the device in order to install the given update
 * package.
 * Requires the {@link android.Manifest.permission#REBOOT} permission.
 *
 * @param context      the Context to use
 * @param packageFile  the update package to install.  Must be on
 * a partition mountable by recovery.  (The set of partitions
 * known to recovery may vary from device to device.  Generally,
 * /cache and /data are safe.)
 *
 * @throws IOException  if writing the recovery command file
 * fails, or if the reboot itself fails.
 */ 
public static void installPackage(Context context, File packageFile) 
    throws IOException { 
    String filename = packageFile.getCanonicalPath(); 
    Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); 
    String arg = "--update_package=" + filename; 
    bootCommand(context, arg); // 都是調用瞭這個函數  
} 
 
 
/**
 * Reboot into the recovery system with the supplied argument.
 * @param arg to pass to the recovery utility.
 * @throws IOException if something goes wrong.
 */ 
private static void bootCommand(Context context, String arg) throws IOException { 
    RECOVERY_DIR.mkdirs();  // In case we need it  
    COMMAND_FILE.delete();  // In case it's not writable  
    LOG_FILE.delete(); 
 
 
    FileWriter command = new FileWriter(COMMAND_FILE);  // 寫命令寫入到recover中  
    try { 
        command.write(arg); 
        command.write("\n"); 
    } finally { 
        command.close(); 
    } 
 
 
    // Having written the command file, go ahead and reboot 系統重啟  
    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 
    pm.reboot("recovery"); 
 
 
    throw new IOException("Reboot failed (no permissions?)"); 
} 

    /**
     * Reboots the device in order to install the given update
     * package.
     * Requires the {@link android.Manifest.permission#REBOOT} permission.
     *
     * @param context      the Context to use
     * @param packageFile  the update package to install.  Must be on
     * a partition mountable by recovery.  (The set of partitions
     * known to recovery may vary from device to device.  Generally,
     * /cache and /data are safe.)
     *
     * @throws IOException  if writing the recovery command file
     * fails, or if the reboot itself fails.
     */
    public static void installPackage(Context context, File packageFile)
        throws IOException {
        String filename = packageFile.getCanonicalPath();
        Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
        String arg = "--update_package=" + filename;
        bootCommand(context, arg); // 都是調用瞭這個函數
    }


    /**
     * Reboot into the recovery system with the supplied argument.
     * @param arg to pass to the recovery utility.
     * @throws IOException if something goes wrong.
     */
    private static void bootCommand(Context context, String arg) throws IOException {
        RECOVERY_DIR.mkdirs();  // In case we need it
        COMMAND_FILE.delete();  // In case it's not writable
        LOG_FILE.delete();


        FileWriter command = new FileWriter(COMMAND_FILE);  // 寫命令寫入到recover中
        try {
            command.write(arg);
            command.write("\n");
        } finally {
            command.close();
        }


        // Having written the command file, go ahead and reboot 系統重啟
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        pm.reboot("recovery");


        throw new IOException("Reboot failed (no permissions?)");
    }

2、出廠恢復

 

 

  

/**
 * Reboots the device and wipes the user data partition.  This is
 * sometimes called a "factory reset", which is something of a
 * misnomer because the system partition is not restored to its
 * factory state.
 * Requires the {@link android.Manifest.permission#REBOOT} permission.
 *
 * @param context  the Context to use
 *
 * @throws IOException  if writing the recovery command file
 * fails, or if the reboot itself fails.
 */ 
public static void rebootWipeUserData(Context context) throws IOException { 
    final ConditionVariable condition = new ConditionVariable(); 
 
 
    Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION"); 
    context.sendOrderedBroadcast(intent, android.Manifest.permission.MASTER_CLEAR, 
            new BroadcastReceiver() { 
                @Override 
                public void onReceive(Context context, Intent intent) { 
                    condition.open(); 
                } 
            }, null, 0, null, null); 
 
 
    // Block until the ordered broadcast has completed.  
    condition.block(); 
 
 
    bootCommand(context, "--wipe_data"); 
} 
/**
 * Reboot into the recovery system to wipe the /cache partition.
 * @throws IOException if something goes wrong.
 */ 
public static void rebootWipeCache(Context context) throws IOException { 
    bootCommand(context, "--wipe_cache"); 
} 

    /**
     * Reboots the device and wipes the user data partition.  This is
     * sometimes called a "factory reset", which is something of a
     * misnomer because the system partition is not restored to its
     * factory state.
     * Requires the {@link android.Manifest.permission#REBOOT} permission.
     *
     * @param context  the Context to use
     *
     * @throws IOException  if writing the recovery command file
     * fails, or if the reboot itself fails.
     */
    public static void rebootWipeUserData(Context context) throws IOException {
        final ConditionVariable condition = new ConditionVariable();


        Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
        context.sendOrderedBroadcast(intent, android.Manifest.permission.MASTER_CLEAR,
                new BroadcastReceiver() {
                    @Override
                    public void onReceive(Context context, Intent intent) {
                        condition.open();
                    }
                }, null, 0, null, null);


        // Block until the ordered broadcast has completed.
        condition.block();


        bootCommand(context, "--wipe_data");
    }
    /**
     * Reboot into the recovery system to wipe the /cache partition.
     * @throws IOException if something goes wrong.
     */
    public static void rebootWipeCache(Context context) throws IOException {
        bootCommand(context, "--wipe_cache");
    }

3、驗證簽名
    public static void verifyPackage(File packageFile,
                                     ProgressListener listener,
                                     File deviceCertsZipFile)  請自行閱讀源碼

 

後面說一下重啟 reboot 命令如何執行的:

 

無論是 factory reset 工廠恢復還是 安裝更新包都會調用到reboot函數,調用   pm.reboot("recovery");

通到jni就是: android_os_Power.cpp

 

shutdown 調用 android_reboot(ANDROID_RB_POWEROFF, 0, 0);
reboot 調用 android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);
最終都是調用到下面函數:
int reboot (int  mode)
{
    return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );
}

=====linux kernel 內核流程=====
/bionic/libc/arch-arm/syscalls/__reboot.S
這個文件由gensyscalls.py自動產生,調用系統調用 __NR_reboot

bionic/libc/include/sys/linux-syscalls.h
#define __NR_reboot (__NR_SYSCALL_BASE+88)

/arch/arm/kernel/calls.S
__NR_reboot?就是系統函數sys_reboot

sys_reboot定義在./include/linux/syscalls.h,實現在./kernel/sys.c?sys_reboot會調用kernel_restart
最終kernel_restart調用每一個架構特定的machine_restart,即machine_restart執行每個架構特定的函數

 

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *