Android 恢复出厂设置流程

一.概述

首先我们来简单说一下 Master Reset 的流程,或者说这个原生功能在执行过程中有哪些部分参与。

从触发开始,Android 的 Settings 中基本会有 恢复出场设置/清除数据 的功能,可以从 Settings 中触发相关功能;但是实际功能实现并不是 Settings 实现的,在系统中有一个 MasterClearReceiver 广播接收器,Settings 的工作是当用户按下相关功能按键后,Settings 会发出相关的广播,广播在流转中被系统中的 MasterClearReceiver 接收到,从而继续具体的功能。当 MasterClearReceiver 接收到功能指令广播后,会跟 RecoverySystem 通信,在 RecoverySystemService 中经过一系列配置后,通过 PowerManager 的 重启函数进行整机的重启,重启后会进入 Recovery 模式,进行具体的 恢复出厂设置/清除数据 的工作,完成后重新引导系统,切回到主系统运行。

上面是简要描述下 恢复出场设置 的流程,可以发现实际完成 恢复出场设置/清除数据 的工作实际是 Recovery 模式下完成的,主系统在进入 Recovery 前会保存相关指令,在启动时与 Recovery 通信,执行对应的指令。下面我们将会现介绍一下 Recovery 模式相关知识,然后会从恢复出场设置的流程开始介绍整体的功能流转以及具体完成了哪些操作。

二.Recovery 模式

Recovery 是 Android 手机备份功能,指的是一种可以对安卓手机内部的数据或系统进行修改的模式。在这个模式下可以,对已有的系统进行备份或升级,也可以在此恢复出厂设置。

下图是系统进入 recovery 的流程图:

如上图,所描绘的分别是3条进入 recovery 模式的路径:

  • 读取寄存器中的 recovery 标志

  • 组合按键

  • 对 misc 分区和 cache 分区写入指令

读取寄存器中的 recovery 标志

此情形有两种方式触发:adb reboot recovery 和 Powermanager.reboot(…,“recovery”,…)

先看通过 Powermanager 方式进入recovery,powermanager 调用 reboot() 函数,最终往寄存器中写入了一个 recovery 标志,在机器重启时在 BootLoader 中读取该标志,然后进入到 recovery 模式,与adb reboot recovery 方式类似,两者都是通过修改 sys.powerctl 的值来达到目的.

组合按键

组合按键的方式比较简单,在机器重启进入 BootLoader 时,会先检查寄存器中的 recovery 标志,如果没有标志则会检测组合按键是否按下,如果按下则选择进入 recovery 模式。

MSIC 和 CACHE 分区指令进入

此方法一般 root 模式可手动修改 CACHE 分区,还有手机设置中的恢复出厂设置是通过这种方式进入的.

三.恢复出厂流程

1.原生设置中响应 恢复出厂设置 功能,继而发出重置广播(Intent.ACTION_FACTORY_RESET) .

2.frameWork层 接收到此广播,根据广播所携带的参数执行Android层的Reset设定.

3.Android层执行到最后会将Reset配置信息写入 BCB 中,最终进入Recovery

Master Reset 的时序图:

Master Reset 流程分析如下

(一)java层的代码解析

1.framework接收 Reset 广播

在framework/base/core/res/AndroidManifest.xml 中声明 MasterClearReceiver .java静态接收 Reset 广播。

1.	 <receiver android:name="com.android.server.MasterClearReceiver"
2.	            android:permission="android.permission.MASTER_CLEAR">
3.	            <intent-filter
4.	                    android:priority="100" >
5.	                <!-- For Checkin, Settings, etc.: action=FACTORY_RESET -->
                   //这条广播表示 恢复出厂设置
6.	                <action android:name="android.intent.action.FACTORY_RESET" />
7.	                <!-- As above until all the references to the deprecated MASTER_CLEAR                         get updated to FACTORY_RESET. -->
9.	                <action android:name="android.intent.action.MASTER_CLEAR" />
10.	
11.	                <!-- MCS always uses REMOTE_INTENT: category=MASTER_CLEAR -->
12.	                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
13.	                <category android:name="android.intent.category.MASTER_CLEAR" />
14.	            </intent-filter>
15.	        </receiver>
2.MasterClearReceiver .onReceive()

在 MasterClearReceiver .java 执行 onReceive() 方法

framework/base/services/core/java/com/android/server/MasterClearReceiver .java

1. public void onReceive(final Context context, final Intent intent) {
2.	        if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
3.	            if (!"google.com".equals(intent.getStringExtra("from"))) {
4.	                Slog.w(TAG, "Ignoring master clear request -- not from trusted server.");
5.	                return;
6.	            }
7.	        }
8.	        if (Intent.ACTION_MASTER_CLEAR.equals(intent.getAction())) {
9.	            Slog.w(TAG, "The request uses the deprecated Intent#ACTION_MASTER_CLEAR, "
10.	                    + "Intent#ACTION_FACTORY_RESET should be used instead.");
11.	        }
12.	        if (intent.hasExtra(Intent.EXTRA_FORCE_MASTER_CLEAR)) {
13.	            Slog.w(TAG, "The request uses the deprecated Intent#EXTRA_FORCE_MASTER_CLEAR, "
14.	                    + "Intent#EXTRA_FORCE_FACTORY_RESET should be used instead.");
15.	        }
16.	
17.	        final String factoryResetPackage = context
18.	                .getString(com.android.internal.R.string.config_factoryResetPackage);
           //判断如果广播是Intent.ACTION_FACTORY_RESET,且factoryResetPackage不为空
19.	        if (Intent.ACTION_FACTORY_RESET.equals(intent.getAction())
20.	                && !TextUtils.isEmpty(factoryResetPackage)) {
21.	            intent.setPackage(factoryResetPackage).setComponent(null);
            //重新将广播发出去
22.	            context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
23.	            return;
24.	        }
         
25.	        // 从广播中获取shutdown、reason、forceWipe、mWipeEsims等参数的值
           //mWipeExternalStorage代表是否需要擦除外部储存的标志
          // mWipeEsims代表是否需要擦除eSIM的标志
26.	        final boolean shutdown = intent.getBooleanExtra("shutdown", false);
27.	        final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
28.	        mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
29.	        mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
30.	        final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
31.	                || intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false);
32.	
33.	        Slog.w(TAG, "!!! FACTORY RESET !!!");
34.	        // The reboot call is blocking, so we need to do it on another thread.
            // 创建一个新线程thr
35.	        Thread thr = new Thread("Reboot") {
36.	            @Override
37.	            public void run() {
38.	                try {
39.	                    RecoverySystem
40.	                            .rebootWipeUserData(context, shutdown, reason, forceWipe,       mWipeEsims);
41.	                    Log.wtf(TAG, "Still running after master clear?!");
42.	                } catch (IOException e) {
43.	                    Slog.e(TAG, "Can't perform master clear/factory reset", e);
44.	                } catch (SecurityException e) {
45.	                    Slog.e(TAG, "Can't perform master clear/factory reset", e);
46.	                }
47.	            }
48.	        };
49.	
50.	        if (mWipeExternalStorage) {
51.	            // thr will be started at the end of this task.
52.	            new WipeDataTask(context, thr).execute();
53.	        } else {
54.	            thr.start();
55.	        }
56.	    }

查看MasterClearReceiver代码,onReceive接收到广播时,判断如果广播是Intent.ACTION_FACTORY_RESET,且factoryResetPackage不为空,就重新将广播发出去,return退出。之后会创建一个新线程thr,该线程内部会重启并擦除用户数据,但是thr线程并不会立刻执行,而是会判断是否需要擦除外置存储卡或者sim卡中的数据,如果需要则会创建WipeDataTask对象,该对象内部会调用StorageManager的wipeAdoptableDisks方法清除外置存储卡的数据。
在调用StorageManager的wipeAdoptableDisks清除完外置存储卡中的数据之后,会执行thr线程的start方法,触发run方法,调用RecoverySystem的rebootWipeUserData方法。

3.RecoverySystem.rebootWipeUserData()

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

RecoverySystem的rebootWipeUserData方法如下所示。

1.	public static void rebootWipeUserData(Context context, boolean shutdown, String 				reason, boolean force, boolean wipeEuicc) throws IOException {
    // 检查是否允许执行工厂重置,如果不允许则抛出SecurityException
3.	        UserManager um = (UserManager) 															context.getSystemService(Context.USER_SERVICE);
4.	        if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
5.	            throw new SecurityException("Wiping data is not allowed for this user.");
6.	        }
7.	        final ConditionVariable condition = new ConditionVariable();
8.	//发送Intent.ACTION_MASTER_CLEAR_NOTIFICATION广播,并等待其完成
9.	        Intent intent = new  																		Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
10.	        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
11.	                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
12.	        context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,
13.	                android.Manifest.permission.MASTER_CLEAR,
14.	                new BroadcastReceiver() {
15.	                    @Override
16.	                    public void onReceive(Context context, Intent intent) {
17.	                        condition.open();
18.	                    }
19.	                }, null, 0, null, null);
20.	
21.	        // Block until the ordered broadcast has completed.
22.	        condition.block();
23.	//如果需要擦除eUICC数据,则调用wipeEuiccData()执行擦除操作。否则调用removeEuiccInvisibleSubs()
24.	        EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
25.	        if (wipeEuicc) {
26.	            wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
27.	        } else {
28.	            removeEuiccInvisibleSubs(context, euiccManager);
29.	        }
30.	
31.	        String shutdownArg = null;
32.	        if (shutdown) {
33.	            shutdownArg = "--shutdown_after";
34.	        }
35.	
36.	        String reasonArg = null;
37.	        if (!TextUtils.isEmpty(reason)) {
38.	            String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", 							System.currentTimeMillis()).toString();
39.	            reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
40.	        }
41.	
42.	        final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
43.	        Log.i(TAG," RecoverySystem.java rebootWipeUserData");
44.	        bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
45.	    }

rebootWipeUserData方法执行过程中,会发送封装好的参数 --wipe_data,–locale,然后调用bootCommand方法。

4.RecoverySystem.bootCommand()

BootCommand方法如下:

1.	private static void bootCommand(Context context, String... args) throws IOException {
2.	        LOG_FILE.delete();
3.	//构建command字符串,将args中的每个参数拼接并换行
4.	        StringBuilder command = new StringBuilder();
5.	        for (String arg : args) {
6.	            if (!TextUtils.isEmpty(arg)) {
7.	                command.append(arg);
8.	                command.append("\n");
9.	            }
10.	        }
11.	
12.	        // Write the command into BCB (bootloader control block) and boot from
13.	        // there. Will not return unless failed.
14.	        RecoverySystem rs = (RecoverySystem)                                                     context.getSystemService(Context.RECOVERY_SERVICE);
15.	        Log.i(TAG," RecoverySystem.java bootcommand");
16.	        rs.rebootRecoveryWithCommand(command.toString());
17.	
18.	        throw new IOException("Reboot failed (no permissions?)");
19.	    }

这个方法会把传入的命令参数写入bootCommand中,bootCommand方法会进一步调用RecoverySystemService的rebootRecoveryWithCommand方法。

5.RecoverySystemService.rebootRecoveryWithCommand()

frameworks/base/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java

RecoverySystemService的rebootRecoveryWithCommand方法如下:

1.	public void rebootRecoveryWithCommand(String command) {
2.	        if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
3.	        synchronized (sRequestLock) {
             //setupOrClearBcb将之前传递过来的参数写入BCB中
4.	            if (!setupOrClearBcb(true, command)) {
5.	                return;
6.	            }
7.	
8.	          //   Having set up the BCB, go ahead and reboot.
9.	            PowerManager pm = mInjector.getPowerManager();           
10.	            pm.reboot(PowerManager.REBOOT_RECOVERY);     
11.	        }
12.	    }
private boolean setupOrClearBcb(boolean isSetup, String command) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
       //检查解密服务是否可用,如果不可用直接返回false
        final boolean available = checkAndWaitForUncryptService();
        if (!available) {
            Slog.e(TAG, "uncrypt service is unavailable.");
            return false;
        }
		// 根据isSetup参数决定是设置BCB(setup-bcb)还是清除BCB(clear-bcb)
        if (isSetup) {
            mInjector.systemPropertiesSet("ctl.start", "setup-bcb");
        } else {
            mInjector.systemPropertiesSet("ctl.start", "clear-bcb");
        }
        //通过socket与 uncrypt通信,将之前的参数发送给 uncrypt
        // Connect to the uncrypt service socket.
        UncryptSocket socket = mInjector.connectService();
        if (socket == null) {
            Slog.e(TAG, "Failed to connect to uncrypt socket");
            return false;
        }

        try {
            // Send the BCB commands if it's to setup BCB.
            if (isSetup) {
                socket.sendCommand(command);
            }

            // Read the status from the socket.
            int status = socket.getPercentageUncrypted();

            // Ack receipt of the status code. uncrypt waits for the ack so
            // the socket won't be destroyed before we receive the code.
            socket.sendAck();
           //根据状态码判断设置/清除BCB是否成功,成功则返回true,失败返回false
            if (status == 100) {
                Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear")
                        + " bcb successfully finished.");
            } else {
                // Error in /system/bin/uncrypt.
                Slog.e(TAG, "uncrypt failed with status: " + status);
                return false;
            }
        } catch (IOException e) {
            Slog.e(TAG, "IOException when communicating with uncrypt:", e);
            return false;
        } finally {
            socket.close();
        }

        return true;
    }

uncrypt.cpp对程序启动的参数分别处理,当参数为“setup-bcb”时调用setup_bcb()方法

bootable/recovery/uncrypt/uncrypt.cpp

static bool setup_bcb(const int socket) {
    // c5. receive message length
    int length;
    if (!android::base::ReadFully(socket, &length, 4)) {
        PLOG(ERROR) << "failed to read the length";
        return false;
    }
    length = ntohl(length);

    // c7. receive message
    std::string content;
    content.resize(length);
    if (!android::base::ReadFully(socket, &content[0], length)) {
        PLOG(ERROR) << "failed to read the message";
        return false;
    }
    LOG(INFO) << "  received command: [" << content << "] (" << content.size() << ")";
    std::vector<std::string> options = android::base::Split(content, "\n");
    std::string wipe_package;
    for (auto& option : options) {
        if (android::base::StartsWith(option, "--wipe_package=")) {
            std::string path = option.substr(strlen("--wipe_package="));
            if (!android::base::ReadFileToString(path, &wipe_package)) {
                PLOG(ERROR) << "failed to read " << path;
                return false;
            }
            option = android::base::StringPrintf("--wipe_package_size=%zu", wipe_package.size());
        }
    }

    // c8. setup the bcb command
    //write_bootloader_message()方法将之前传递过来的参数写入BCB
    std::string err;
    if (!write_bootloader_message(options, &err)) {
        LOG(ERROR) << "failed to set bootloader message: " << err;
        //成功写入参数后,调用这个函数,返回状态值100
        write_status_to_socket(-1, socket);
        return false;
    }
    if (!wipe_package.empty() && !write_wipe_package(wipe_package, &err)) {
        PLOG(ERROR) << "failed to set wipe package: " << err;
        write_status_to_socket(-1, socket);
        return false;
    }
    // c10. send "100" status
    write_status_to_socket(100, socket);
    return true;
}

uncrypt.cpp对程序启动的参数分别处理,当参数为“setup-bcb”时调用setup_bcb()方法

bootable/recovery/uncrypt/uncrypt.cpp

static bool setup_bcb(const int socket) {
    // c5. receive message length
    int length;
    if (!android::base::ReadFully(socket, &length, 4)) {
        PLOG(ERROR) << "failed to read the length";
        return false;
    }
    length = ntohl(length);

    // c7. receive message
    std::string content;
    content.resize(length);
    if (!android::base::ReadFully(socket, &content[0], length)) {
        PLOG(ERROR) << "failed to read the message";
        return false;
    }
    LOG(INFO) << "  received command: [" << content << "] (" << content.size() << ")";
    std::vector<std::string> options = android::base::Split(content, "\n");
    std::string wipe_package;
    for (auto& option : options) {
        if (android::base::StartsWith(option, "--wipe_package=")) {
            std::string path = option.substr(strlen("--wipe_package="));
            if (!android::base::ReadFileToString(path, &wipe_package)) {
                PLOG(ERROR) << "failed to read " << path;
                return false;
            }
            option = android::base::StringPrintf("--wipe_package_size=%zu", wipe_package.size());
        }
    }

    // c8. setup the bcb command
    //write_bootloader_message()方法将之前传递过来的参数写入BCB
    std::string err;
    if (!write_bootloader_message(options, &err)) {
        LOG(ERROR) << "failed to set bootloader message: " << err;
        //成功写入参数后,调用这个函数,返回状态值100
        write_status_to_socket(-1, socket);
        return false;
    }
    if (!wipe_package.empty() && !write_wipe_package(wipe_package, &err)) {
        PLOG(ERROR) << "failed to set wipe package: " << err;
        write_status_to_socket(-1, socket);
        return false;
    }
    // c10. send "100" status
    write_status_to_socket(100, socket);
    return true;
}

写入参数的操作在这里完成:

write_bootloader_message()方法将之前传递过来的参数写入BCB。由bootloader_message.cpp中write_misc_partition()方法实现

bootable/recovery/bootloader_message/bootloader_message.cpp

这个方法实现了向misc分区的块设备文件写入数据的功能。

bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
                          size_t offset, std::string* err) {
  android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
  if (fd == -1) {
    *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
                                       strerror(errno));
    return false;
  }
    //通过lseek()将文件指针移动到offset偏移位置
  if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
    *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
                                       strerror(errno));
    return false;
  }
    //通过WriteFully()将p指向的缓冲区的数据写入fd文件描述符,大小为size个字节
  if (!android::base::WriteFully(fd, p, size)) {
    *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
                                       strerror(errno));
    return false;
  }
    //通过fsync()刷新文件缓冲,将数据写入物理设备
  if (fsync(fd) == -1) {
    *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
                                       strerror(errno));
    return false;
  }
  return true;
}

将参数写入misc分区后,将状态值一步步返回到setupOrClearBcb()中,然后执行重启操作

pm.reboot(PowerManager.REBOOT_RECOVERY)重启进入recovery模式

这个方法会调用PowerManager的reboot方法来重启系统。

6.PowerManager.reboot()

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

1.	 public void reboot(@Nullable String reason) {
2.	        if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
3.	            throw new UnsupportedOperationException(
4.	              "Attempted userspace reboot on a device that doesn't support it");
5.	        }
6.	        try {
7.	            mService.reboot(false, reason, true);            
8.	        } catch (RemoteException e) {
9.	            throw e.rethrowFromSystemServer();
10.	        }
11.	    }

PowerManager的reboot会进一步调用PowerManagerService的reboot,第一个参数如果为true,会显示一个确认弹窗,这里设置为false表示不需要确认弹窗。

7.PowerManagerService.reboot()

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

PowerManagerService的reboot方法如下所示:

1.	public void reboot(boolean confirm, @Nullable String reason, boolean wait) {
    //检查调用方是否有REBOOT和RECOVERY权限
2.	    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, 				null);
3.	            if (PowerManager.REBOOT_RECOVERY.equals(reason)
4.	                    || PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
5.	mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
6.	            }
7.	
8.	            final long ident = Binder.clearCallingIdentity();
9.	            try {
10.	                Log.i(TAG," powermanagerservice.java reboot start");
11.	                (HALT_MODE_REBOOT, confirm, reason, wait);
12.	                Log.i(TAG," powermanagerservice.java reboot---                                                                    >shutdownOrRebootInternal");
13.	            } finally {
14.	                Binder.restoreCallingIdentity(ident);
15.	            }
16.	        }
17.	    private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean               confirm,@Nullable final String reason, boolean wait) {
19.	        if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
20.	            if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
21.	                throw new UnsupportedOperationException(
22.	                 "Attempted userspace reboot on a device that doesn't support it");
23.	            }
24.	            UserspaceRebootLogger.noteUserspaceRebootWasRequested();
25.	        }
26.	        if (mHandler == null || !mSystemReady) {
27.	            if (RescueParty.isAttemptingFactoryReset()) {
28.	                // If we're stuck in a really low-level reboot loop, and a
29.	                // rescue party is trying to prompt the user for a factory data
30.	                // reset, we must GET TO DA CHOPPA!
31.	                PowerManagerService.lowLevelReboot(reason);           
32.	            } else {
33.	                throw new IllegalStateException("Too early to call shutdown() or                                                                               reboot()");
34.	            }
35.	        }
36. 	//创建一个Runnable,在run()方法中根据haltMode的值来调用ShutdownThread的reboot()、     rebootSafeMode()或shutdown()方法执行重启或关机操作
37.	        Runnable runnable = new Runnable() {
38.	            @Override
39.	            public void run() {
40.	                synchronized (this) {
41.	                    if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
42.	                        ShutdownThread.rebootSafeMode(getUiContext(), confirm);
43.	                    } else if (haltMode == HALT_MODE_REBOOT) {
44.	                         //走到这里
45.	                        ShutdownThread.reboot(getUiContext(), reason, confirm);
46.	                    } else {
47.	                        ShutdownThread.shutdown(getUiContext(), reason, confirm);
48.	                    }
49.	                }
50.	            }
51.	        };
52.	
53.	        // ShutdownThread must run on a looper capable of displaying the UI.
54.	        Message msg = Message.obtain(UiThread.getHandler(), runnable);
55.	        msg.setAsynchronous(true);
56.	        UiThread.getHandler().sendMessage(msg);
57.	
58.	        // PowerManager.reboot() is documented not to return so just wait for the                   inevitable.
           //PowerManager.reboot()被记录为不会返回,所以只需等待不可避免的结果。
59.	        if (wait) {
60.	            synchronized (runnable) {
61.	                while (true) {
62.	                    try {
63.	                        runnable.wait();
64.	                    } catch (InterruptedException e) {
65.	                    }
66.	                }
67.	            }
68.	        }
69.	    }

PowerManagerService的reboot方法会首先检查是否有REBOOT和RECOVERY权限,然后调用shutdownOrRebootInternal()方法,最后会触发ShutdownThread的reboot方法。

8.ShutdownThread.reboot()

frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java

ShutdownThread的reboot方法如下所示、

1.	public static void reboot(final Context context, String reason, boolean confirm) {
2.	        mReboot = true;
3.	        mRebootSafeMode = false;
4.	        mRebootHasProgressBar = false;
5.	        mReason = reason;
6.	         //走到这里
7.	        shutdownInner(context, confirm);
8.	    }
9.	    private static void shutdownInner(final Context context, boolean confirm) {
10.	        // ShutdownThread is called from many places, so best to verify here that the                context passed
11.	        // in is themed.
12.	        context.assertRuntimeOverlayThemable();
13.	
14.	        // ensure that only one thread is trying to power down.
15.	        // any additional calls are just returned
           // 检查sIsStarted标志位,确保重启操作没有已经启动,防止重复调用
16.	        synchronized (sIsStartedGuard) {
17.	            if (sIsStarted) {
18.	                Log.d(TAG, "Request to shutdown already running, returning.");
19.	                return;
20.	            }
21.	        }
22.	//根据longPressBehavior和mRebootSafeMode的值决定显示哪个确认对话框
23.	        final int longPressBehavior = context.getResources().getInteger(
24.	        com.android.internal.R.integer.config_longPressOnPowerBehavior);
25.	        final int resourceId = mRebootSafeMode
26.	                ? com.android.internal.R.string.reboot_safemode_confirm
27.	                : (longPressBehavior == 2
28.	                        ? com.android.internal.R.string.shutdown_confirm_question
29.	                        : com.android.internal.R.string.shutdown_confirm);
30.	
31.	        Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" +                            longPressBehavior);
32.	   //如果confirm为true,则显示确认对话框
33.	        if (confirm) {
34.	            final CloseDialogReceiver closer = new CloseDialogReceiver(context);
35.	            if (sConfirmDialog != null) {
36.	                sConfirmDialog.dismiss();
37.	            }
38.	            sConfirmDialog = new AlertDialog.Builder(context)
39.	                    .setTitle(mRebootSafeMode
40.	                            ? com.android.internal.R.string.reboot_safemode_title
41.	                            : com.android.internal.R.string.power_off)
42.	                    .setMessage(resourceId)
43.	                    .setPositiveButton(com.android.internal.R.string.yes, new                                    DialogInterface.OnClickListener() {
44.	                        public void onClick(DialogInterface dialog, int which) {
45.	                            beginShutdownSequence(context);
46.	                        }
47.	                    })
48.	                    .setNegativeButton(com.android.internal.R.string.no, null)
49.	                    .create();
50.	            closer.dialog = sConfirmDialog;
51.	            sConfirmDialog.setOnDismissListener(closer);
52.	            sConfirmDialog.getWindow().
                           setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
53.	            sConfirmDialog.show();
54.	        } else {// 如果confirm为false,则直接调用beginShutdownSequence启动关机序列
55.	            beginShutdownSequence(context);
56.	        }
57.	    }

reboot方法进一步调用shutdownInner方法,shutdownInner首先判断是否需要显示确认弹窗,由于时恢复出厂设置,这里为false表示不需要显示确认弹窗,会直接调用beginShutdownSequence方法。

9.ShutdownThread.beginShutdownSequence()

ShutdownThread的beginShutdownSequence方法如下所示。

1. private static void beginShutdownSequence(Context context) {
2.	        synchronized (sIsStartedGuard) {
    //检查sIsStarted标志位,确保关机序列没有已经启动,防止重复启动
3.	            if (sIsStarted) {
4.	                Log.d(TAG, "Shutdown sequence already running, returning.");
5.	                return;
6.	            }
7.	            sIsStarted = true;
8.	        }
9.	//显示shutdownDialog对话框并获取Context
10.	        sInstance.mProgressDialog = showShutdownDialog(context);
11.	        sInstance.mContext = context;
    //获取PowerManager实例,用于后续获取WakeLock
12.	        sInstance.mPowerManager =                                                                     (PowerManager)context.getSystemService(Context.POWER_SERVICE);
13.	
14.	        // make sure we never fall asleep again
15.	        sInstance.mCpuWakeLock = null;
16.	        try {
17.	            sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
18.	                    PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
19.	            sInstance.mCpuWakeLock.setReferenceCounted(false);
20.	            sInstance.mCpuWakeLock.acquire();
21.	        } catch (SecurityException e) {
22.	            Log.w(TAG, "No permission to acquire wake lock", e);
23.	            sInstance.mCpuWakeLock = null;
24.	        }
25.	
26.	        // also make sure the screen stays on for better user experience
        //检查屏幕是否已开启,如果是则获取一个FULL_WAKE_LOCK权限的ScreenWakeLock,保持屏幕常亮
27.	        sInstance.mScreenWakeLock = null;
28.	        if (sInstance.mPowerManager.isScreenOn()) {
29.	            try {
30.	                sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
31.	                        PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
32.	                sInstance.mScreenWakeLock.setReferenceCounted(false);
33.	                sInstance.mScreenWakeLock.acquire();
34.	            } catch (SecurityException e) {
35.	                Log.w(TAG, "No permission to acquire wake lock", e);
36.	                sInstance.mScreenWakeLock = null;
37.	            }
38.	        }
39.	
40.	        if (SecurityLog.isLoggingEnabled()) {
41.	            SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
42.	        }
43.	
44.	        // start the thread that initiates shutdown
           //创建Handler并启动关机线程
45.	        sInstance.mHandler = new Handler() {
46.	        };
47.	      // 启动线程
48.	        sInstance.start();
49.	    }

这个方法会做一些处理:

显示一个系统进度dialog表示当前设备正在关机/重启;
持CPU锁、屏幕锁,保持设备处于唤醒态、屏幕处于亮屏态;
最后启动线程,执行run方法,这个线程指的是ShutdownThread.java,执行的run()方法,也是这个类的run()方法。方法太长,主要是做一些重启前的准备工作,可以直接看最后调用的函数。

1. public void run() {
2.	        TimingsTraceLog shutdownTimingLog = newTimingsLog();
3.	        shutdownTimingLog.traceBegin("SystemServerShutdown");
4.	        metricShutdownStart();
5.	        metricStarted(METRIC_SYSTEM_SERVER);
6.	
7.	        BroadcastReceiver br = new BroadcastReceiver() {
8.	            @Override public void onReceive(Context context, Intent intent) {
9.	                // We don't allow apps to cancel this, so ignore the result.
10.	                actionDone();
11.	            }
12.	        };
13.	 ……
14.	        shutdownTimingLog.traceEnd(); // SystemServerShutdown
15.	        metricEnded(METRIC_SYSTEM_SERVER);
16.	        saveMetrics(mReboot, mReason);
17.	        // Remaining work will be done by init, including vold shutdown
            // 重启前的准备工作结束, 开始重启
18.	        rebootOrShutdown(mContext, mReboot, mReason);
19.	    }

最终调用到shutdownThread.java的rebootOrShutdown()方法

10.shutdownThread.rebootOrShutdown()
1.	    public static void rebootOrShutdown(final Context context, boolean reboot, String 				reason) {
    // 如果reboot为true,则先调用PowerManagerService.lowLevelReboot()尝试重启。如果重启失败,则将reason设置为null,并进行关机操作
2.	        if (reboot) {
3.	            Log.i(TAG, "Rebooting, reason: " + reason);
4.	            PowerManagerService.lowLevelReboot(reason);
5.	            Log.e(TAG, "Reboot failed, will attempt shutdown instead");
6.	            reason = null;
7.	        } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
8.	            // vibrate before shutting down(关闭前振动)
9.	            Vibrator vibrator = new SystemVibrator(context);
10.	            try {
11.	                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
12.	            } catch (Exception e) {
13.	                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                    //振动故障不应中断停机。只需记录即可。
14.	                Log.w(TAG, "Failed to vibrate during shutdown.", e);
15.	            }
16.	
17.	            // vibrator is asynchronous so we need to wait to avoid shutting down too                    soon.(可控震源是异步的,所以我们需要等待以避免过早关闭。)
18.	            try {
19.	                Thread.sleep(SHUTDOWN_VIBRATE_MS);
20.	            } catch (InterruptedException unused) {
21.	            }
22.	        }
23.	        // Shutdown power
24.	        Log.i(TAG, "Performing low-level shutdown...");
25.	        PowerManagerService.lowLevelShutdown(reason);
26.	    }

判断是否是重启,如果是重启的话则调用:PowerManagerService.lowLevelReboot(reason);

11.PowerManagerService.lowLevelReboot()

PowerManagerService.java的lowLevelReboot()方法如下

 public static void lowLevelReboot(String reason) {
     //检查reason是否为空,如果是则设置为空字符串
2.	        if (reason == null) {
3.	            reason = "";
4.	        }
5.	
6.	        // If the reason is "quiescent", it means that the boot process should 						proceed
7.	        // without turning on the screen/lights.
8.	        // The "quiescent" property is sticky, meaning that any number
9.	        // of subsequent reboots should honor the property until it is reset.
    
    //如果reason为"quiescent"或以",quiescent"结尾,则设置sQuiescent标志位,并截取reason
10.	        if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
11.	            sQuiescent = true;
12.	            reason = "";
13.	        } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
14.	            sQuiescent = true;
15.	            reason = reason.substring(0,
16.	                    reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
17.	        }
18.	// 如果reason为REBOOT_RECOVERY或REBOOT_RECOVERY_UPDATE,则将reason设置为"recovery"
19.	        if (reason.equals(PowerManager.REBOOT_RECOVERY)
20.	                || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
21.	         // 走的这里
22.	            reason = "recovery";
23.	        }    
24.	// 如果sQuiescent被设置,则将",quiescent"追加到reason的末尾
25.	        if (sQuiescent) {
28.	            reason = reason + ",quiescent";
29.	        }
30.	        // 设置这个prop触发init进程重启进入recovery模式
31.	        SystemProperties.set("sys.powerctl", "reboot," + reason);
32.	        try {
33.	            Thread.sleep(20 * 1000L);
34.	        } catch (InterruptedException e) {
35.	            Thread.currentThread().interrupt();
36.	        }
37.	        Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
38.	    }

首先判断重启原因,根据reason的不同,传入不同的命令参数进入native层(此处reason为 recovery),开始重启操作。

 (二)native层的解析

设备重启后,会自动进入recovery mode模式,读取BCB的信息 ,开始清除data和cache分区,清除成功后,系统重启,然后进入正常开机流程,重新使用system分区的内容完成开机初始化。

重启后,从recovery模式的init.rc文件中可以看到启动recovery服务,具体可参考bootable/recovery/etc/init.rc文件,代码片段如下:

recovery服务的主函数在bootable/recovery/recovery.c文件中,main函数的主要流程分析如下:

1. recovery_main.cpp

首先进入recovery_main.cpp中

1.	int main(int argc, char** argv) {
2.	……
3.	  load_volume_table();  //加载挂载分区
4.	
5.	  std::string stage;
6.	//从/misc分区读取之前写入到BCB中的信息,赋值给变量args
7.	  std::vector<std::string> args = get_args(argc, argv, &stage);
8.	  auto args_to_parse = StringVectorToNullTerminatedArray(args);
9.	
10.	 ……
11.	  int arg;
12.	  int option_index;
13.	  //对args信息进行一些处理
14.	  while ((arg = getopt_long(args_to_parse.size() - 1, args_to_parse.data(), "", 				OPTIONS, &option_index)) != -1) {
15.	    switch (arg) {
16.	      case 't':
17.	        show_text = true;
18.	        break;
19.	      case 0: {
20.	        std::string option = OPTIONS[option_index].name;
21.	        if (option == "locale") {
22.	          locale = optarg;
23.	        } else if (option == "reason") {
24.	          reason = optarg;
25.	          LOG(INFO) << "reason is [" << reason << "]";
26.	        } else if (option == "fastboot" &&
27.	                   android::base::GetBoolProperty("ro.boot.dynamic_partitions", 					false)) {
28.	          fastboot = true;
29.	        }
30.	        break;
31.	      }
32.	    }
33.	  }
34.	 ……
35.	//while(true)循环体中判断start_recovery()返回参数
36.	  while (true) {
37.	   ……
38.	    auto ret = fastboot ? StartFastboot(device, args) : start_recovery(device, args);
39.	
40.	    if (ret == Device::KEY_INTERRUPTED) {
41.	      ret = action.exchange(ret);
42.	      if (ret == Device::NO_ACTION) {
43.	        continue;
44.	      }
45.	    }
46.	    switch (ret) {
47.	      case Device::SHUTDOWN:
48.	        ui->Print("Shutting down...\n");
49.	        Shutdown("userrequested,recovery");
50.	        break;
51.	……
52.	
53.	      case Device::REBOOT:
54.	        ui->Print("Rebooting...\n");
55.	        Reboot("userrequested,recovery");
56.	        break;
57.	……
58.	    }
59.	  }
60.	
61.	  // Should be unreachable.
62.	  return EXIT_SUCCESS;
63.	}

对args信息进行一些处理后,判断是否是StartFastboot()或则start_recovery(),这里选择的是start_recovery(),while(true)循环体中判断start_recovery()返回参数,以此执行下一步操作。

2. Recovery.cpp

Recovery.cpp的start_recovery()方法如下:

1.	Device::BuiltinAction start_recovery(Device* device, const std::vector<std::string>& 		args) {
2.	  static constexpr struct option OPTIONS[] = {
3.	    { "fastboot", no_argument, nullptr, 0 },
4.	 ……
5.	    { "wipe_cache", no_argument, nullptr, 0 },
6.	    { "wipe_data", no_argument, nullptr, 0 },
7.	    { "wipe_package_size", required_argument, nullptr, 0 },
8.	    { nullptr, 0, nullptr, 0 },
9.	  };
10.	  ……
11.	// args参数被处理赋值给args_to_parse变量
12.	  auto args_to_parse = StringVectorToNullTerminatedArray(args);
13.	  int arg;
14.	  int option_index;
15.	//通过getopt_long()对参数进行判断选择对应的操作
16.	  while ((arg = getopt_long(args_to_parse.size() - 1, args_to_parse.data(), "", 				OPTIONS, &option_index)) != -1) {
18.	    switch (arg) {
19.	      case 't':
20.	        // Handled in recovery_main.cpp
21.	        break;
22.	      case 'x':
23.	        just_exit = true;
24.	        break;
25.	      case 0: {
26.	        std::string option = OPTIONS[option_index].name;
27.	        if (option == "install_with_fuse") {
28.	          install_with_fuse = true;
29.	        } else if (option == "locale" || option == "fastboot" || option == "reason") {
30.	          // Handled in recovery_main.cpp
31.	  ……
           // 根据传参的内容为 wipe_data 走到这里
32.	        } else if (option == "wipe_data") {
33.	          should_wipe_data = true;
34.	        } else if (option == "wipe_package_size") {
35.	          android::base::ParseUint(optarg, &wipe_package_size);
36.	        }
37.	        break;
38.	      }
39.	      case '?':
40.	        LOG(ERROR) << "Invalid command argument";
41.	        continue;
42.	    }
43.	  }
44.	  optind = 1;
45.	
46.	  // next_action indicates the next target to reboot into upon finishing the install. 			It could be
47.	  // overridden to a different reboot target per user request.
48.	  Device::BuiltinAction next_action = shutdown_after ? Device::SHUTDOWN : 						Device::REBOOT;
49.	  ……
50.	
51.	  if (update_package != nullptr) {
52.	    ……
    //走到这里
53.	  } else if (should_wipe_data) {
54.	    save_current_log = true;
55.	    CHECK(device->GetReason().has_value());
56.	    bool convert_fbe = device->GetReason().value() == "convert_fbe";
    // 调用 wipeData函数
57.	    if (!WipeData(device, convert_fbe)) {
58.	      status = INSTALL_ERROR;
59.	    }
60.	  } 
61.	  ……
62.	
63.	error:
64.	  if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
65.	    ui->SetBackground(RecoveryUI::ERROR);
66.	    if (!ui->IsTextVisible()) {
67.	      sleep(5);
68.	    }
69.	  }
70.	……

 和上面的main()方法一样,args参数被处理赋值给args_to_parse变量,通过getopt_long()对参数进行判断选择对应的操作,option == "wipe_data"时,should_wipe_data = true,之后通过判断变量should_wipe_data调用WipeData(device, convert_fbe)方法清除用户数据.

3. Wipe_data.cpp

Wipe_data.cpp的WipeData()方法如下:

1.	bool WipeData(Device* device, bool convert_fbe) {
2.	  RecoveryUI* ui = device->GetUI();
3.	  ui->Print("\n-- Wiping data...\n");
4.	//检查更新状态和是否完成合并
5.	  if (!FinishPendingSnapshotMerges(device)) {
6.	    ui->Print("Unable to check update status or complete merge, cannot wipe 				partitions.\n");
7.	    return false;
8.	  }
9.	//调用PreWipeData()做擦除前准备,结果保存到success
10.	  bool success = device->PreWipeData();
11.	  if (success) {
    // 擦除/data分区,按convert_fbe决定是否转换格式
12.	    success &= EraseVolume(DATA_ROOT, ui, convert_fbe);
13.	    bool has_cache = volume_for_mount_point("/cache") != nullptr;
14.	    if (has_cache) {
15.	      success &= EraseVolume(CACHE_ROOT, ui, false);
16.	    }
17.	    if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
18.	      success &= EraseVolume(METADATA_ROOT, ui, false);
19.	    }
20.	  }
21.	  if (success) {
22.	    success &= device->PostWipeData();
23.	  }
24.	  ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
25.	  return success;
26.
27.	}

WipeData() 方法对 /data,/cache,/metadata分区进行格式化操作。这里可以对需要格式化的分区进行自定义。之后start_recovery()返回Device::REBOOT,设备重启。

四.总结

1、应用可以通过发送 android.intent.action.FACTORY_RESET 广播,来启用原生的恢复出场设置功能。当然需要申请权限(android:permission=“android.permission.MASTER_CLEAR”)
2、系统中的 /android/frameworks/base/core/res/AndroidManifest.xml 中定义类广播的接收器为 MasterClearReceiver
3、恢复出场设置接收器 MasterClearReceiver 定义在 /android/frameworks/base/services/core/java/com/android/server 下。同目录下为系统服务
4、MasterClearReceiver 在 onReceiver() 广播接收处理中,会创建一个线程 thr,该线程会调用 RecoverySystem.rebootWipeUserData() 函数进行擦除用户数据。但是 thr 线程不会立即进行,系统会根据传过来的 Intent 中的值判断是否需要擦除外置存储卡或者 sim 卡中的数据,如果需要,会调用 StorageManager.wipeAdoptableDisks() 函数进行外置存储卡或者 sim 卡数据擦除
5、RecoverySystem 是定义在 /android/frameworks/base/core/java/android/os/RecoverySystem.java 中的,是 Android 提供的功能接口。
6、RecoverySystem.rebootWipeUserData() 函数会调用 bootCommand() 函数进行下一步处理。
7、RecoverySystem.bootCommand() 函数会调用 RecoverySystem.rebootRecoveryWithCommand() 函数进行 AIDL 通信,与 RecoverySystemService.java 服务端进行通信。aidl 文件为 /android/frameworks/base/core/java/android/os/IRecoverySystem.aidl, 服务端文件为 /android/frameworks/base/services/core/java/com/android/server/RecoverySystemService.java
8、RecoverySystemService.rebootRecoveryWithCommand() 函数首先会调用 setupOrCleanBcb() 函数,然后调用 PowerManager.reboot() 函数重启进入 recovery 模式
9、setupOrCleanBcb() 函数会设置 ctl.start 属性(setup-bcb 或者 clear-bcb),并且会通过 Socket 与 uncrypt 服务通信(/system/bin/uncrypt)。通过 ctl.start 属性可以控制 init.rc 中定义的各种 service
10、PowerManager.reboot() 函数是在 /android/frameworks/base/core/java/android/os/PowerManager.java 中,是 Android 提供的接口,reboot() 函数是 aidl 通信,服务端是 PowerManagerService.java,路径为 /android/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java。

11、重启后进入 recovery 模式,recovery 读取 BCB,执行下发的指令,进行恢复出场设置、升级等操作。

12、recovery 模式中实行完毕后,切换回主系统启动。

 

  • 26
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式_笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值