android7.0 开机后立刻恢复出厂设置,偶尔会失败
来源:互联网 发布:js获取另一个页面的id 编辑:程序博客网 时间:2024/05/16 19:02
开机后立刻恢复出厂设置,偶尔会进入recovery模式,不会主动进行擦除用户数据动作。log类似05-12 10:08:17.928 D/RecoverySystemService( 815): setupBcb: [--wipe_data05-12 10:08:17.928 D/RecoverySystemService( 815): --reason=MasterClearConfirm05-12 10:08:17.928 D/RecoverySystemService( 815): --locale=en_US05-12 10:08:17.928 D/RecoverySystemService( 815): ]05-12 10:08:18.954 I/RecoverySystemService( 815): uncrypt setup bcb successfully finished.05-12 10:08:18.957 I/RecoverySystem( 815): RecoverySystem setupBcb true05-12 10:08:29.015 D/RecoverySystemService( 815): clearBcb05-12 10:08:30.031 I/RecoverySystemService( 815): uncrypt clear bcb successfully finished.此问题是googel源码有问题,google已经发布patchhttps://android.googlesource.com/platform/frameworks/base/+/794c8b0b3fe16051843c22232d58d6b184dde49b
Handle the race condition when calling uncrypt services.We call uncrypt services to setup / clear bootloader control block (BCB)for scheduling tasks under recovery (applying OTAs, performing FDR).However, we cannot start multiple requests simultaneously. Because theyall use the same socket (/dev/socket/uncrypt) for communication and initdeletes the socket on service exits.This CL fixes the issue by a) adding synchronized blocks for the servicerequests; b) checking the availability of the socket before initiating anew one.Note that adding synchronized blocks to RecoverySystem doesn't help,because the calls could be made from different processes (e.g. priv-app,system_server).Bug: 31526152Test: FDR works while a priv-app keeps calling clear BCB.Change-Id: I95308989e849a9c98a9503ac509f2bc51ed3de19
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidlindex 12830a4..c5ceecd 100644--- a/core/java/android/os/IRecoverySystem.aidl+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -25,4 +25,5 @@ boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener); boolean setupBcb(in String command); boolean clearBcb();+ void rebootRecoveryWithCommand(in String command); }
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.javaindex 028e30a..df7df64 100644--- a/core/java/android/os/RecoverySystem.java+++ b/core/java/android/os/RecoverySystem.java
@@ -663,28 +663,22 @@ * @throws IOException if something goes wrong. */ private static void bootCommand(Context context, String... args) throws IOException {- synchronized (sRequestLock) {- LOG_FILE.delete();+ LOG_FILE.delete(); - StringBuilder command = new StringBuilder();- for (String arg : args) {- if (!TextUtils.isEmpty(arg)) {- command.append(arg);- command.append("\n");- }+ 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).- RecoverySystem rs = (RecoverySystem) context.getSystemService(- Context.RECOVERY_SERVICE);- rs.setupBcb(command.toString());-- // Having set up the BCB, go ahead and reboot.- PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);- pm.reboot(PowerManager.REBOOT_RECOVERY);-- throw new IOException("Reboot failed (no permissions?)"); }++ // 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?)"); } // Read last_install; then report time (in seconds) and I/O (in MiB) for@@ -879,6 +873,17 @@ } /**+ * Talks to RecoverySystemService via Binder to set up the BCB command and+ * reboot into recovery accordingly.+ */+ private void rebootRecoveryWithCommand(String command) {+ try {+ mService.rebootRecoveryWithCommand(command);+ } catch (RemoteException ignored) {+ }+ }++ /** * Internally, recovery treats each line of the command file as a separate * argv, so we only need to protect against newlines and nulls. */
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.javaindex 276687f..3c8c699 100644--- a/services/core/java/com/android/server/RecoverySystemService.java+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -21,6 +21,7 @@ import android.net.LocalSocketAddress; import android.os.IRecoverySystem; import android.os.IRecoverySystemProgressListener;+import android.os.PowerManager; import android.os.RecoverySystem; import android.os.RemoteException; import android.os.SystemProperties;@@ -50,8 +51,15 @@ // The socket at /dev/socket/uncrypt to communicate with uncrypt. private static final String UNCRYPT_SOCKET = "uncrypt"; + // The init services that communicate with /system/bin/uncrypt.+ private static final String INIT_SERVICE_UNCRYPT = "init.svc.uncrypt";+ private static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb";+ private static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb";+ private static final int SOCKET_CONNECTION_MAX_RETRY = 30; + private static final Object sRequestLock = new Object();+ private Context mContext; public RecoverySystemService(Context context) {@@ -69,95 +77,155 @@ public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) { if (DEBUG) Slog.d(TAG, "uncrypt: " + filename); - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);+ synchronized (sRequestLock) {+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); - // Write the filename into UNCRYPT_PACKAGE_FILE to be read by- // uncrypt.- RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();-- try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) {- uncryptFile.write(filename + "\n");- } catch (IOException e) {- Slog.e(TAG, "IOException when writing \"" + RecoverySystem.UNCRYPT_PACKAGE_FILE +- "\": ", e);- return false;- }-- // Trigger uncrypt via init.- SystemProperties.set("ctl.start", "uncrypt");-- // Connect to the uncrypt service socket.- LocalSocket socket = connectService();- if (socket == null) {- Slog.e(TAG, "Failed to connect to uncrypt socket");- return false;- }-- // Read the status from the socket.- DataInputStream dis = null;- DataOutputStream dos = null;- try {- dis = new DataInputStream(socket.getInputStream());- dos = new DataOutputStream(socket.getOutputStream());- int lastStatus = Integer.MIN_VALUE;- while (true) {- int status = dis.readInt();- // Avoid flooding the log with the same message.- if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {- continue;- }- lastStatus = status;-- if (status >= 0 && status <= 100) {- // Update status- Slog.i(TAG, "uncrypt read status: " + status);- if (listener != null) {- try {- listener.onProgress(status);- } catch (RemoteException ignored) {- Slog.w(TAG, "RemoteException when posting progress");- }- }- if (status == 100) {- Slog.i(TAG, "uncrypt successfully finished.");- // Ack receipt of the final status code. uncrypt- // waits for the ack so the socket won't be- // destroyed before we receive the code.- dos.writeInt(0);- break;- }- } else {- // Error in /system/bin/uncrypt.- Slog.e(TAG, "uncrypt failed with status: " + status);- // Ack receipt of the final status code. uncrypt waits- // for the ack so the socket won't be destroyed before- // we receive the code.- dos.writeInt(0);- return false;- }+ final boolean available = checkAndWaitForUncryptService();+ if (!available) {+ Slog.e(TAG, "uncrypt service is unavailable.");+ return false; }- } catch (IOException e) {- Slog.e(TAG, "IOException when reading status: ", e);- return false;- } finally {- IoUtils.closeQuietly(dis);- IoUtils.closeQuietly(dos);- IoUtils.closeQuietly(socket);- } - return true;+ // Write the filename into UNCRYPT_PACKAGE_FILE to be read by+ // uncrypt.+ RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();++ try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) {+ uncryptFile.write(filename + "\n");+ } catch (IOException e) {+ Slog.e(TAG, "IOException when writing \"" ++ RecoverySystem.UNCRYPT_PACKAGE_FILE + "\":", e);+ return false;+ }++ // Trigger uncrypt via init.+ SystemProperties.set("ctl.start", "uncrypt");++ // Connect to the uncrypt service socket.+ LocalSocket socket = connectService();+ if (socket == null) {+ Slog.e(TAG, "Failed to connect to uncrypt socket");+ return false;+ }++ // Read the status from the socket.+ DataInputStream dis = null;+ DataOutputStream dos = null;+ try {+ dis = new DataInputStream(socket.getInputStream());+ dos = new DataOutputStream(socket.getOutputStream());+ int lastStatus = Integer.MIN_VALUE;+ while (true) {+ int status = dis.readInt();+ // Avoid flooding the log with the same message.+ if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {+ continue;+ }+ lastStatus = status;++ if (status >= 0 && status <= 100) {+ // Update status+ Slog.i(TAG, "uncrypt read status: " + status);+ if (listener != null) {+ try {+ listener.onProgress(status);+ } catch (RemoteException ignored) {+ Slog.w(TAG, "RemoteException when posting progress");+ }+ }+ if (status == 100) {+ Slog.i(TAG, "uncrypt successfully finished.");+ // Ack receipt of the final status code. uncrypt+ // waits for the ack so the socket won't be+ // destroyed before we receive the code.+ dos.writeInt(0);+ break;+ }+ } else {+ // Error in /system/bin/uncrypt.+ Slog.e(TAG, "uncrypt failed with status: " + status);+ // Ack receipt of the final status code. uncrypt waits+ // for the ack so the socket won't be destroyed before+ // we receive the code.+ dos.writeInt(0);+ return false;+ }+ }+ } catch (IOException e) {+ Slog.e(TAG, "IOException when reading status: ", e);+ return false;+ } finally {+ IoUtils.closeQuietly(dis);+ IoUtils.closeQuietly(dos);+ IoUtils.closeQuietly(socket);+ }++ return true;+ } } @Override // Binder call public boolean clearBcb() { if (DEBUG) Slog.d(TAG, "clearBcb");- return setupOrClearBcb(false, null);+ synchronized (sRequestLock) {+ return setupOrClearBcb(false, null);+ } } @Override // Binder call public boolean setupBcb(String command) { if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]");- return setupOrClearBcb(true, command);+ synchronized (sRequestLock) {+ return setupOrClearBcb(true, command);+ }+ }++ @Override // Binder call+ public void rebootRecoveryWithCommand(String command) {+ if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");+ synchronized (sRequestLock) {+ if (!setupOrClearBcb(true, command)) {+ return;+ }++ // Having set up the BCB, go ahead and reboot.+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);+ pm.reboot(PowerManager.REBOOT_RECOVERY);+ }+ }++ /**+ * Check if any of the init services is still running. If so, we cannot+ * start a new uncrypt/setup-bcb/clear-bcb service right away; otherwise+ * it may break the socket communication since init creates / deletes+ * the socket (/dev/socket/uncrypt) on service start / exit.+ */+ private boolean checkAndWaitForUncryptService() {+ for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {+ final String uncryptService = SystemProperties.get(INIT_SERVICE_UNCRYPT);+ final String setupBcbService = SystemProperties.get(INIT_SERVICE_SETUP_BCB);+ final String clearBcbService = SystemProperties.get(INIT_SERVICE_CLEAR_BCB);+ final boolean busy = "running".equals(uncryptService) ||+ "running".equals(setupBcbService) || "running".equals(clearBcbService);+ if (DEBUG) {+ Slog.i(TAG, "retry: " + retry + " busy: " + busy ++ " uncrypt: [" + uncryptService + "]" ++ " setupBcb: [" + setupBcbService + "]" ++ " clearBcb: [" + clearBcbService + "]");+ }++ if (!busy) {+ return true;+ }++ try {+ Thread.sleep(1000);+ } catch (InterruptedException e) {+ Slog.w(TAG, "Interrupted:", e);+ }+ }++ return false; } private LocalSocket connectService() {@@ -176,7 +244,7 @@ try { Thread.sleep(1000); } catch (InterruptedException e) {- Slog.w(TAG, "Interrupted: ", e);+ Slog.w(TAG, "Interrupted:", e); } } }@@ -190,6 +258,12 @@ private boolean setupOrClearBcb(boolean isSetup, String command) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); + final boolean available = checkAndWaitForUncryptService();+ if (!available) {+ Slog.e(TAG, "uncrypt service is unavailable.");+ return false;+ }+ if (isSetup) { SystemProperties.set("ctl.start", "setup-bcb"); } else {@@ -232,7 +306,7 @@ return false; } } catch (IOException e) {- Slog.e(TAG, "IOException when communicating with uncrypt: ", e);+ Slog.e(TAG, "IOException when communicating with uncrypt:", e); return false; } finally { IoUtils.closeQuietly(dis);
- android7.0 开机后立刻恢复出厂设置,偶尔会失败
- Android7.0恢复出厂设置过程分析
- iPad 升级失败后重新恢复出厂设置
- 恢复出厂设置后, 时间没有恢复
- mt6735第一次开机或者恢复出厂设置后,桌面小部件一直不更新无图标
- Android 8.0接收通知后会手机崩溃,闪屏,恢复出厂设置的解决方法
- 恢复出厂设置后Launcher数据错乱
- [FAQ16697]第一开机或者恢复出厂设置后,桌面小部件很长时间才更新到正常的画面
- S1恢复出厂设置
- 恢复出厂设置流程
- android-恢复出厂设置
- android-恢复出厂设置
- BIOS恢复出厂设置
- 恢复出厂设置流程
- 小米恢复出厂设置
- Android恢复出厂设置
- Android恢复出厂设置
- IPAD2 恢复出厂设置
- 为何台湾在移动互联网时代远远落后于大陆?
- WMQ队列信息发送接收监听配置
- xcode简单教程,实现一个button点击事件
- 面向对象OOA、OOD、OOP
- SSL工作原理
- android7.0 开机后立刻恢复出厂设置,偶尔会失败
- 网页布局四之jQuery-easyUI的datagrid的基本应用
- iOS fmdb及工具MesaSQLite的使用
- Java的缓存与缓冲区
- sudo chown -R $(jianan) /usr/local
- 数据结构练习题5
- 判断定位服务是否开启(手机上位置信息按钮开关)
- mybatis的输入输出
- 从输入 URL 到页面展示,到底发生了什么