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恢复出厂设置代码流程分析

工作中排查到了恢复出厂设置的bug, 有一些细节是需要注意的,于是把这块的代码流程看一下:
代码基于:Android9.0

应用层:
就发送MASTER_CLEAR的广播, 这里没有带参数的

private final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
...
Intent intent = new Intent(ACTION_MASTER_CLEAR);
intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
sendBroadcast(intent);

框架层:

接受广播:
frameworks/base/services/core/java/com/android/server/MasterClearReceiver.java

final boolean shutdown = intent.getBooleanExtra("shutdown", false);
final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, 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 !!!");
// 子线程中执行,防止blocking
// The reboot call is blocking, so we need to do it on another thread.
Thread thr = new Thread("Reboot") {
    @Override
    public void run() {
        try {
            // 这里的四个参数就是默认值了,应用层并没有传进来
            // shutdown:false
            // reason:null
            // forceWipe:false
            // mWipeEsims:false
            // 重启擦除user data and cache partitions
            RecoverySystem
                            .rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims);
            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);
            }
        }
};

// mWipeExternalStorage & mWipeEsims都是false,所以这里不走
if (mWipeExternalStorage || mWipeEsims) {
    // thr will be started at the end of this task.
    new WipeDataTask(context, thr).execute();
    } else {
        thr.start();
}

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

public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
        boolean force, boolean wipeEuicc) throws IOException {
    ...
    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);
    // 注意这里发了MASTER_CLEAR_NOTIFICATION这个有序广播,而且是block的,
    // 也就是接受这个广播的所有接受者处理完成之后才会继续往下执行,
    // 所以需要恢复出厂设置进行的一些操作可以接收这个广播进行处理.
    // 比如:除了用户数据&缓存数据,还有别的数据需要擦除、
    // 比如:车载系统需要通知MCU层做一些操作,也是接收这个广播发命令到MCU.
    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();
    ...
    final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
    // shutdownArg:--shutdown_after
    // reasonArg:--reason=
    // localeArg:--locale=zh_CN
    // --wipe_data: 擦除data分区,同时擦除cache分区.
    bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
}
...
private static void bootCommand(Context context, String... args) throws IOException {
    // 这里删除的是cache/recovery/log
    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.
    // 上面的command参数会写进BCB, 重启读BCB的数据从而进入Recovery
    RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
    rs.rebootRecoveryWithCommand(command.toString());
    ...
}

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

@Override // Binder call
public void rebootRecoveryWithCommand(String command) {
    if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
    synchronized (sRequestLock) {
        // 设置Bcb,返回true才继续往下走
        if (!setupOrClearBcb(true, command)) {
            return;
        }
    
        // Having set up the BCB, go ahead and reboot.
        // 已设置BCB, 重启进Recovery模式
        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        pm.reboot(PowerManager.REBOOT_RECOVERY);
    }
}

private boolean setupOrClearBcb(boolean isSetup, String command) {
    ...

    if (isSetup) {
        // isSetup=true,走的这里,启动服务:setup-bcb
        // uncrypt.rc里面定义了这个服务:
        // service setup-bcb /system/bin/uncrypt --setup-bcb
        // class main
        // socket uncrypt stream 600 system system
        // disabled
        // oneshot
        SystemProperties.set("ctl.start", "setup-bcb");
    } else {
        SystemProperties.set("ctl.start", "clear-bcb");
    }

     // Connect to the uncrypt service socket.
     // 连接uncrypt服务
     LocalSocket socket = connectService();
     if (socket == null) {
         Slog.e(TAG, "Failed to connect to uncrypt socket");
         return false;
     }

     DataInputStream dis = null;
     DataOutputStream dos = null;
     try {
         dis = new DataInputStream(socket.getInputStream());
         dos = new DataOutputStream(socket.getOutputStream());

         // Send the BCB commands if it's to setup BCB.
         if (isSetup) {
             // 走的这里
             // 写入BCB commands
             byte[] cmdUtf8 = command.getBytes("UTF-8");
             dos.writeInt(cmdUtf8.length);
             dos.write(cmdUtf8, 0, cmdUtf8.length);
             dos.flush();
         }

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

         // Ack receipt of the status code. uncrypt waits for the ack so
         // the socket won't be destroyed before we receive the code.
         dos.writeInt(0);

         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 {
        IoUtils.closeQuietly(dis);
        IoUtils.closeQuietly(dos);
        IoUtils.closeQuietly(socket);
    }

    return true;
}
...
private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
// The socket at /dev/socket/uncrypt to communicate with uncrypt.
private static final String UNCRYPT_SOCKET = "uncrypt";
...
private LocalSocket connectService() {
    LocalSocket socket = new LocalSocket();
    boolean done = false;
    // The uncrypt socket will be created by init upon receiving the
    // service request. It may not be ready by this point. So we will
    // keep retrying until success or reaching timeout.
    for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
        try {
            socket.connect(new LocalSocketAddress(UNCRYPT_SOCKET,
                    LocalSocketAddress.Namespace.RESERVED));
            done = true;
            break;
        } catch (IOException ignored) {
            try {
                Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Slog.w(TAG, "Interrupted:", e);
                }
            }
    }
    if (!done) {
        Slog.e(TAG, "Timed out connecting to uncrypt socket");
        return null;
    }
    return socket;
}

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

@Override // Binder call
        
public void reboot(boolean confirm, String reason, boolean wait) {
    ...
    // confirm:false
    // reason:recovery
    // wait:true
    try {
        shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
        final String reason, boolean wait) {
    ...
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
                    ShutdownThread.rebootSafeMode(getUiContext(), confirm);
                } else if (haltMode == HALT_MODE_REBOOT) {
                    // 走的这里
                    ShutdownThread.reboot(getUiContext(), reason, confirm);
                } else {
                    ShutdownThread.shutdown(getUiContext(), reason, confirm);
                }
            }
        }
    };

    // ShutdownThread must run on a looper capable of displaying the UI.
    Message msg = Message.obtain(UiThread.getHandler(), runnable);
    msg.setAsynchronous(true);
    UiThread.getHandler().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) {
                }
            }
        }
    }
}

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

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

private static void shutdownInner(final Context context, boolean confirm) {
    ...

    final int longPressBehavior = context.getResources().getInteger(
                    com.android.internal.R.integer.config_longPressOnPowerBehavior);
    // mRebootSafeMode:false
    // longPressBehavior=1
    // <integer name="config_longPressOnPowerBehavior">1</integer>
    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) {
        ...
    } else {
        // 走的这里
        beginShutdownSequence(context);
    }
}

private static void beginShutdownSequence(Context context) {
    ...

    sInstance.mProgressDialog = showShutdownDialog(context);
    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;
        }
    }

    if (SecurityLog.isLoggingEnabled()) {
        SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
    }

    // start the thread that initiates shutdown
    sInstance.mHandler = new Handler() {
    };
    // 启动线程
    sInstance.start();
}

public void run() {
    // 重启前的准备工作
    TimingsTraceLog shutdownTimingLog = newTimingsLog();
    shutdownTimingLog.traceBegin("SystemServerShutdown");
    metricShutdownStart();
    metricStarted(METRIC_SYSTEM_SERVER);

    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") + (mReason != null ? mReason : "");
        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");
    }

    metricStarted(METRIC_SEND_BROADCAST);
    shutdownTimingLog.traceBegin("SendShutdownBroadcast");
    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 | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    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;
            } else if (mRebootHasProgressBar) {
                int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
                        BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
                sInstance.setRebootProgress(status, null);
            }
            try {
                mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
            } catch (InterruptedException e) {
            }
        }
    }
    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
    metricEnded(METRIC_SEND_BROADCAST);

    Log.i(TAG, "Shutting down activity manager...");
    shutdownTimingLog.traceBegin("ShutdownActivityManager");
    metricStarted(METRIC_AM);

    final IActivityManager am =
            IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
    if (am != null) {
        try {
            am.shutdown(MAX_BROADCAST_TIME);
         } catch (RemoteException e) {
        }
     }
    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd();// ShutdownActivityManager
    metricEnded(METRIC_AM);

    Log.i(TAG, "Shutting down package manager...");
    shutdownTimingLog.traceBegin("ShutdownPackageManager");
    metricStarted(METRIC_PM);

    final PackageManagerService pm = (PackageManagerService)
        ServiceManager.getService("package");
    if (pm != null) {
        pm.shutdown();
    }
    if (mRebootHasProgressBar) {
       sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd(); // ShutdownPackageManager
    metricEnded(METRIC_PM);

     // Shutdown radios.
     shutdownTimingLog.traceBegin("ShutdownRadios");
     metricStarted(METRIC_RADIOS);
     shutdownRadios(MAX_RADIO_WAIT_TIME);
     if (mRebootHasProgressBar) {
         sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
     }
     shutdownTimingLog.traceEnd(); // ShutdownRadios
     metricEnded(METRIC_RADIOS);

     if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);

        // If it's to reboot to install an update and uncrypt hasn't been
        // done yet, trigger it now.
        uncrypt();
    }

    shutdownTimingLog.traceEnd(); // SystemServerShutdown
    metricEnded(METRIC_SYSTEM_SERVER);
    saveMetrics(mReboot, mReason);
    // Remaining work will be done by init, including vold shutdown
    // 重启前的准备工作结束, 开始重启
    rebootOrShutdown(mContext, mReboot, mReason);
}
...
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
    // reboot = true
    // reason = recovery
    if (reboot) {
        //走的这里
        Log.i(TAG, "Rebooting, reason: " + reason);
        PowerManagerService.lowLevelReboot(reason);
        Log.e(TAG, "Reboot failed, will attempt shutdown instead");
        reason = null;
    } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
        ...
    }
    // Shutdown power
    Log.i(TAG, "Performing low-level shutdown...");
    PowerManagerService.lowLevelShutdown(reason);
}

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

/**
 * Low-level function to reboot the device. On success, this
 * function doesn't return. If more than 20 seconds passes from
 * the time a reboot is requested, this method returns.
 *
 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
 */
public static void lowLevelReboot(String reason) {
    if (reason == null) {
        reason = "";
    }

    // If the reason is "quiescent", it means that the boot process should proceed
    // without turning on the screen/lights.
    // The "quiescent" property is sticky, meaning that any number
    // of subsequent reboots should honor the property until it is reset.
    if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
        sQuiescent = true;
        reason = "";
    } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
        sQuiescent = true;
        reason = reason.substring(0,
                reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
    }

    // reason = recovery
    if (reason.equals(PowerManager.REBOOT_RECOVERY)
            || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
        // 走的这里
        reason = "recovery";
    }

    if (sQuiescent) {
        // Pass the optional "quiescent" argument to the bootloader to let it know
        // that it should not turn the screen/lights on.
        reason = reason + ",quiescent";
    }

  // 设置这个prop触发init进程重启进入recovery模式
    SystemProperties.set("sys.powerctl", "reboot," + reason);
    try {
        Thread.sleep(20 * 1000L);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
}

sys.powerctl这个prop设置后最终走到**/system/core/init/reboot.cpp**::
bool HandlePowerctlMessage(const std::string& command)
void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,bool runFsck)
void attribute((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget)
到RebootSystem这个方法,重启结束, 进入kernel,调用syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str())进入内核空间进行重启,重启后进入Recovery模式,进行擦除cache分区、擦除data分区等操作。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:34:46  更:2022-07-17 16:36:13 
 
开发: 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年11日历 -2024/11/25 3:23:39-

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