(android 关机/重启)Android关机/重启流程解析
refer:http://blog.csdn.net/airk000/article/details/9177077
refer:http://blog.csdn.net/leerobin83/article/details/7162751
目录(?)[-]
- public void reboot String reason
- --------------------------------上层空间--------------------------------
- frameworks/base/core/java/android/os/PowerManagerjava
- frameworks/base/core/java/android/os/IPowerManageraidl
- frameworks/base/services/java/com/androidserver/PowerManagerServicejava
- frameworks/base/services/java/com/androidserver/pm/ShutdownThreadjava
- frameworks/base/services/java/com/androidserver/PowerManagerServicejava
- frameworks/base/services/jni/com_android_server_PowerManagerServicecpp
- system/core/lib/cutils/android_rebootc
- bionic/libc/unistdre/bootc
- --------------------------------KERNEL域--------------------------------
- __reboot通过syscall来到内核
- kernelsysc
- 最终实现
- 关机怎么用(最终的总结:本质的调用)
- adb 测试重启命令(adb reboot recovery,charger,kpoc,bootloader,etc)
-
资料:
https://developer.android.com/intl/zh-CN/reference/android/os/PowerManager.html
--------------------------------Introduction--------------------------
1. 在PowerManager的API文档中,给出了一个关机/重启接口:
public void reboot (String reason)
2. 对于这个接口的描述很简单,就是几句话。接口的作用就是重启设备,而且,就算重启成功了也没有返回值。
需要包含REBOOT权限,也就是android.permission.REBOOT
唯一参数reason代表需要的特定重启模式,比如recovery,当然也可以为null。
--------------------------------上层空间--------------------------------
1.frameworks/base/core/java/android/os/PowerManager.java
- /**
- * Reboot the device. Will not return if the reboot is
- * successful. Requires the {@link android.Manifest.permission#REBOOT}
- * permission.
- *
- * @param reason code to pass to the kernel (e.g., "recovery") to
- * request special boot modes, or null.
- */
- public void reboot(String reason)
- {
- try {
- mService.reboot(reason);
- } catch (RemoteException e) {
- }
- }
mService为IPowerManager Binder接口服务。
- /**
- * {@hide}
- */
- public PowerManager(IPowerManager service, Handler handler)
- {
- mService = service;
- mHandler = handler;
- }
2.frameworks/base/core/java/android/os/IPowerManager.aidl
- interface IPowerManager
- {
- ...
- void reboot(String reason);
- ...
- }
3.frameworks/base/services/java/com/android/server/PowerManagerService.java
- /**
- * 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(getUiContext(), finalReason, false);
- }
- }
- };
- // 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) {
- }
- }
- }
- }
4.frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
- /**
- * Request a clean shutdown, waiting for subsystems to clean up their
- * state etc. Must be called from a Looper thread in which its UI
- * is shown.
- *
- * @param context Context used to display the shutdown progress dialog.
- * @param reason code to pass to the kernel (e.g. "recovery"), or null.
- * @param confirm true if user confirmation is needed before shutting down.
- */
- public static void reboot(final Context context, String reason, boolean confirm) {
- mReboot = true;
- mRebootSafeMode = false;
- mRebootReason = reason;
- shutdownInner(context, confirm);
- }
重启的实现在run()中,因为ShutdownThread是Thread的扩展,所以run会自动运行,如下。
/**
* Makes sure we handle the shutdown gracefully.
* Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
*/
public void run() {
checkShutdownFlow();
while (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
stMgr.saveStates(mContext);
stMgr.enterShutdown(mContext);
running();
}
if (mShutdownFlow != IPO_SHUTDOWN_FLOW) {
stMgr.enterShutdown(mContext);
running();
}
}
public void running() {
try {
sPreShutdownApi.onPowerOff();
} catch (RemoteException e) {
Log.e(TAG, "onPowerOff exception" + e.getMessage());
}
}else{
Log.w(TAG, "sPreShutdownApi is null");
}
command = SystemProperties.get("sys.ipo.pwrdncap");
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"); } ... rebootOrShutdown(mReboot, mRebootReason); }
如上所叙述,在重启前会将重启原因写入sys.shutdown.requested,如果没有则为空,如果是安全模式还会将persist.sys.safemode置1,之后会进行一些关机前的预处理,关闭ActivityManager以及MountService,最终调用rebootOrShutdown进行关机操作。
======>>>>>>android2中的rebootOrShutdown
- /**
- * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
- * or {@link #shutdown(Context, boolean)} instead.
- *
- * @param reboot true to reboot or false to shutdown
- * @param reason reason for reboot
- */
- public static void rebootOrShutdown(boolean reboot, String reason) {
- if (reboot) {
- Log.i(TAG, "Rebooting, reason: " + reason);
- try {
- PowerManagerService.lowLevelReboot(reason); ///重启Reboot
- } catch (Exception e) {
- Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
- }
- } else if (SHUTDOWN_VIBRATE_MS > 0) { ///关机的时候需要震动,就是这里了
- //SHUTDOWN_VIBRATE_MS,默认的定义是500ms。
- // 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();直接关机Shutdown
- }
- }
如果重启,则调用PowerManagerService的lowLevelReboot函数,参数就是传递下来的reason,稍后分析。
如果不是重启,即mReboot=false,那就是需要关机了,在shutdown函数中就能够知道。如下所示:
- /**
- * Request a clean shutdown, waiting for subsystems to clean up their
- * state etc. Must be called from a Looper thread in which its UI
- * is shown.
- *
- * @param context Context used to display the shutdown progress dialog.
- * @param confirm true if user confirmation is needed before shutting down.
- */
- public static void shutdown(final Context context, boolean confirm) {
- mReboot = false;
- mRebootSafeMode = false;
- shutdownInner(context, confirm);
- }
如上所示,会有reboot 以及 shutdown的操作。
但是在代码逻辑上看,无论如何,最后都会调用一下lowLevelShutdown函数,也就是关机。
逻辑上,这里可能是个问题,但是实际中,如果重启操作能够调用成功的话,整个系统都重启了,后边的代码当然不可能执行到了。
======>>>>>>android4.4中的rebootOrShutdown
{ android 4.4中的rebootOrShutdown---解析
public static void rebootOrShutdown(boolean reboot, String reason) {
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
if ( (reason != null) && reason.equals("recovery") ) {
delayForPlayAnimation();
}
try {
PowerManagerService.lowLevelReboot(reason);
} catch (Exception e) {
Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
}
} 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) {
}
}
delayForPlayAnimation();
// Shutdown power
// power off auto test, don't modify
Log.i(TAG, "Performing low-level shutdown...");
//PowerManagerService.lowLevelShutdown();已经注释掉了
//add your func: HDMI off
//add for MFR
try {
if (ImHDMI == null)
ImHDMI=MediatekClassFactory.createInstance(IHDMINative.class);
} catch (Exception e) {
e.printStackTrace();
}
ImHDMI.hdmiPowerEnable(false);
//unmout data/cache partitions while performing shutdown
SystemProperties.set("ctl.start", "shutdown");直接关机shutdown操作
/* sleep for a long time, prevent start another service */
try {
Thread.currentThread().sleep(Integer.MAX_VALUE);
} catch ( Exception e) {
Log.e(TAG, "Shutdown rebootOrShutdown Thread.currentThread().sleep exception!");
}
}
}
目光转回PowerManagerService 中进行的lowLevelReboot()调用。。。。
4.frameworks/base/services/java/com/android/server/PowerManagerService.java
- /**
- * Low-level function to reboot the device.
- *
- * @param reason code to pass to the kernel (e.g. "recovery"), or null.
- * @throws IOException if reboot fails for some reason (eg, lack of
- * permission)
- */
- public static void lowLevelReboot(String reason) throws IOException {
- nativeReboot(reason);
- }
- /**
- * Low-level function turn the device off immediately, without trying
- * to be clean. Most people should use
- * {@link com.android.server.pm.internal.app.ShutdownThread} for a clean shutdown.
- */
- public static void lowLevelShutdown() {
- nativeShutdown();
- }
很熟悉的字样native,是JNI调用了:
- private static native void nativeShutdown();
- private static native void nativeReboot(String reason) throws IOException;
===>>>>>android 4.4 中的lowLevelShutdown 以及 lowLevelReboot
public static void lowLevelShutdown() {
SystemProperties.set("sys.powerctl", "shutdown");直接关机
}
/**
* Low-level function to reboot the device. On success, this function
* doesn't return. If more than 5 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 = "";
}
SystemProperties.set("sys.powerctl", "reboot," + reason);///重启
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
【注解:】
关机流程中最后是通过修改Android属性进行关机操作(SystemProperties.java通过JNI调用访问系统属性)。
当然我们也可以通过adb命令修改Android系统属性执行关机操作,例如adb shell setpro sys.powerctl shutdown。
这里我们简单介绍下修改Android属性关机的原理或流程。
native_set()
这是SystemProperties.java类中设置系统函数的方法。
5.frameworks/base/services/jni/com_android_server_PowerManagerService.cpp
- static JNINativeMethod gPowerManagerServiceMethods[] = {
- /* name, signature, funcPtr */
- ...
- { "nativeShutdown", "()V",
- (void*) nativeShutdown },
- { "nativeReboot", "(Ljava/lang/String;)V",
- (void*) nativeReboot },
- ...
- };
这两个好哥俩的实现也是在一起的:
- static void nativeShutdown(JNIEnv *env, jobject clazz) {
- android_reboot(ANDROID_RB_POWEROFF, 0, 0); 直接关机
- }
- static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) {
- if (reason == NULL) {
- android_reboot(ANDROID_RB_RESTART, 0, 0); //无reboot reason的重启
- } else {
- const char *chars = env->GetStringUTFChars(reason, NULL);
- android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars); //有reboot reason的重启
- env->ReleaseStringUTFChars(reason, chars); // In case it fails.
- }
- jniThrowIOException(env, errno);
- }
可以看到无论是关机还是重启,都是调用android_reboot来实现的,只是参数不一样而已。
6.system/core/libcutils/android_reboot.c
int android_reboot(int cmd, int flags, char *arg)
{
int ret;
sync();
remount_ro();
switch (cmd) {
case ANDROID_RB_RESTART://普通重启
ret = reboot(RB_AUTOBOOT);
break;
case ANDROID_RB_POWEROFF:/直接关机
ret = reboot(RB_POWER_OFF);
break;
case ANDROID_RB_RESTART2:
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,///有参数的重启直接走这里
LINUX_REBOOT_CMD_RESTART2, arg);
break;
default:
ret = -1;
}
return ret;
}
接下来才是对参数的解析处理:
1)普通重启 ANDROID_RB_RESTART, reason = RB_AUTOBOOT;
2)关机 ANDROID_RB_POWEROFF, 无需reason,直接调用reboot进行关机;
3)带参数的特殊重启 ANDROID_RB_RESTART2, reason 将为默认值 -1
注解1:在有的android版本里,这里又会出现一个#ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON,如果定义了它,则无论上层传下来的参数是什么样的,最终都只是普通重启而已。定义它的方式是在BoardConfig.mk中加入TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON := true,应该有厂商会喜欢这么做的,毕竟除了普通重启,都可能带给用户一定的风险。最后会对reason进行一个检测,那么通过上边的分析,其实只有带参数的特殊重启才会为-1,而不等于-1的情况中有普通重启和关机,而关机已经自行解决了……所以,不等于-1的情况到了这里也只有普通重启了。最终这里就是区分普通重启与特殊重启的地方了。
注解2: 这里再插入一个问题,其他的几个cmd都是什么值呢?答案在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
而,LINUX_REBOOT_XXXX之类的在bionic/libc/kernel/common/linux/reboot.h中:
- #define LINUX_REBOOT_MAGIC1 0xfee1dead
- #define LINUX_REBOOT_MAGIC2 672274793
- /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- #define LINUX_REBOOT_MAGIC2A 85072278
- #define LINUX_REBOOT_MAGIC2B 369367448
- #define LINUX_REBOOT_MAGIC2C 537993216
- #define LINUX_REBOOT_CMD_RESTART 0x01234567
- /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- #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
- /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
- #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2
- #define LINUX_REBOOT_CMD_KEXEC 0x45584543
先来看reboot函数,按照往常的经验,reboot最终一定会调用到__reboot的。
7.bionic/libc/unistd/reboot.c ///A:\v387-debug\my-ap\bionic\libc\bionic\reboot.c
- int reboot (int mode)
- {
- return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );
- }
Bingo!果然是这样,如此说来reboot(reason) -> reboot(RB_AUTOBOOT) -> __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART, NULL ),要是直接这样写多好~~~免得绕这一层了。
--------------------------------KERNEL域--------------------------------
8.__reboot通过syscall来到内核
这里用一些篇幅简要介绍syscall,以后遇到类似的东西更好追踪一些。
第七步中的__reboot在arm架构的实现是这样的(bionic/libc/arch-arm/syscalls/__reboot.S)
/home/tonylau/tony-workspace/v387-debug/my-ap/bionic/libc/arch-arm/syscalls/__reboot.S
- ENTRY(__reboot)
- .save {r4, r7}
- stmfd sp!, {r4, r7}
- ldr r7, =__NR_reboot
- swi #0
- ldmfd sp!, {r4, r7}
- movs r0, r0
- bxpl lr
- b __set_syscall_errno
- END(__reboot)
可以看出来,这里将__reboot的实现映射到了__NR_reboot, 而在bionic/libc/sys/linux-syscalls.h((/home/tonylau/tony-workspace/v387-debug/my-ap/prebuilts/ndk/8/platforms/android-4/arch-arm/usr/include/sys/linux-syscalls.h))能够找到:
- #define __NR_reboot (__NR_SYSCALL_BASE + 88)
其被指定了一个固定的偏移量,在被调用的时候就是通过这个偏移量去内核中寻找对应的入口的,由此可见,内核中一定有着相同的定义,否则将不能成功调用。内核中对syscall偏移量的定义在内核源码中的arch/arm/include/asm/unistd.h,相关信息完全一致。
已经找到了内核中的对应映射,那么下一步就要去找寻真正的实现函数了,在include/asm-generic/unistd.h(/home/tonylau/tony-workspace/v387-debug/my-ap/prebuilts/ndk/9/platforms/android-4/arch-arm/usr/include/asm/unistd.h)中可以找到内核对__NR_reboot的syscall函数映射,即
- /* kernel/sys.c */
- #define __NR_setpriority 140
- __SYSCALL(__NR_setpriority, sys_setpriority)
- #define __NR_getpriority 141
- __SYSCALL(__NR_getpriority, sys_getpriority)
- #define __NR_reboot 142
- __SYSCALL(__NR_reboot, sys_reboot)
同时,能够发现如此温馨的一幕,内核已经指引我们下一步该去哪里寻找sys_reboot,即kernel/sys.c。
9.kernel/sys.c
在进入这个文件前,我们先去include/linux/syscalls.h中查看一下sys_reboot的定义:
- asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd,
- void __user *arg);
与__reboot的调用参数一致。
进入sys.c文件后,并没有找到名为sys_reboot的函数,而通过仔细查找,发现一个很有趣的函数,其定义为SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),对比__reboot的参数,能够符合。究竟是不是这个函数?
同样在include/linux/syscalls.h文件中,能够找到这样几个定义:
- #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
- #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
- #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
- #define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
- #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
- #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
- ...
- #define SYSCALL_DEFINEx(x, sname, ...) \
- __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
- ...
- #define __SYSCALL_DEFINEx(x, name, ...) \
- asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
整合后等价于:
- #define SYSCALL_DEFINE4(name, ...) \
- asmlinkage long sys##_name(__SC_DECL##4(__VA_ARGS__))
这样就不难看出,SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)就是sys_reboot,也就是上层调用的__reboot的最终实现。函数实现如下:
- /*
- * Reboot system call: for obvious reasons only root may call it,
- * and even root needs to set up some magic numbers in the registers
- * so that some mistake won't make this reboot the whole machine.
- * You can also set the meaning of the ctrl-alt-del-key here.
- *
- * reboot doesn't sync: do that yourself before calling this.
- */
- 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; /否则将返回权限错误。对应的权限列表在include/linux/capability.h中,重启操作为22
- /* For safety, we require "magic" arguments. */ //随后对magic number进行了校验
- if (magic1 != LINUX_REBOOT_MAGIC1 || 如果数据传输过程中没有发生错误的话,
- 这里也当然不会有问题,所以只是一个安全性校验,基本不会发生错误。
- (magic2 != LINUX_REBOOT_MAGIC2 &&
- magic2 != LINUX_REBOOT_MAGIC2A &&
- magic2 != LINUX_REBOOT_MAGIC2B &&
- magic2 != LINUX_REBOOT_MAGIC2C))
- return -EINVAL;
- /* Instead of trying to make the power_off code look like
- * halt when pm_power_off is not set do it the easy way.
- */
- if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
- cmd = LINUX_REBOOT_CMD_HALT; ///之后有一个很有趣的检查,如果用户要求关机,而pm_power_off为空的话,就把用户的关机命令转换为挂起:
- mutex_lock(&reboot_mutex);
- switch (cmd) {
- case LINUX_REBOOT_CMD_RESTART:
- kernel_restart(NULL); 重启方式1---无命令
- break;
- case LINUX_REBOOT_CMD_CAD_ON:
- C_A_D = 1;
- break;
- case LINUX_REBOOT_CMD_CAD_OFF:
- C_A_D = 0;
- break;
- case LINUX_REBOOT_CMD_HALT:
- kernel_halt();
- do_exit(0);
- panic("cannot halt");
- case LINUX_REBOOT_CMD_POWER_OFF:
- kernel_power_off(); 直接
- do_exit(0);
- break;
- case LINUX_REBOOT_CMD_RESTART2:
- if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
- ret = -EFAULT;
- break;
- }
- buffer[sizeof(buffer) - 1] = '\0';
- kernel_restart(buffer); 重启方式2---有命令
- break;
- #ifdef CONFIG_KEXEC
- case LINUX_REBOOT_CMD_KEXEC:
- ret = kernel_kexec();
- break;
- #endif
- #ifdef CONFIG_HIBERNATION
- case LINUX_REBOOT_CMD_SW_SUSPEND:
- ret = hibernate();
- break;
- #endif
- default:
- ret = -EINVAL;
- break;
- }
- mutex_unlock(&reboot_mutex);
- return ret;
- }
注解1:pm_power_off挂起的操作,
在arch/arm/kernel/process.c中可以找到它的定义:
- /*
- * Function pointers to optional machine specific functions
- */
- void (*pm_power_off)(void);
- EXPORT_SYMBOL(pm_power_off);
好的,只是一个函数指针,而且做了全局操作,整个kernel都可以调用它。以高通msm7x30为例,在arch/arm/mach-msm/pm2.c中对这个函数指针进行了赋值:
- pm_power_off = msm_pm_power_off;
msm_pm_power_off的具体实现就不再跟踪了,各家的都不一样,跟下去没有太大意义。现在只要知道,我分析的这个kernel是给了这个函数指针赋值的,所以不为空,关机命令将正常执行。
注解2:接下来就是这个函数的正题了,对用户命令进行解析操作,同时这个过程是用reboot_mutex互斥锁来进行保护的,以保证同一时间只可能有一个解析过程,避免冲突。
下边贴出所有关机重启相关的命令定义:
- /*
- * 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
注释中的说明很详细了,比较陌生的就是关于CAD,其实就是用来想用Ctrl+Alt+Del操作的;然后SW_SYSPEND是软件休眠;KEXEC就太高端了,属于内核的一个补丁,用来利用老内核重启,详细资料:http://www.ibm.com/developerworks/cn/linux/l-kexec/?ca=dwcn-newsletter-linux
以上这些只有前六个命令被Android系统所使用,为什么这么说,可以去看bionic/libc/include/sys/reboot.h,上边已经贴出了。LINUX_REBOOT_CMD_HALT虽有定义,但是也没有发现Android系统中哪里有调用,有高手找到的话,希望能够告知一下。
最终的最终,能够用到的就只有三个:
RESTART
POWER_OFF
RESTART2
10.最终实现
重启调用的是kernel_restart,区别是参数是不是空,关机则调用kernel_power_off(),
注解1:先看关机:
/**
* kernel_power_off - power_off the system
*
* Shutdown everything and perform a clean system power_off.
*/
void kernel_power_off(void)
{
kernel_shutdown_prepare(SYSTEM_POWER_OFF);
if (pm_power_off_prepare)
pm_power_off_prepare();
migrate_to_reboot_cpu();
syscore_shutdown();
printk(KERN_EMERG "Power down.\n");
kmsg_dump(KMSG_DUMP_POWEROFF);
machine_power_off();
}
EXPORT_SYMBOL_GPL(kernel_power_off);
做了一系列准备工作,最终调用machine_power_off():
- void machine_power_off(void)
- {
- machine_shutdown();
- if (pm_power_off)
- pm_power_off();
- }
之前找寻的pm_power_off在这里就有用处了,是关机的最后一步操作。关机完成,之后看下重启操作:
- /**
- * kernel_restart - reboot the system
- * @cmd: pointer to buffer containing command to execute for restart
- * or %NULL
- *
- * Shutdown everything and perform a clean reboot.
- * This is not safe to call in interrupt context.
- */
- 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);
- kmsg_dump(KMSG_DUMP_RESTART);
- machine_restart(cmd);
- }
- EXPORT_SYMBOL_GPL(kernel_restart);
注解2:在看重启。同样的套路,也是会进行一些准备工作,之后调用machine_restart(cmd), 如果是普通重启,那么中个cmd就为NULL,如果是特殊重启,那么这个cmd就是一层一层传递下来得那个arg了。
@@kernel/arch/arm/kernel/process.c:
void machine_restart(char *cmd)machine_restart执行每个架构特定的函数
{
machine_shutdown();
printk("Reboot:machine restart...\n");
/* Flush the console to make sure all the relevant messages make it
* out to the console drivers */
arm_machine_flush_console();
arm_pm_restart(reboot_mode, cmd);
/* Give a grace period for failure to restart of 1s */
mdelay(1000);
/* Whoops - the platform was unable to reboot. Tell the user! */
printk("Reboot failed -- System halted\n");
local_irq_disable();
while (1);
}
void arm_machine_restart(char mode, const char *cmd)
{
/* Flush the console to make sure all the relevant messages make it
* out to the console drivers */
arm_machine_flush_console();
/* Disable interrupts first */
local_irq_disable();
local_fiq_disable();
/*
* 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();
/* When l1 is disabled and l2 is enabled, the spinlock cannot get the lock,
* so we need to disable the l2 as well. by Chia-Hao Hsu
*/
outer_flush_all();
outer_disable();
outer_flush_all();
/* Clean and invalidate caches */
flush_cache_all();
/* Turn off caching */
cpu_proc_fin();
/* Push out any further dirty data, and ensure cache is empty */
flush_cache_all();
/*
* Now call the architecture specific reboot code.
*/
arch_reset(mode, cmd);
/*
* Whoops - the architecture was unable to reboot.
* Tell the user!
*/
mdelay(1000);
printk("Reboot failed -- System halted\n");
while (1);
}
@@@@kernel/meditek/kernel/kernel/system.c:
void arch_reset(char mode, const char *cmd)
{
char reboot = 0;
int res=0;
struct wd_api*wd_api = NULL;
res = get_wd_api(&wd_api);
printk("arch_reset: cmd = %s\n", cmd ? : "NULL");
if (cmd && !strcmp(cmd, "charger")) {//
/* do nothing */
} else if (cmd && !strcmp(cmd, "recovery")) {/如果传下来的字符串是recovery=>就在RTC寄存器里设置某个特定值,
当uboot里读取RTC寄存器的时候如果获取了这个特定值,那就可以起recovery这个动作了
rtc_mark_recovery();
} else if (cmd && !strcmp(cmd, "bootloader")){//
rtc_mark_fast();
}
#ifdef MTK_KERNEL_POWER_OFF_CHARGING
else if (cmd && !strcmp(cmd, "kpoc")){ rtc_mark_kpoc(); }///
#endif
else {
reboot = 1;
}
if(res){
printk("arch_reset, get wd api error %d\n",res);
} else {
wd_api->wd_sw_reset(reboot);
}
}
@@@@@kernel/meditek/kernel/drivers/rtc/mtk_rtc_common.c
void rtc_mark_recovery(void)
{
unsigned long flags;
spin_lock_irqsave(&rtc_lock, flags);
hal_rtc_mark_mode("recv");
spin_unlock_irqrestore(&rtc_lock, flags);
}
@@@@kernel/mediatek/platform/mt6582/kernel/drivers/rtc/mtk_rtc_hal.c
void hal_rtc_mark_mode(const char *cmd)
{
u16 pdn1;
if (!strcmp(cmd, "recv")) {
pdn1 = rtc_read(RTC_PDN1) & (~RTC_PDN1_RECOVERY_MASK);
rtc_write(RTC_PDN1, pdn1 | RTC_PDN1_FAC_RESET);
}
else if (!strcmp(cmd, "kpoc")) {
pdn1 = rtc_read(RTC_PDN1) & (~RTC_PDN1_KPOC);
rtc_write(RTC_PDN1, pdn1 | RTC_PDN1_KPOC);
}
else if (!strcmp(cmd, "fast")) {
pdn1 = rtc_read(RTC_PDN1) & (~RTC_PDN1_FAST_BOOT);
rtc_write(RTC_PDN1, pdn1 | RTC_PDN1_FAST_BOOT);
}
rtc_write_trigger();
}
--------------------------------adb重启测试--------------------------------
adb 测试重启命令(adb reboot + recovery,charger,kpoc,bootloader,etc)
1. 测试源码:
@@@@/meditek/kernel/kernel/system.c:
void arch_reset(char mode, const char *cmd)
{
char reboot = 0;
int res=0;
struct wd_api*wd_api = NULL;
res = get_wd_api(&wd_api);
printk("arch_reset: cmd = %s\n", cmd ? : "NULL");
if (cmd && !strcmp(cmd, "charger")) {//充电
/* do nothing */
}
else if (cmd && !strcmp(cmd, "recovery")) {/如果传下来的字符串是recovery=>就在RTC寄存器里设置某个特定值,
当uboot里读取RTC寄存器的时候如果获取了这个特定值,那就可以起recovery这个动作了
rtc_mark_recovery();
}
else if (cmd && !strcmp(cmd, "bootloader")){//进入bootloader(fastboot 模式)
rtc_mark_fast();
}
#ifdef MTK_KERNEL_POWER_OFF_CHARGING
else if (cmd && !strcmp(cmd, "kpoc")){ rtc_mark_kpoc(); }///(关机充电模式)
#endif
else {
reboot = 1;
}
if(res){
printk("arch_reset, get wd api error %d\n",res);
} else {
wd_api->wd_sw_reset(reboot);
}
}
tonylau@tonylau-OptiPlex-780:~/tony-workspace/new-v387/my-ap$ adb reboot bootloader
tonylau@tonylau-OptiPlex-780:~/tony-workspace/new-v387/my-ap$ adb reboot kpoc
tonylau@tonylau-OptiPlex-780:~/tony-workspace/new-v387/my-ap$ adb reboot charger
tonylau@tonylau-OptiPlex-780:~/tony-workspace/new-v387/my-ap$ adb reboot handoff_resolution(自己定义的)
若要实现自己的adb reboot handoff_resolution进入bootloader模式
===>>在1中添加如下代码即可:
//begin tony
if (cmd && !strcmp(cmd, "handoff_resolution_enable")) {
rtc_mark_fast();
}
//end tony
--------------------------------关机怎么用(总结)--------------------------------
关机怎么用?----总结
本文的分析是由Android给出的reboot接口开始的,但是分析来分析去,回头想一想会发现,Android给出的接口reboot就真的只能重启而已,不能进行关机操作,可以在跟踪这个流程的过程中会发现,确实是有存在关机的相关接口的。那么关机该怎么用呢? frameworks/base/services/java/com/android/serverBatteryService.java- private final void shutdownIfNoPower() {
- // shut down gracefully if our battery is critically low and we are not powered.
- // wait until the system has booted before attempting to display the shutdown dialog.
- if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {
- Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
- intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
- }
这样就可以了,不用多说了吧。
更多相关文章
- android生命周期
- Android中Message机制的灵活应用
- Android(安卓)Interface Definition Language (AIDL) android接
- Android自带Music播放器更新播放时间和进度条的方法
- zxing项目源码解读(2.3.0版本,Android部分)
- 详解 Android(安卓)的 Activity 组件
- 2010.11.28(2)———android 展示网页 和 调用js代码
- Android事件分发机制源码分析
- Android(安卓)NFC架构分析