android recovery模式流程


 1388人閱讀 評論(0) 收藏 編輯 刪除
 分類:

分類: android 3453人閱讀 評論(8) 收藏 舉報
 前言:
 前幾天做了通過T卡安裝gms應該,也做了在recovery中強制刪除的動作,不過這些都是在eng-release版本軟件中測試的。現在上面
要求以後發佈user-release版本的軟件,所以這個功能也應該在user-release中進行測試。之前的不能再recovery中刪除是因爲,沒有將
刪除的動作放到recovery的init.rc文件中去,後來加上即ok。
 不過奇怪的是,出的user-release版本,始終進不去recovery模式,剛開始就去研究看了下recovery的流程,不過研究了之後,還是
找不出爲什麼上層明明是下的恢復出廠設置的命令,怎麼到下層就解析成了關機的動作了呢?
 實在沒有辦法,開始懷疑mtk的user-release版本編譯有問題,所以還發了個eservice給mtk,對我們的詳細進行了詳細的描述,不過他們也是遲遲不回覆,沒辦法,還是自己來回版本吧。
 首先取了21號的最新軟件在informax-ui1上編譯成eng-release版本,測試發現recovery也進不去,這個時候我嚇壞了(因爲前見天爲了裝mail系統將mtk-ui1搞壞了,以前一直都是使用mtk-ui1來編譯版本的),所以我懷疑是編譯環境的影響。所以我就換了臺服務器到mtk-ui2上下載了最新的代碼編譯之後也進不去recovery模式。那個時候我還沒有在意這個結果,直到下午再往前回版本。
 下午聽說最近兩天的20和21好發佈出來的版本都是user-release,所以進不去recovery,測試人員問過我,我誤解地回答了他。不過我燒回了18號發佈的eng-release版本,recovery是ok的。所以我就下載了18號的源碼,編譯成user-release版本,燒錄進去之後居然可以進入recovery模式。這個時候我纔想到上午的驗證和測試人員的問題,意識到可能是這就幾天提交的代碼有問題。
 最後卻爲18號和20號之間的30次提交總有問題,最後在瀏覽了提交記錄之後,纔將目光鎖定在文件ShoudownThread.java身上,因爲只有這個文件和關機重啓有關,而起其他文件的修改根本和這個問題打不上邊。經過迴歸 版本測試,最終確定確實是這個文件影響了recovery模式的進入。
 不過據應用人員說,那個提交是修改和關機鈴聲的一個bug,也沒有多大可能影響到recovery。不過萬事皆有可能,最後他們確定是因爲對handler用法不妥導致,具體什麼原因,我還真不懂java的部分,也沒有空去研究,下面將要描述的是recovery在源碼中的流程,關機重啓的流程和這個一樣,都是使用的reboot(),只是參數不同罷了。


1.  上層應用的設置->隱私權->恢復出廠設置對應的Java代碼在如下路徑文件:
 packages/apps/Settings/src/com/Android/settings/MasterClear.java
 MasterClear:mFinalClickListener()函數會發送一個廣播出去:
 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));

2.  這個廣播的接收者在收到廣播之後會開啓一個java服務線程:MasterClearReceiver:RebootThread
 frameworks/base/services/java/com/android/server/MasterClearReceiver.java  -- TAG = "MasterClear"
 public void onReceive(Context context, Intent intent) {
        RebootThread mThread = new RebootThread(context, intent);
        mThread.start();
    }
    在線程的run函數中會調用函數:RecoverySystem.rebootWipeUserData(mContext);這個方法是RecoverySystem類的靜態方法。

3.  RecoverySystem類定義於文件:frameworks/base/core/java/android/os/RecoverySystem.java   --  TAG = "RecoverySystem"
 public class RecoverySystem {
  /** Used to communicate with recovery.  See bootable/recovery/recovery.c. */
      private static File RECOVERY_DIR = new File("/cache/recovery");
     private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
     private static File LOG_FILE = new File(RECOVERY_DIR, "log");
  
  public static void rebootWipeUserData(Context context)
         throws IOException {
         bootCommand(context, "--wipe_data");
     }
     
     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);  // 往文件/cache/recovery/command中寫入recovery ELF的執行參數。
             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");  // 調用PowerManager類中的reboot方法
 
         throw new IOException("Reboot failed (no permissions?)");
     }
 }
 
4.  PowerManager類定義於文件:frameworks/base/core/java/android/os/PowerManager.java  --  TAG = "PowerManager"
 public class PowerManager
 {
  ...
  public void reboot(String reason)
     {
         try {
             mService.reboot(reason);
         } catch (RemoteException e) {
         }
     }
  
   public PowerManager(IPowerManager service, Handler handler)
     {
         mService = service;
         mHandler = handler;
     }
  
  IPowerManager mService;
     Handler mHandler;
 }

5.  mService指向的是PowerManagerService類,這個類定義於文件:
 frameworks/base/services/java/com/android/server/PowerManagerService.java  --  TAG = "PowerManagerService"
 /**
     * Reboot the device immediately, passing 'reason' (may be null)
     * to the underlying __reboot system call.  Should not return.
     */
    public void reboot(String reason)
    {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);

        if (mHandler == null || !ActivityManagerNative.isSystemReady()) {
            throw new IllegalStateException("Too early to call reboot()");
        }

        final String finalReason = reason;
        Runnable runnable = new Runnable() {
            public void run() {
                synchronized (this) {
                    ShutdownThread.reboot(mContext, finalReason, false);
                } // 調用ShutdownThread服務中的reboot方法
                
            }
        };
        // ShutdownThread must run on a looper capable of displaying the UI.
        mHandler.post(runnable);

        // PowerManager.reboot() is documented not to return so just wait for the inevitable.
        synchronized (runnable) {
            while (true) {
                try {
                    runnable.wait();
                } catch (InterruptedException e) {
                }
            }
        }
    }
 
6.  ShutdownThread類在下列文件中實現:
 frameworks/base/core/java/com/android/internal/app/ShutdownThread.java   -- TAG = "ShutdownThread"
 public final class ShutdownThread extends Thread {
  ...
  public static void reboot(final Context context, String reason, boolean confirm) {
         mReboot = true;
         mRebootReason = reason;
         shutdown(context, confirm);
     }
     
     ...
     public void run() {
      ...
      if (mReboot) {
             Log.i(TAG, "Rebooting, reason: " + mRebootReason);
             try {
                 Power.reboot(mRebootReason);
             } catch (Exception e) {
                 Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
             }
         } else if (SHUTDOWN_VIBRATE_MS > 0) {
             ...
         }
         ...
     }
 }
 流程:reboot() --> shutdown() --> beginShutdownSequence() --> sInstance.start() --> run() --> Power.reboot(mRebootReason).
 最後調用Power類的reboot方法。

7.  Power類定義於文件:
 frameworks/base/core/java/android/os/Power.java    --- 
 public class Power
 {
  ...
  public static void reboot(String reason) throws IOException
     {
         rebootNative(reason);
     }

     private static native void rebootNative(String reason) throws IOException ;
 }
 調用本地JNI接口rebootNative().

8. Power類對應的JNI接口函數定義於文件:
 frameworks/base/core/jni/android_os_Power.cpp
 static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason)
 {
     sync();
 #ifdef HAVE_ANDROID_OS
     if (reason == NULL) {
         reboot(RB_AUTOBOOT);
     } else {
         const char *chars = env->GetStringUTFChars(reason, NULL);
         __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                  LINUX_REBOOT_CMD_RESTART2, (char*) chars);
         env->ReleaseStringUTFChars(reason, chars);  // In case it fails.
     }
     jniThrowIOException(env, errno);
 #endif
 }
 上面的各種宏定義於文件:bionic/libc/kernel/common/Linux/reboot.h
 #define LINUX_REBOOT_MAGIC1 0xfee1dead
 #define LINUX_REBOOT_MAGIC2 672274793
 #define LINUX_REBOOT_MAGIC2A 85072278
 #define LINUX_REBOOT_MAGIC2B 369367448
 #define LINUX_REBOOT_MAGIC2C 537993216
 
 /*
  * Commands accepted by the _reboot() system call.
  *
  * RESTART     Restart system using default command and mode.
  * HALT        Stop OS and give system control to ROM monitor, if any.
  * CAD_ON      Ctrl-Alt-Del sequence causes RESTART command.
  * CAD_OFF     Ctrl-Alt-Del sequence sends SIGINT to init task.
  * POWER_OFF   Stop OS and remove all power from system, if possible.
  * RESTART2    Restart system using given command string.
  * SW_SUSPEND  Suspend system using software suspend if compiled in.
  * KEXEC       Restart system using a previously loaded Linux kernel
  */
 #define LINUX_REBOOT_CMD_RESTART 0x01234567
 #define LINUX_REBOOT_CMD_HALT 0xCDEF0123
 #define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF
 #define LINUX_REBOOT_CMD_CAD_OFF 0x00000000
 #define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC
 #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
 #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2
 #define LINUX_REBOOT_CMD_KEXEC 0x45584543
 
 bionic/libc/include/sys/reboot.h
 #define RB_AUTOBOOT     LINUX_REBOOT_CMD_RESTART
 #define RB_HALT_SYSTEM  LINUX_REBOOT_CMD_HALT
 #define RB_ENABLE_CAD   LINUX_REBOOT_CMD_CAD_ON
 #define RB_DISABLE_CAD  LINUX_REBOOT_CMD_CAD_OFF
 #define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF

9.  libc中__reboot的實現
 bionic/libc/arch-arm/syscalls/__reboot.S
 #include <sys/linux-syscalls.h>

    .text
    .type __reboot, #function
    .globl __reboot
    .align 4
    .fnstart

__reboot:
    .save   {r4, r7}
    stmfd   sp!, {r4, r7}
    ldr     r7, =__NR_reboot // 系統調用號 88, binoic/libc/include/sys/linux-syscalls.h
    swi     #0     
    ldmfd   sp!, {r4, r7}
    movs    r0, r0
    bxpl    lr
    b       __set_syscall_errno
    .fnend

10. reboot系統調用實現
 kernel/kernel/sys.c
 SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)
 {
  char buffer[256];
  int ret = 0;
 
  /* We only trust the superuser with rebooting the system. */
  if (!capable(CAP_SYS_BOOT))
   return -EPERM;
 
  /* For safety, we require "magic" arguments. */
  if (magic1 != LINUX_REBOOT_MAGIC1 ||
      (magic2 != LINUX_REBOOT_MAGIC2 &&
                  magic2 != LINUX_REBOOT_MAGIC2A &&
    magic2 != LINUX_REBOOT_MAGIC2B &&
                  magic2 != LINUX_REBOOT_MAGIC2C))
   return -EINVAL;
 
  if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
   cmd = LINUX_REBOOT_CMD_HALT;
 
  lock_kernel();
  switch (cmd) {
   ...
   case LINUX_REBOOT_CMD_POWER_OFF:
    kernel_power_off();
    unlock_kernel();
    do_exit(0);
    break;
  
   case LINUX_REBOOT_CMD_RESTART2:
    if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
     unlock_kernel();
     return -EFAULT;
    }
    buffer[sizeof(buffer) - 1] = '\0';
  
    kernel_restart(buffer);
    break;
  
    ...
  
   default:
    ret = -EINVAL;
    break;
  }
  unlock_kernel();
  return ret;
 }

 void kernel_restart(char *cmd)
 {
  kernel_restart_prepare(cmd);
  if (!cmd)
   printk(KERN_EMERG "Restarting system.\n");
  else
   printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
  machine_restart(cmd);
 }
 void kernel_restart_prepare(char *cmd)
 {
  blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); // 調用通知鏈reboot_notifier_list上的函數
  system_state = SYSTEM_RESTART;
  device_shutdown(); // shutdown設備
  sysdev_shutdown(); // 系統設備shutdoen
 }

 @kernel/arch/arm/kernel/process.c
 void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart;
 void machine_restart(char *cmd)
 {
  arm_pm_restart(reboot_mode, cmd);
 }
 void arm_machine_restart(char mode, const char *cmd)
 {
  /*
   * Clean and disable cache, and turn off interrupts
   */
  cpu_proc_fin();
 
  /*
   * Tell the mm system that we are going to reboot -
   * we may need it to insert some 1:1 mappings so that
   * soft boot works.
   */
  setup_mm_for_reboot(mode);
 
  /*
   * Now call the architecture specific reboot code.
   */
  arch_reset(mode, cmd);   // reset硬件系統,寫reboot標記,供bootloader中判斷
 
  /*
   * Whoops - the architecture was unable to reboot.
   * Tell the user!
   */
  mdelay(1000);
  printk("Reboot failed -- System halted\n");
  while (1);
 }

11. arch_reset()
 文件:kernel/arch/arm/mach-mt6516/system.c
 void arch_reset(char mode, const char *cmd)
 {
  printk("arch_reset: cmd = %s\n", cmd ? : "NULL");
 
  if (cmd && !strcmp(cmd, "charger")) {
   /* do nothing */
  } else if (cmd && !strcmp(cmd, "recovery")) {
   rtc_mark_recovery();  // 寫recovery的標記到寄存器中去。
  } else {
   rtc_mark_swreset();
  }
 
  DRV_WriteReg32(RGU_USRST1,0xbb1f); 
  
  printk("MT6516 SW Reset\n");
  DRV_WriteReg32(WDT_MODE, 0x2221);
  DRV_WriteReg32(WDT_RESTART, 0x1971);
  DRV_WriteReg32(WDT_SWRST, 0x1209);
 
  /* enter loop waiting for restart */
  while (1);
 }
 @ kernel/driver/ret/ret-mt6516.c
 /* used in arch_reset() */
 void rtc_mark_recovery(void)
 {
  u16 pdn1;
 
  spin_lock_irq(&rtc_lock);
  pdn1 = rtc_read(RTC_PDN1) & ~0x0030;
  pdn1 |= 0x0010;
  rtc_writeif_unlock();
  rtc_write(RTC_PDN1, pdn1);
  rtc_writeif_lock();
  spin_unlock_irq(&rtc_lock);
 }
 /* used in arch_reset() */
 void rtc_mark_swreset(void)
 {
  u16 pdn1;
 
  spin_lock_irq(&rtc_lock);
  pdn1 = rtc_read(RTC_PDN1) & ~0x0030;
  pdn1 |= 0x0020;
  rtc_writeif_unlock();
  rtc_write(RTC_PDN1, pdn1);
  rtc_writeif_lock();
  spin_unlock_irq(&rtc_lock);
 }
 可以看出,recovery和reset都是往RTC_PDN1的bit5:bit4上分別寫01和10來標識。
 
 
12. 正常的log如下:
#logcat ShutdownThread:D *:S &
# --------- beginning of /dev/log/system
--------- beginning of /dev/log/main
D/ShutdownThread(  127): !!! Request to shutdown !!!
D/ShutdownThread(  127): Notifying thread to start radio shutdown
D/ShutdownThread(  127): shutdown acquire partial WakeLock 2
I/ShutdownThread(  127): Sending shutdown broadcast...
I/ShutdownThread(  127): Shutting down activity manager...
W/ShutdownThread(  127): Turning off radio...
I/ShutdownThread(  127): Waiting for Bluetooth and Radio...
I/ShutdownThread(  127): Radio and Bluetooth shutdown complete.
I/ShutdownThread(  127): Shutting down MountService
W/ShutdownThread(  127): Result code 0 from MountService.shutdown
[  127.981918] save exit: isCheckpointed 1
[  127.985002] save exit: isCheckpointed 1
I/ShutdownThread(  127): Rebooting, reason: recovery
[  128.081532] [lizhiguo reboot1] LINUX_REBOOT_CMD_RESTART2.
[  128.082357] GPS: mt3326_gps_shutdown: Shutting down
[  128.083011] GPS: mt3326_gps_power: Switching GPS device off
[  128.083741] GPS: mt3326_gps_power: null pointer!!
[  128.084376] GPIO Shut down
[  128.089814] [MATV] shutdown
[  128.090193] [H264_DEC] h264_dec_shutdown
[  128.090710] JPEG Codec shutdown
[  128.091248] ----MT6516 M3D shutdown----
[  128.091839] m2d_shutdown() is called
[  128.092320] ******** MT6516 WDT driver shutdown!! ********
[  128.093040] [MM_QUEUE] mm_queue_shutdown
[  128.094333] [lizhiguo reboot2] kernel_restart.
[  128.094955] Restarting system with command 'recovery'.
[  128.097483] [lizhiguo reboot3] arm_machine_restart.
[  128.099275] arch_reset: cmd = recovery
[  128.100917] MT6516 SW Reset
u516 EVBgetflashID ADBC successful!!!
[MEM] complex R/W mem test pass


13. uboot中會先後檢查三種方式進入recovery是否成立:第一種是kernel直接寫一個寄存器來標記下次啓動將進入recovery模式;第二種是快捷鍵:powerkey+downVOL;第三中就是上層應用發送下來的回覆出廠設置的命令,這個命令在restart之前kernel會往MISC分區中寫command(boot-recovery)。這項工作在文件:bootable/bootloader/uboot/board/mt6516/mt6516_recovery.c完成。
recovery_check_key_trigger()
recovery_check_command_trigger()
BOOL recovery_check_command_trigger(void)
{
 struct misc_message misc_msg;
 struct misc_message *pmisc_msg = &misc_msg;
 const unsigned int size = NAND_WRITE_SIZE * MISC_PAGES;
 unsigned char *pdata;
     int ret;

 pdata = (uchar*)malloc(sizeof(uchar)*size);

 ret = mboot_recovery_load_misc(pdata, size);
 
    if (ret < 0)
    {
     return FALSE; 
    } 
 
#ifdef LOG_VERBOSE
    MSG("\n--- get_bootloader_message ---\n");
    dump_data(pdata, size);
    MSG("\n");
#endif

 memcpy(pmisc_msg, &pdata[NAND_WRITE_SIZE * MISC_COMMAND_PAGE], sizeof(misc_msg)); 
 MSG("Boot command: %.*s\n", sizeof(misc_msg.command), misc_msg.command);
 MSG("Boot status: %.*s\n", sizeof(misc_msg.status), misc_msg.status);
 MSG("Boot message\n\"%.20s\"\n", misc_msg.recovery);

 if(strcmp(misc_msg.command, "boot-recovery")==0)
 { g_boot_mode = RECOVERY_BOOT;
 }

 return TRUE;
}
// recovery模式檢測
BOOL recovery_detection(void)
{
 if ((DRV_Reg16(RTC_PDN1) & 0x0030) == 0x0010) { /* factory data reset */
  g_boot_mode = RECOVERY_BOOT;
  return TRUE;
 } // 讀取寄存器的值

    if(recovery_check_key_trigger())
    {
     return TRUE;
    }
    // 檢測是否有快捷鍵按下
    
    #ifdef CFG_NAND_BOOT 
 recovery_check_command_trigger();
 #endif
 // 檢測是否通過將忘MISC分區寫命令的方式
 // 以上如果都不是,那麼最後一次檢查模式全局量是夠是RECOVERY_BOOT
 if (g_boot_mode == RECOVERY_BOOT)
 { return TRUE;
 }
 else
 { return FALSE;
 }
}


14. g_boot_mode = RECOVERY_BOOT這個成立之後,uboot將會從RECOVERY分區加載recovery.img進SDRAM來運行。
 其實這個recovery.img和boot.img結構類似,zImage一樣,所不同的是ramdisk.img不同而已。
 在運行recovery這個elf的時候會從/cache/recovery/comamnd中讀取參數,這個參數是android的上層應用寫進入的,--wipe-data,
 之後會清除USERDATA和CACHE分區,在將recovery的log文件放在/cache/recovery/下,將原來的command文件刪除,最後
 調用函數reboot(RB_AUTOBOOT)來重新啓動系統。
 bootable/recovery/recovery.c
 
 最後需要注意的一個問題是,recovery這個elf在編譯user-release版本軟件的時候沒有copy到/system/bin下面去,需要修改
 bootable/recovery/Android.mk文件中的如下地方:
 /* BENGIN: lizhiguo 2011-07-27, copy recovery to /system/bin for user builds.*/
 #LOCAL_MODULE_TAGS := eng
 /* END: lizhiguo 2011-07-27 */
 如果放開這行,將只會在eng版本軟件中有copy到/system/bin的動作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章