• Android系统恢复出场设置流程分析


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

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

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

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

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

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

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

    • 点击按钮触发恢复出厂设置事件
    • 发送"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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    从上面的代码我们可以知道点击恢复出厂按钮最终是调用了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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 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();//启动重启并擦除用户数据的线程
            }
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    /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());
                }
            }
        }
        ...
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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);
    	    }
    	    ...
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在代码中启动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?)");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    整体流程

  • 相关阅读:
    linux python 保存图形savefig import matplotlib.pyplot as plt
    Innodb是如何实现事务的
    十年老程序员分享13个最常用的Python深度学习库和介绍,赶紧收藏码住!
    VMware Ubuntu虚拟机忘记密码
    Apache开启SSL(https)访问网站配置
    分库分表解决300亿记录存储的三个方案方法
    设计模式:适配器模式
    Flink SQL TopN语句详解
    猿创征文|〖Python 数据库开发实战 - Python与MySQL交互篇⑯〗- 项目实战 - 实现用户管理 - 新增用户功能
    5.基于飞蛾扑火算法(MFO)优化的VMD参数(MFO-VMD)
  • 原文地址:https://blog.csdn.net/abc6368765/article/details/125425733