分类:android 3453人阅读 评论(8) 收藏 举报 android cmd linux command system string 前言:
前几天做了通过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的动作。

更多相关文章

  1. Android(安卓)插件化分析(3)- Activity启动流程
  2. Android(安卓)CursorAdapter
  3. android 手机欢迎界面不显示的问题?
  4. Android(安卓)手机sdcard目录或文件的拷贝、移动、删除(递归)
  5. Android(安卓)Activity 淡入淡出和从底部向上弹出动画效果【转】
  6. android语言三
  7. ReactNative对接Android步骤
  8. 在eclipse里卸载已安装的插件[例如Android(安卓)Development Too
  9. android 读取文件相关

随机推荐

  1. android之实现各个组件点击事件处理
  2. Android中手机声音调节步骤(Android学习随
  3. android白盒测试所需其他安卓开发内容链
  4. android 随笔
  5. 二之番外.Android六种布局详细讲解
  6. 修改Android开机画面
  7. 基于Android(安卓)6.0修改的音乐播放器可
  8. 编译 Android版本的Openal方式
  9. Android通过AudioFocus机制对音频焦点进
  10. Android:Bitmap->Drawble->Byte[]