IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android系统恢复出场设置流程分析 -> 正文阅读

[移动开发]Android系统恢复出场设置流程分析

一、Android恢复出厂设置的三种方式

1【系统设置页面】进入【恢复出厂设置页面】,点击【恢复出厂设置】按钮。

2 直接通过adb发送恢复出厂设置的广播

adb shell am broadcast -a android.intent.action.MASTER_CLEAR

3 使用adb命令修改recovery的command文件,并重启系统进入recovery模式

adb shell 'echo "--wipe_data\n--locale=en_US" > /cache/recovery/command'
adb shell setprop sys.powerctl reboot,recovery

二、在【恢复出厂设置】页面点击【恢复出厂设置】按钮,触发的流程主要有以下几步:

  • 点击按钮触发恢复出厂设置事件
  • 发送"android.intent.action.MASTER_CLEAR"广播
  • 系统MasterClearReceiver接收广播,并进行android层的相关处理最后重启
  • 在/cache/recovery/command文件中写入命令字段
  • 重启系统,进入Recovery模式
  • 根据/cache/recovery/command中的命令字段清楚用户数据
  • 重新启动系统,恢复出厂设置成功

三、点击【恢复出厂设置】按钮,系统源码流程分析

3.1 点击按钮,系统发送恢复出厂设置的广播

在确定【要恢复出厂设置吗】页面点击确定【清除全部内容】按钮,系统会发送恢复出厂设置的广播:

/packages/apps/Settings/src/com/android/settings/MasterClearConfirm.java

package com.android.settings;
...
public class MasterClearConfirm extends OptionsMenuFragment {

    private View mContentView;
    private boolean mEraseSdCard;

	//02用户点击恢复出厂设置按钮触发的点击事件
    private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {

        public void onClick(View v) {
              ...
   		      //03发送恢复出厂设置的广播事件
              doMasterClear();
              ...
   		}
        ...
    };

    //04发送恢复出厂设置的广播事件
    private void doMasterClear() {
    	//ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR"
        Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
        //该参数表示是否擦除SD卡,默认为False 
        intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);
        //发送恢复出厂设置的广播事件
        getActivity().sendBroadcast(intent);
    }

    /**
     * Configure the UI for the final confirmation interaction
     */
    private void establishFinalConfirmationState() {
    	//01为恢复出厂设置按钮添加点击事件
        mContentView.findViewById(R.id.execute_master_clear)
                .setOnClickListener(mFinalClickListener);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        ...
        //设置恢复出厂设置确认页面的布局文件
        mContentView = inflater.inflate(R.layout.master_clear_confirm, null);
        ...
        return mContentView;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle args = getArguments();
        mEraseSdCard = args != null && args.getBoolean(MasterClear.ERASE_EXTERNAL_EXTRA);
    }

    @Override
    protected int getMetricsCategory() {
        return MetricsEvent.MASTER_CLEAR_CONFIRM;
    }
}

从上面的代码我们可以知道点击恢复出厂按钮最终是调用了doMasterClear方法,然后在该方法中发送了一个恢复出厂设置的广播事件。

二、接受恢复出厂设置的广播

3.2 系统MasterClearReceiver接收广播,并进行android层的相关处理

  • MasterClearReceiver广播组件的配置信息:

/framework/base/core/res/AndroidManifest.xml

        <receiver android:name="com.android.server.MasterClearReceiver"
            android:permission="android.permission.MASTER_CLEAR">
            <intent-filter
                    android:priority="100" >
                <!-- For Checkin, Settings, etc.: action=MASTER_CLEAR -->
                <action android:name="android.intent.action.MASTER_CLEAR" />

                <!-- MCS always uses REMOTE_INTENT: category=MASTER_CLEAR -->
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="android.intent.category.MASTER_CLEAR" />
            </intent-filter>
        </receiver>
  • MasterClearReceiver广播组件的源码信息:

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

package com.android.server;
...
public class MasterClearReceiver extends BroadcastReceiver {
    private static final String TAG = "MasterClear";
    @Override
    public void onReceive(final Context context, final Intent intent) {
     	...
        final boolean shutdown = intent.getBooleanExtra("shutdown", false);
        final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
        final boolean wipeExternalStorage = intent.getBooleanExtra( Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
        final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
                || intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false);
        Slog.w(TAG, "!!! FACTORY RESET !!!");
        // 创建一个线程处理重启任务
        Thread thr = new Thread("Reboot") {
            @Override
            public void run() {
                try {
                	//重启并擦除用户信息
                    RecoverySystem.rebootWipeUserData(context, shutdown, reason, forceWipe);
                    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);
                }
            }
        };

        if (wipeExternalStorage) {
            // 线程将会在下面的任务执行完毕以后开始执行
            new WipeAdoptableDisksTask(context, thr).execute();
        } else {
            thr.start();
        }
    }
    
	//继承自AsyncTask类,此处擦除的SdCard指的是Android M(6.0)新加入的
	//内置存储设备的功能中的存储位置SD卡或者USB存储设备,不是传统data分区下的SdCard
	//手机进入Recovery模式后,直接擦除data分区,SdCard目录包含在内,这样SdCard必然会被擦除,无需再做多余操作
    private class WipeAdoptableDisksTask extends AsyncTask<Void, Void, Void> {
        private final Thread mChainedTask;
        private final Context mContext;
        private final ProgressDialog mProgressDialog;

        public WipeAdoptableDisksTask(Context context, Thread chainedTask) {
            mContext = context;
            mChainedTask = chainedTask;
            mProgressDialog = new ProgressDialog(context);
        }

        @Override
        protected void onPreExecute() {
            mProgressDialog.setIndeterminate(true);
            mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));
            mProgressDialog.show();
        }

        @Override
        protected Void doInBackground(Void... params) {
            Slog.w(TAG, "Wiping adoptable disks");
            StorageManager sm = (StorageManager) mContext.getSystemService(
                    Context.STORAGE_SERVICE);
            //具体逻辑请看下面的StorageManager.java对象
            sm.wipeAdoptableDisks();
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            mProgressDialog.dismiss();
            mChainedTask.start();//启动重启并擦除用户数据的线程
        }

    }
}

/frameworks/base/core/java/android/os/storage/StorageManager.java

public class StorageManager {
    ...
    public void wipeAdoptableDisks() {
        // We only wipe devices in "adoptable" locations, which are in a
        // long-term stable slot/location on the device, where apps have a
        // reasonable chance of storing sensitive data. (Apps need to go through
        // SAF to write to transient volumes.)
        final List<DiskInfo> disks = getDisks();
        for (DiskInfo disk : disks) {
            final String diskId = disk.getId();
            if (disk.isAdoptable()) {
                Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
                try {
                    // TODO: switch to explicit wipe command when we have it,
                    // for now rely on the fact that vfat format does a wipe
                    mStorageManager.partitionPublic(diskId);
                } catch (Exception e) {
                    Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
                }
            } else {
                Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
            }
        }
    }
    ...
    }

3.3 RecoverySystem来重启系统,启动擦除用户数据的操作

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

public class RecoverySystem {
	...
	public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
	            boolean force, boolean wipeEuicc) throws IOException {
	        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
	        if (!force && 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
	                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
	        context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,
	                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();
	
	        if (wipeEuicc) {
	            wipeEuiccData(context, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK);
	        }
	
	        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().toLanguageTag() ;
	        bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
	    }
	    ...
    }

在代码中启动bootCommand传递命令时,封装参数 --wipe_data,–locale,这些命令可以在recovery log(/cache/recovery/*.log)信息中看到

Command: “recovery” “–wipe_data” “–locale=zh_CN”

判断是否可以执行恢复出厂设置操作,可以,发送系统广播,设置command参数,随后进入bootCommand方法

2.4 bootCommand方法

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

  private static void bootCommand(Context context, String... args) throws IOException {
        LOG_FILE.delete();

        StringBuilder command = new StringBuilder();
        for (String arg : args) {
            if (!TextUtils.isEmpty(arg)) {
                command.append(arg);
                command.append("\n");
            }
        }

        // Write the command into BCB (bootloader control block) and boot from
        // there. Will not return unless failed.
        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
        rs.rebootRecoveryWithCommand(command.toString());

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

整体流程

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 18:16:18  更:2022-06-25 18:17:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 18:19:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码