美文网首页
Android4.4.2恢复出厂设置(二)

Android4.4.2恢复出厂设置(二)

作者: 发条蛙 | 来源:发表于2017-11-14 20:47 被阅读0次

    Android4.4.2恢复出厂设置(二)
    根据前文所述,UI层最终发送Intent消息MASTER_CLEAR进行恢复出厂设置的操作,这部分的流程如下图所示:

    recovery操作的Framework流程

    MASTER_CLEAR接收器的设定

    经过搜索AndroidManifest.xml文件可知,对MASTER_CLEAR的最终的处理在文件frameworks/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>
    

    而真正的处理函数在文件frameworks/base/services/java/com/android/server/MasterClearReceiver.java中,其具体实现如下:

        @Override
        public void onReceive(final Context context, final Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
                if (!"google.com".equals(intent.getStringExtra("from"))) {
                    Slog.w(TAG, "Ignoring master clear request -- not from trusted server.");
                    return;
                }   
            }   
    
            Slog.w(TAG, "!!! FACTORY RESET !!!");
            // The reboot call is blocking, so we need to do it on another thread.
            Thread thr = new Thread("Reboot") {
                @Override
                public void run() {
                    try {
                        RecoverySystem.rebootWipeUserData(context);
                        Log.wtf(TAG, "Still running after master clear?!");
                    } catch (IOException e) {
                        Slog.e(TAG, "Can't perform master clear/factory reset", e); 
                    }   
                }   
            };  
            thr.start();
        }
    

    这部分的主要工作就是校验请求来源字符串是否为google.com,如果不是则不能操作,如果是,则调用函数RecoverySystem.rebootWipeUserData(context)执行重启并清理数据的操作。

    rebootWipeUserData

    函数rebootWipeUserData实现在文件frameworks/base/core/java/android/os/RecoverySystem.java中:

        public static void rebootWipeUserData(Context context) throws IOException {
            final ConditionVariable condition = new ConditionVariable();
    
            Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
            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();
    
            bootCommand(context, "--wipe_data\n--locale=" + Locale.getDefault().toString());
        } 
    

    这部分的内容很直接,可以理解为是调用函数bootCommand(context, "--wipe_data\n--locale=en_US")进行重启即可。

    bootCommand

    函数bootCommand实现在文件frameworks/base/core/java/android/os/RecoverySystem.java中:

        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);
            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?)");
        }
    

    即将传入参数写入文件COMMAND_FILE然后通通过电源管理重启进入recovery模式。而COMMAND_FILE在该文件中的定义如下:

        private static File RECOVERY_DIR = new File("/cache/recovery");
        private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
    

    也就是说,最终会在文件/cache/recovery/command中写入如下信息:

    --wipe_data
    --locale=en_US
    

    而调用的函数为pm.reboot("recovery")

    reboot

    接下来的函数reboot实现在文件frameworks/base/core/java/android/os/PowerManager.java中,内容如下:

        public void reboot(String reason) {
            try {
                mService.reboot(false, reason, true);
            } catch (RemoteException e) {
            }   
        }
    

    而其调用到的PowerManagerService中的函数位于frameworks/base/services/java/com/android/server/power/PowerManagerService.java中:

        @Override // Binder call
        public void reboot(boolean confirm, String reason, boolean wait) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
    
            final long ident = Binder.clearCallingIdentity();
            try {
                shutdownOrRebootInternal(false, confirm, reason, wait);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }    
        }
    

    在进行了REBOOT权限校验后,调用到了函数shutdownOrRebootInternal(false, false, "recovery", true)继续重启操作。

    shutdownOrRebootInternal

    函数shutdownOrRebootInternal实现在文件frameworks/base/services/java/com/android/server/power/PowerManagerService.java中:

        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) {
                        }
                    }
                }
            }
        }
    

    其传入参数分别表示:

    • shutdown: 为false表示重启;
    • confirm: 为false表示直接重启,无需弹出询问框再次确认;
    • reason: 重启操作类型,如果是恢复出厂设置,则该值为recovery;
    • wait: 为true表示阻塞等待重启操作完成;

    根据传入参数,这里最终是调用到函数ShutdownThread.reboot(mContext, "recovery", false),另外需要注意的是这里需要运行在具有UI显示的循环中。

    reboot

    ShutdownThread.reboot(mContext, "recovery", false)函数实现在文件frameworks/base/services/java/com/android/server/power/ShutdownThread.java中:

        public static void reboot(final Context context, String reason, boolean confirm) {
            mReboot = true;
            mRebootSafeMode = false;
            mRebootReason = reason;
            shutdownInner(context, confirm);
        }
    

    调用到的函数为shutdownInner(context, false),另外这里设置的mReboot为true,则表示为重启操作,否则表示关机操作,而mRebootReason的取值为recovery字符串。

    shutdownInner

    函数shutdownInner依然实现在文件frameworks/base/services/java/com/android/server/power/ShutdownThread.java中:

        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);
            }   
        }
    

    这里的处理,其实主要是针对通过长按power键进入重启或模式的处理,对于通过Settings进入的,相当于直接调用到beginShutdownSequence(context)

    beginShutdownSequence

    函数beginShutdownSequence实现在文件frameworks/base/services/java/com/android/server/power/ShutdownThread.java中:

        private static void beginShutdownSequence(Context context) {
            synchronized (sIsStartedGuard) {
                if (sIsStarted) {
                    Log.d(TAG, "Shutdown sequence already running, returning.");
                    return;
                }
                sIsStarted = true;
            }
            // throw up an indeterminate system dialog to indicate radio is
            // shutting down.
            ProgressDialog pd = new ProgressDialog(context);
            pd.setTitle(context.getText(com.android.internal.R.string.power_off));
            pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
            pd.setIndeterminate(true);
            pd.setCancelable(false);
            pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
            pd.show();
            sInstance.mContext = context;
            sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
            // make sure we never fall asleep again
            sInstance.mCpuWakeLock = null;
            try {
                sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                        PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
                sInstance.mCpuWakeLock.setReferenceCounted(false);
                sInstance.mCpuWakeLock.acquire();
            } catch (SecurityException e) {
                Log.w(TAG, "No permission to acquire wake lock", e);
                sInstance.mCpuWakeLock = null;
            }
            // also make sure the screen stays on for better user experience
            sInstance.mScreenWakeLock = null;
            if (sInstance.mPowerManager.isScreenOn()) {
                try {
                    sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                            PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
                    sInstance.mScreenWakeLock.setReferenceCounted(false);
                    sInstance.mScreenWakeLock.acquire();
                } catch (SecurityException e) {
                    Log.w(TAG, "No permission to acquire wake lock", e);
                    sInstance.mScreenWakeLock = null;
                }
            }
            // start the thread that initiates shutdown
            sInstance.mHandler = new Handler() {
            };
            sInstance.start();
        }
    

    这部分主要是重启前关闭屏幕的操作,最下方的sInstance.start()其实启动的是当前文件中的run函数,具体实现如下:

        public void run() {
            BroadcastReceiver br = new BroadcastReceiver() {
                @Override public void onReceive(Context context, Intent intent) {
                    // We don't allow apps to cancel this, so ignore the result.
                    actionDone();
                }   
            };  
    
            /*  
             * Write a system property in case the system_server reboots before we
             * get to the actual hardware restart. If that happens, we'll retry at
             * the beginning of the SystemServer startup.
             */
            {   
                String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
                SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
            }   
    
            /*  
             * If we are rebooting into safe mode, write a system property
             * indicating so.
             */
            if (mRebootSafeMode) {
                SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
            }   
    
            Log.i(TAG, "Sending shutdown broadcast...");
                
            // First send the high-level shut down broadcast.
            mActionDone = false;
            Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
            mContext.sendOrderedBroadcastAsUser(intent,
                    UserHandle.ALL, null, br, mHandler, 0, null, null);
                
            final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
            synchronized (mActionDoneSync) {
                while (!mActionDone) {
                    long delay = endTime - SystemClock.elapsedRealtime();
                    if (delay <= 0) {
                        Log.w(TAG, "Shutdown broadcast timed out");
                        break;
                    }   
                    try {
                        mActionDoneSync.wait(delay);
                    } catch (InterruptedException e) {
                    }   
                }   
            }
            
            Log.i(TAG, "Shutting down activity manager...");
            
            final IActivityManager am =
                ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
            if (am != null) {
                try {
                    am.shutdown(MAX_BROADCAST_TIME);
                } catch (RemoteException e) {
                }
            }
    
            // Shutdown radios.
            shutdownRadios(MAX_RADIO_WAIT_TIME);
    
            // Shutdown MountService to ensure media is in a safe state
            IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
                public void onShutDownComplete(int statusCode) throws RemoteException {
                    Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
                    actionDone();
                }
            };
    
            Log.i(TAG, "Shutting down MountService");
    
            // Set initial variables and time out time.
            mActionDone = false;
            final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
            synchronized (mActionDoneSync) {
                try {
                    final IMountService mount = IMountService.Stub.asInterface(
                            ServiceManager.checkService("mount"));
                    if (mount != null) {
                        mount.shutdown(observer);
                    } else {
                        Log.w(TAG, "MountService unavailable for shutdown");
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Exception during MountService shutdown", e);
                }
                while (!mActionDone) {
                    long delay = endShutTime - SystemClock.elapsedRealtime();
                    if (delay <= 0) {
                        Log.w(TAG, "Shutdown wait timed out");
                        break;
                    }
                    try {
                        mActionDoneSync.wait(delay);
                    } catch (InterruptedException e) {
                    }
                }
            }
    
            rebootOrShutdown(mReboot, mRebootReason);
        }
    

    这里主要执行了如下这些操作:

    1. 写入系统属性sys.shutdown.requested,对于恢复系统设置,则写入值为recovery
    2. 发送关机广播,默认等待超时时间为10秒;
    3. 依次关闭AMS、Radio、MountService等;
    4. 调用rebootOrShutdown进入重启或关机流程;

    rebootOrShutdown

    函数rebootOrShutdown也实现在文件frameworks/base/services/java/com/android/server/power/ShutdownThread.java中:

        public static void rebootOrShutdown(boolean reboot, String reason) {
            if (reboot) {
                Log.i(TAG, "Rebooting, reason: " + reason);
                PowerManagerService.lowLevelReboot(reason);
                Log.e(TAG, "Reboot failed, will attempt shutdown instead");
            } else if (SHUTDOWN_VIBRATE_MS > 0) {
                // vibrate before shutting down
                Vibrator vibrator = new SystemVibrator();
                try {
                    vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
                } catch (Exception e) {
                    // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                    Log.w(TAG, "Failed to vibrate during shutdown.", e);
                }
    
                // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
                try {
                    Thread.sleep(SHUTDOWN_VIBRATE_MS);
                } catch (InterruptedException unused) {
                }
            }
    
            // Shutdown power
            Log.i(TAG, "Performing low-level shutdown...");
            PowerManagerService.lowLevelShutdown();
        }
    

    其实这里主要就是调用到函数PowerManagerService.lowLevelReboot("recovery")进行重启操作。

    lowLevelReboot

    函数lowLevelReboot实现在文件frameworks/base/services/java/com/android/server/power/PowerManagerService.java中:

        public static void lowLevelReboot(String reason) {
            if (reason == null) {
                reason = "";
            }    
            SystemProperties.set("sys.powerctl", "reboot," + reason);
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) { 
                Thread.currentThread().interrupt();
            }    
        }
    

    很明显,这里是设置了属性值sys.powerctl的值为reboot recovery。其效用类似于命令adb shell setprop sys.powerctl reboot,recovery

    总结

    综上所述,恢复出厂设置,本质上可以总括为如下两条命令:

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

    相关文章

      网友评论

          本文标题:Android4.4.2恢复出厂设置(二)

          本文链接:https://www.haomeiwen.com/subject/aeofvxtx.html