Androidは工場出荷時の設定を復元(recovery)

16275 ワード

Androidリカバリ出荷時の基本手順
(1)リモコン/キーパッドのバックドアキーがトリガーされたり、アプリケーションの中でシステム設定から工場出荷オプションを復元したりしてもトリガーされます.//システム設定の適用トリガを例に挙げる
(2)工場出荷時の設定の復元を選択すると、ブロードキャスト「android.intent.action.MASTER_CLEAR」が送信されます.//framework/base/core/res/AndroidManifest.xml
(3)MasterClearReceiverはブロードキャストをキャプチャし、android層の関連処理を行い、最後に再起動する.
(4)/cache/recovery/commandファイルにコマンドフィールドを書き込む.
(5)システムを再起動する;
recoveryアクセス方式
(1)ファイル/cache/recovery/commandコンテンツの読み込み/cacheパーティションへのアクセス
(2)キー操作で入る(G 1 HOMEとターンオフキーを同時に押すことで)
以上の2つの方法で入るにはblobのサポートが必要です
recoveryに入る条件
(1)
blobはrecoveryパーティションからカーネルとファイルシステムをマウントできる必要があります(2
)flashにはcacheパーティションとrecoveryパーティションが必要です(3
)recoveryをコンパイルする必要があります.imgをrecoveryパーティションに記録する
Androidの処理フロー
ブロードキャスト受信
framework/base/core/res/AndroidManifest.xml

    
        
        

        
        
        
    

MasterClearReceiverはブロードキャストを受信しました.intent.action.MASTER_CLEAR、県城を作って処理します
Thread thr = new Thread("Reboot") {
    @Override
    public void run() {
        try {
            RecoverySystem.rebootWipeUserData(context, shutdown, reason);
            Log.wtf(TAG, "Still running after master clear?!");
        } catch (IOException e) {
            Slog.e(TAG, "Can't perform master clear/factory reset", e);
        } catch (SecurityException e) {
            Slog.e(TAG, "Can't perform master clear/factory reset", e);
        }
    }
};
thr.start();

RecoverySystemを再起動し、ユーザーデータの消去操作を開始
/**
 * Reboots the device and wipes the user data and cache
 * partitions.  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
 * @param shutdown  if true, the device will be powered down after
 *                  the wipe completes, rather than being rebooted
 *                  back to the regular system.
 *
 * @throws IOException  if writing the recovery command file
 * fails, or if the reboot itself fails.
 * @throws SecurityException if the current user is not allowed to wipe data.
 *
 * @hide
 */
public static void rebootWipeUserData(Context context, boolean shutdown, String reason)
        throws IOException {
    UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    if (um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
        throw new SecurityException("Wiping data is not allowed for this user.");
    }
    final ConditionVariable condition = new ConditionVariable();

    Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
            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();

    String shutdownArg = null;
    if (shutdown) {
        shutdownArg = "--shutdown_after";
    }

    String reasonArg = null;
    if (!TextUtils.isEmpty(reason)) {
        reasonArg = "--reason=" + sanitizeArg(reason);
    }

    final String localeArg = "--locale=" + Locale.getDefault().toString();
    bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
}

bootCommand転送コマンドを起動すると、パラメータ--wipe_がカプセル化されることに注意してください.Data,--locale、これらのコマンドはrecovery log(/cache/recovery/*.log)情報を表示するときに表示できます.
「Command:"/sbin/recovery"--wipe_data"--locale=zh_CN」とは、bootCommandが実行するコマンドのことです
/**
 * Reboot into the recovery system with the supplied argument.
 * @param args to pass to the recovery utility.
 * @throws IOException if something goes wrong.
 */
private static void bootCommand(Context context, String... args) 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);
    try {
        for (String arg : args) {
            if (!TextUtils.isEmpty(arg)) {
                // MStar Android Patch Begin
                String cmd = arg;
                String label = null;
                String uuid = null;
                if (cmd.startsWith("--update_package")) {
                    cmd = arg.substring(17, 23);
                    if (cmd.equals("/cache")) {
                        command.write("--uuid=mstar-cache");
                        command.write("
"); command.write("--label=mstar-cache"); command.write("
"); } else { cmd = arg.substring(17, 28); if (cmd.equals("/mnt/usb/sd")) { cmd = arg.substring(17, 30); uuid = "--uuid=" + getVolumeUUID(cmd).toString(); label = "--label=" + getVolumeLabel(cmd).toString(); command.write(uuid); command.write("
"); command.write(label); command.write("
"); } else { if (cmd.equals("/mnt/sdcard")) { uuid = "--uuid=" + getVolumeUUID(cmd).toString(); label = "--label=" + getVolumeLabel(cmd).toString(); command.write(uuid); command.write("
"); command.write(label); command.write("
"); } else { cmd = arg.substring(17, 32); if (cmd.equals("/mnt/usb/mmcblk")) { cmd = arg.substring(17, 35); uuid = "--uuid=" + getVolumeUUID(cmd).toString(); label = "--label=" + getVolumeLabel(cmd).toString(); command.write(uuid); command.write("
"); command.write(label); command.write("
"); } } } } } // MStar Android Patch End command.write(arg); command.write("
"); } } } finally { command.close(); } // Having written the command file, go ahead and reboot PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); pm.reboot(PowerManager.REBOOT_RECOVERY); throw new IOException("Reboot failed (no permissions?)"); }

以上のコードの分析から、bootCommandの主な仕事はrecoveryに入ることを再起動することで、ここでCOMMAND_を見ることができますFILEはファイル"/cache/recovery/command"で、上にパッケージされたパラメータ情報
変更ファイルを書き込み、再起動後にファイルを読み込むときにrecoveryモードに入ります.また、ファイルを書き終わった後、PowerManagerを呼び出してrebootを呼び出し、パラメータPowerManagerに注意します.REBOOT_RECOVERY
// Having written the command file, go ahead and reboot
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
pm.reboot(PowerManager.REBOOT_RECOVERY);

/**
 * Reboot the device.  Will not return if the reboot is successful.
 * 

* Requires the {@link android.Manifest.permission#REBOOT} permission. *

* * @param reason code to pass to the kernel (e.g., "recovery") to * request special boot modes, or null. */ public void reboot(String reason) { try { mService.reboot(false, reason, true); } catch (RemoteException e) { } }

最後にPowerManagerServiceのreboot関数に入ります
/**
 * Reboots the device.
 *
 * @param confirm If true, shows a reboot confirmation dialog.
 * @param reason The reason for the reboot, or null if none.
 * @param wait If true, this call waits for the reboot to complete and does not return.
 */
@Override // Binder call
public void reboot(boolean confirm, String reason, boolean wait) {
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
    if (PowerManager.REBOOT_RECOVERY.equals(reason)) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
    }

    final long ident = Binder.clearCallingIdentity();
    try {
        shutdownOrRebootInternal(false, confirm, reason, wait);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

次にshutdownOrRebootInternalに入ります
private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
        final String reason, boolean wait) {
    if (mHandler == null || !mSystemReady) {
        throw new IllegalStateException("Too early to call shutdown() or reboot()");
    }

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                if (shutdown) {
                    ShutdownThread.shutdown(mContext, confirm);
                } else {
                    ShutdownThread.reboot(mContext, reason, confirm);
                }
            }
        }
    };

    // ShutdownThread must run on a looper capable of displaying the UI.
    Message msg = Message.obtain(mHandler, runnable);
    msg.setAsynchronous(true);
    mHandler.sendMessage(msg);

    // PowerManager.reboot() is documented not to return so just wait for the inevitable.
    if (wait) {
        synchronized (runnable) {
            while (true) {
                try {
                    runnable.wait();
                } catch (InterruptedException e) {
                }
            }
        }
    }
}

ShutdownThreadは再起動動作を担当する
/**
 * Request a clean shutdown, waiting for subsystems to clean up their
 * state etc.  Must be called from a Looper thread in which its UI
 * is shown.
 *
 * @param context Context used to display the shutdown progress dialog.
 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
 * @param confirm true if user confirmation is needed before shutting down.
 */
public static void reboot(final Context context, String reason, boolean confirm) {
    mReboot = true;
    mRebootSafeMode = false;
    mRebootReason = reason;
    shutdownInner(context, confirm);
}

static void shutdownInner(final Context context, boolean confirm) {
    // ensure that only one thread is trying to power down.
    // any additional calls are just returned
    synchronized (sIsStartedGuard) {
        if (sIsStarted) {
            Log.d(TAG, "Request to shutdown already running, returning.");
            return;
        }
    }

    final int longPressBehavior = context.getResources().getInteger(
                    com.android.internal.R.integer.config_longPressOnPowerBehavior);
    final int resourceId = mRebootSafeMode
            ? com.android.internal.R.string.reboot_safemode_confirm
            : (longPressBehavior == 2
                    ? com.android.internal.R.string.shutdown_confirm_question
                    : com.android.internal.R.string.shutdown_confirm);

    Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);

    if (confirm) {
        final CloseDialogReceiver closer = new CloseDialogReceiver(context);
        if (sConfirmDialog != null) {
            sConfirmDialog.dismiss();
        }
        sConfirmDialog = new AlertDialog.Builder(context)
                .setTitle(mRebootSafeMode
                        ? com.android.internal.R.string.reboot_safemode_title
                        : com.android.internal.R.string.power_off)
                .setMessage(resourceId)
                .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        beginShutdownSequence(context);
                    }
                })
                .setNegativeButton(com.android.internal.R.string.no, null)
                .create();
        closer.dialog = sConfirmDialog;
        sConfirmDialog.setOnDismissListener(closer);
        sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
        sConfirmDialog.show();
    } else {
        beginShutdownSequence(context);
    }
}

beginShutdownSequenceは主要なシャットダウンプロセスに入り、ShutdownThreadを起動する.run()は、フォトマシンブロードキャストを送信し、コアサービスをオフにし、最後にrebootOrShutdownに入って再起動します.
recoveryプロセスへの移行
recoveryに入るにはいくつかの方法があります.
(1)recoveryに入る前にmiscパーティションを書き、再起動時に変化が発見されたら直接recoveryモードに入る.
(2)ファイル/cache/recovery/commandファイルを書き、再起動時にrecoveryモードに入る.//このモードではrecoveryを起動する場所は見つかりませんが、recoveryを起動した後に読み取りが見られます.
//cache/recovery/commandファイルデータを取り、その後の操作を行う
MISCパーティションの内容
Bootloader Control Block(BCB)はrecovery bootloader messageを格納し、構造は以下の通りである.
struct bootloader_message {
    char command[32];
    char status[32];
    char recovery[768];
   //The 'recovery' field used to be 1024 bytes.  It has only ever
   //been used to store the recovery command line, so 768 bytes
   //should be plenty.  We carve off the last 256 bytes to store the
   //stage string (for multistage packages) and possible future
   //expansion.
    char stage[32];
    char reserved[224];
};
commandには次の2つの値があります.
「boot-recovery」:recoveryが進行中であるか、bootloaderがrecovery modeに入るべきであることを示す
「update-hboot/radio」:フラグbootloader更新fireware
recoveryコンテンツ"recovery
"recovery commandはCACHE:/recovery/commandコマンド
Recovery Case 
Factory reset(工場出荷時の設定を復元)
1.ユーザーが「工場出荷時設定の復元」を選択
2.システムが「--wipe_data」コマンドを/cache/recovery/commandに書き込むように設定
3.システムを再起動し、recoveryモード(sbin/recovery or/system/bin/recovery)に入る
4. recovery get_Args()は「boot-recovery」と「--wipe_data」をBCBに書き込む
5. erase_rootフォーマットDATAパーティション
6. erase_rootフォーマットCACHEパーティション
7. finish_recovery BCBパーティションの消去
8.システムの再起動
OTA INSTALL(OTAアップグレード)1.アップグレードシステムはOTAバッグ/cache/some-filename.zip
2.アップグレードシステム書き込みrecoveryコマンド"--update_package=CACHE:some-filename.zip"
3.システムを再起動し、recoveryモードに入る
4. get_Args()は「boot-recovery」、「--wipe_packkage=...」BCBへの書き込み
5. install_パッケージアップグレード
6. finish_recovery()BCB消去
7.**パッケージのインストールに失敗した場合**prompt_and_wait()ユーザー操作待ち、ALT+SまたはALT+Wアップグレードまたは工場出荷時設定への返信を選択
8.main()でmaybeを呼び出すinstall_firmware_update()
    1.パケットにhboot/radioのfirewareが含まれている場合は続行し、そうでない場合は戻ります.
    2.「boot-recovery」と「--wipe_cache」をBCBに書き込む
    3.firewareイメージをcacheパーティションに書き込む
    4.BCBに「update-readio/hboot」と「--wipe_date」を書き込む
    5.システムの再起動
    6.bootloader自身がfirewareを更新
    7.bootloaderはBCBに「boot-recovery」を書き込む
    8.erase_root消去CACHEパーティション
    9.BCBクリア
9.main呼び出しreboot再起動システム
Recoveryコードの場所:bootable/recovery/,プライマリファイルrecovery.cpp
recoveryプロセスを後で再分析します.
                  
Android Recoveryモード学習!