一、Android开机运行脚本流程
在Android启动的过程中,在kernel/init/main.c中的start_kernel->rest_init->kernel_init->init_post->run_init_process("/sbin/init")中启动了init相关脚本,
static noinline int init_post(void)
804{
805 /* need to finish all async __init code before freeing the memory */
806 async_synchronize_full();
807 free_initmem();
808 mark_rodata_ro();
809 system_state = SYSTEM_RUNNING;
810 numa_default_policy();
811
812 log_boot("Kernel_init_done");
813
814 current->signal->flags |= SIGNAL_UNKILLABLE;
815
816 if (ramdisk_execute_command) {
817 run_init_process(ramdisk_execute_command);
818 printk(KERN_WARNING "Failed to execute %s\n",
819 ramdisk_execute_command);
820 }
821
822 /*
823 * We try each of these until one succeeds.
824 *
825 * The Bourne shell can be used instead of init if we are
826 * trying to recover a really broken machine.
827 */
828 if (execute_command) {
829 run_init_process(execute_command);
830 printk(KERN_WARNING "Failed to execute %s. Attempting "
831 "defaults...\n", execute_command);
832 }
833 run_init_process("/sbin/init");
834 run_init_process("/etc/init");
835 run_init_process("/bin/init");
836 run_init_process("/bin/sh");
837
838 panic("No init found. Try passing init= option to kernel. "
839 "See Linux Documentation/init.txt for guidance.");
840}
794static void run_init_process(const char *init_filename)
795{
796 argv_init[0] = init_filename;
797 kernel_execve(init_filename, argv_init, envp_init);
798}
二、Android关机流程简介
看这个看完这篇博客关机流程分析,我们基本上对关机流程有一个初步的认识,现在我们往流程中插入shell脚本,使用和开机时候相类似的办法来做。
三、定位到jni层
首先在blog中我们了解到,在关机或者重启的时候最终会分别调用jni层的如下函数,路径为:
frameworks/base/services/jni/com_android_server_power_PowerManagerService.cpp
196static void nativeShutdown(JNIEnv *env, jclass clazz) {
197 android_reboot(ANDROID_RB_POWEROFF, 0, 0);
198}
199
200static void nativeReboot(JNIEnv *env, jclass clazz, jstring reason) {
201 if (reason == NULL) {
202 android_reboot(ANDROID_RB_RESTART, 0, 0);
203 } else {
204 const char *chars = env->GetStringUTFChars(reason, NULL);
205 android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);
206 env->ReleaseStringUTFChars(reason, chars); // In case it fails.
207 }
208 jniThrowIOException(env, errno);
209}
在上面代码中,我们发现不管是nativeShutdown 还是nativeReboot函数最终都调用了android_reboot函数。
四、进入android_reboot函数
对应路径:
system/core/libcutils/android_reboot.c
int android_reboot(int cmd, int flags, char *arg)
105{
106 int ret;
107
108 if (!(flags & ANDROID_RB_FLAG_NO_SYNC))
109 sync();
110
111 if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))
112 remount_ro();
113
114 switch (cmd) {
115 case ANDROID_RB_RESTART:
116 ret = reboot(RB_AUTOBOOT);
117 break;
118
119 case ANDROID_RB_POWEROFF:
120 ret = reboot(RB_POWER_OFF);
121 break;
122
123 case ANDROID_RB_RESTART2:
124 ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
125 LINUX_REBOOT_CMD_RESTART2, arg);
126 break;
127
128 default:
129 ret = -1;
130 }
131
132 return ret;
133}
在这个函数中,重启或者关机都进入这里,当然细心的童鞋,应该已经发现了,在这里面,switch对应的所有分支选择也都将进入reboot函数,reboot函数非常简单,如下所示路径:bionic/libc/unistd/reboot.c
28#include <unistd.h>
29#include <sys/reboot.h>
30
31int reboot (int mode)
32{
33 return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );
34}
因此,我们想要关机时候插入脚本的话,就可以选择在android_reboot函数或者在reboot函数中做一些愉快的操作。
五、插入脚本
1.需要点的技能点
需要一些小知识如下:fork,execl族,shell脚本和.bin文件,相信大家的技能树上都有的。。。。
2.准备工作
(1)首先需要一个测试的脚本,名称为myclose:
#!/bin/sh
echo "I am myclose.sh open"
./system/bin/vibrator_test
echo "I am myclose.sh close"
(2)然后加入一个.bin文件,该文件的作用也就是控制震动马达震动10S,vibrator_test:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
int fd;
int ret;
char data[1];
if((fd=open("/sys/class/timed_output/vibrator/vibr_on",O_WRONLY))==-1){
printf("open memdev WRONG!\n");
perror("open");
}
else
printf("open memdev SUCCESS!\n");
data[0] = '1';
write(fd, &data,sizeof(data));
sleep(10);
data[0] = '0';
write(fd, &data,sizeof(data));
close(fd);
return 0;
}
3.修改android_reboot
对它的修改很简单,也就是首先使用fork生成一个子进程,然后用execl族取代子进程,运行myclose的脚本文件,父进程等待子进程执行完了之后,在继续剩下的关机流程,
注:不要使用vfork,这样会导致android_reboot传进来的参数cmd自己就发生变化,就算不操作cmd,求大神指导原因。。。。
修改后的android_reboot函数如下:
int android_reboot(int cmd, int flags, char *arg)
{
int ret;
SLOGD("yulinghan i am here!!!!,cmd = %d",cmd);
pid_t pc,pid;
pid = getpid();
pc = fork();
if(pc<0){
exit(1);
}
else if(pc == 0){
SLOGD("yulinghan Child ID is %d,father id is %d,cmd = %d",getpid(),getppid(),cmd);
execl("/system/bin/sh","sh","/system/bin/myclose",NULL);
exit(0);
}
else{
pid = waitpid(pc, NULL, 0);
SLOGD("yulinghan I am father,my id is %d,cmd = %d",getpid(),cmd);
if (!(flags & ANDROID_RB_FLAG_NO_SYNC))
sync();
if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))
remount_ro();
switch (cmd) {
case ANDROID_RB_RESTART:
SLOGD("yulinghan ANDROID_RB_RESTART");
ret = reboot(RB_AUTOBOOT);
break;
case ANDROID_RB_POWEROFF:
SLOGD("yulinghan ANDROID_RB_POWEROFF");
ret = reboot(RB_POWER_OFF);
break;
case ANDROID_RB_RESTART2:
SLOGD("yulinghan ANDROID_RB_RESTART2");
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, arg);
break;
default:
SLOGD("yulinghan default\n");
ret = -1;
}
return ret;
}
}
4.结果测试
(1)修改android_reboot之后重新编译,编译成库,push进手机或者直接编译系统都行。。
(2)adb push 将脚本myclose和执行文件vibrator_test放到/system/bin/ 目录下,注意修改他们的执行权限。
( 3)执行reboot,测试效果如下:
(4)输入reboot命令之后,首先打印了 "I am myclose.sh open"
然后开始执行 ./system/bin/vibrator_test
手机在震动了10s之后,再打印了 "I am myclose.sh close",之后才重启。
5.j结束语
在加入了这个myclose脚本之后,如果想在关机流程中加入想执行的操作,直接将该操作加在myclose上面就好,相当的美妙。。