工作笔记-code
1.
android系统启动完成会发送Intent.ACTION_BOOT_COMPLETED事件,我们在 base/services/java/com/android/server/WiredAccessoryObserver.java中可以看到类似代码
linux-3.0/drivers/switch/ switch_headset.c中会根据无耳机,三段耳机,四段耳机和四段耳机是否有hook键按下4个状态更新state的值为0 ,1, 2,3,并且切换机台MIC和耳机
private static final String uEventInfo[][] = { {"DEVPATH=/devices/virtual/switch/h2w", "/sys/class/switch/h2w/state", "/sys/class/switch/h2w/name"}, {"DEVPATH=/devices/virtual/switch/usb_audio", "/sys/class/switch/usb_audio/state", "/sys/class/switch/usb_audio/name"}, {"DEVPATH=/devices/virtual/switch/hdmi", "/sys/class/switch/hdmi/state", "/sys/class/switch/hdmi/name"} };
大体流程是用定时器每200ms检查一次是否有耳机插入,如果有4段耳机,延时30ms检查hook key是否按下,这样,如果旧的state和新的state不相等,就用uevent上报状态改变
事件
可参考电路图P11的说明:
检测耳机插入:
1、0V-0.2V 则判定为3节耳机;
2、1V-2.5V 则判定为4节耳机;
3、检测为4接耳机后如果ADC再次检测为0V则认为HOOK见按下。
2.
在drivers/media/pa中的是提供借口给上层做外音和内音切换的,也就是如FM,播放器等都会检测耳机是否插入,以进行外音和内音切换,看看其中的代码:
pa_dev_class = class_create(THIS_MODULE, "pa_cls");//这一句运行后,会创建虚拟文件系统/sys/class/pa_cls device_create(pa_dev_class, NULL,dev_num, NULL, "pa_dev");//这一句运行后,会创建节点/dev/pa_dev printk("[pa_drv] init end!!!\n");
他对应的framework层代码在device/softwinner/common/hardware/audio/audio_hw.c中
而file_operations结构体和节点对应起来则是通过如下的关系,如touch中:
ret= register_chrdev(I2C_MAJOR,"aw_i2c_ts",&aw_i2c_ts_fops );//I2C_MAJOR是自己定义的主设备号
接着调用:device_create(i2c_dev_class, &client->adapter->dev, MKDEV(I2C_MAJOR,client->adapter->nr), NULL, "aw_i2c_ts%d", client->adapter->nr);这样
就关联起来了
3.
两种定时器的使用:
3.1.
static struct hrtimer vibe_timer; INIT_WORK(&vibrator_work, update_vibrator); static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) { schedule_work(&vibrator_work); } hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); vibe_timer.function = vibrator_timer_func;这样在你想要启动定时器的函数中调用如下语句:
hrtimer_start(&vibe_timer,
ktime_set(value / 1000, (value % 1000) * 1000000), //ktime_set的第一个参数单位时秒,第二个单位是纳秒
HRTIMER_MODE_REL);
schedule_work(&vibrator_work); //hrtimer_start后调用一次schedule_work,时间到后,再调用一次schedule_work,这样在两次schedule_work中可以做如控制按键灯
亮灭等动作,具体例子可参考drivers/misc/sun4i-vibrator.c
这个定时器还提供了一些查询功能,如可以查询定时器当前是否激活的,定时器当前的剩余时间等,如下面的函数:
static int vibrator_get_time(struct timed_output_dev *dev) { struct timespec time_tmp; if (hrtimer_active(&vibe_timer)) {//判断是否激活 ktime_t r = hrtimer_get_remaining(&vibe_timer);//取得定时器剩余时间 time_tmp = ktime_to_timespec(r);//时间单位转换,他和上面的ktime_set相当于相反过程的转换 //return r.tv.sec * 1000 + r.tv.nsec/1000000; return time_tmp.tv_sec* 1000 + time_tmp.tv_nsec/1000000;//返回的单位是毫秒 } else return 0; }
3.2.
struct timer_list timer; static void earphone_hook_handle(unsigned long data) { mod_timer(&switch_data->timer, jiffies + msecs_to_jiffies(200)); } init_timer(&timer); timer.function = &earphone_hook_handle; timer.data = (unsigned long)switch_data; add_timer(&switch_data->timer);//这一句之后已经启动定时器了
4.
声卡的设备节点在/proc/asound/card0,创建过程为snd_card_create -> snd_ctl_create
创建sys节点snd_card_register -> device_create(sound_class, card->dev, MKDEV(0, 0), card, "card%i", card->number)sound_class = class_create(THIS_MODULE, "sound");
sound_class->devnode = sound_devnode;
其中:
static char *sound_devnode(struct device *dev, mode_t *mode) { if (MAJOR(dev->devt) == SOUND_MAJOR) return NULL; return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev)); }
这样声卡的class出现在/class/sys/sound/中,sound_devnode就决定了设备节点出现在/dev/snd/中,用户空间操作的就是/dev/snd/中的设备节点。
接着调用snd_device_register_all函数注册前面挂接在card->device链表中的所有设备:
185 int snd_device_register_all(struct snd_card *card) 186 { 187 struct snd_device *dev; 188 int err; 189 190 if (snd_BUG_ON(!card)) 191 return -ENXIO; 192 list_for_each_entry(dev, &card->devices, list) { 193 if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { 194 if ((err = dev->ops->dev_register(dev)) < 0) 195 return err; 196 dev->state = SNDRV_DEV_REGISTERED; 197 } 198 } 199 return 0; 200 }
我们看看设备是如何挂在card->device链表中的:
snd_card_sun4i_codec_pcm -> snd_pcm_new -> snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)),其中ops结构体为: 720 static struct snd_device_ops ops = { 721 .dev_free = snd_pcm_dev_free, 722 .dev_register = snd_pcm_dev_register, 723 .dev_disconnect = snd_pcm_dev_disconnect, 724 };
snd_pcm_dev_register为:
976 static int snd_pcm_dev_register(struct snd_device *device) 977 { 978 int cidx, err; 979 struct snd_pcm_substream *substream; 980 struct snd_pcm_notify *notify; 981 char str[16]; 982 struct snd_pcm *pcm; 983 struct device *dev; 984 985 if (snd_BUG_ON(!device || !device->device_data)) 986 return -ENXIO; 987 pcm = device->device_data; 988 mutex_lock(®ister_mutex); 989 err = snd_pcm_add(pcm); 990 if (err) { 991 mutex_unlock(®ister_mutex); 992 return err; 993 } 994 for (cidx = 0; cidx < 2; cidx++) { 995 int devtype = -1; 996 if (pcm->streams[cidx].substream == NULL) 997 continue; 998 switch (cidx) { 999 case SNDRV_PCM_STREAM_PLAYBACK: 1000 sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);//这里就是设备节点的名字 1001 devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 1002 break; 1003 case SNDRV_PCM_STREAM_CAPTURE: 1004 sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);//这里就是设备节点的名字 1005 devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 1006 break; 1007 } 1008 /* device pointer to use, pcm->dev takes precedence if 1009 * it is assigned, otherwise fall back to card's device 1010 * if possible */ 1011 dev = pcm->dev; 1012 if (!dev) 1013 dev = snd_card_get_device_link(pcm->card); 1014 /* register pcm */ 1015 err = snd_register_device_for_dev(devtype, pcm->card, 1016 pcm->device, 1017 &snd_pcm_f_ops[cidx], 1018 pcm, str, dev);//在这个函数中,最终调用device_create(sound_class, device, MKDEV(major, minor),private_data, //"%s", name);来创建设备 1019 if (err < 0) { 1020 list_del(&pcm->list); 1021 mutex_unlock(®ister_mutex); 1022 return err; 1023 } 1024 snd_add_device_sysfs_file(devtype, pcm->card, pcm->device, 1025 &pcm_attrs);
看看我的设备节点名称:
$ cd /dev/snd $ ls -l crw-rw----+ 1 root audio 116, 8 2011-02-23 21:38 controlC0 crw-rw----+ 1 root audio 116, 4 2011-02-23 21:38 midiC0D0 crw-rw----+ 1 root audio 116, 7 2011-02-23 21:39 pcmC0D0c crw-rw----+ 1 root audio 116, 6 2011-02-23 21:56 pcmC0D0p crw-rw----+ 1 root audio 116, 5 2011-02-23 21:38 pcmC0D1p crw-rw----+ 1 root audio 116, 3 2011-02-23 21:38 seq crw-rw----+ 1 root audio 116, 2 2011-02-23 21:38 timer controlC0 --> 用于声卡的控制,例如通道选择,混音,麦克风的控制等 midiC0D0 --> 用于播放midi音频 pcmC0D0c --> 用于录音的pcm设备 pcmC0D0p --> 用于播放的pcm设备 seq --> 音序器 timer --> 定时器
其中,C0D0代表的是声卡0中的设备0,pcmC0D0c最后一个c代表capture,pcmC0D0p最后一个p代表playback,这些都是alsa-driver中的命名规则。从上面的列表可以看出,我
的声卡下挂了6个设备,根据声卡的实际能力,驱动实际上可以挂上更多种类的设备,在include/sound/core.h中,定义了以下设备类型,通常,我们更关心的是pcm和control
这两种设备。
对于sound/core/control.c文件
#ifdef CONFIG_COMPAT
#include "control_compat.c"
#else
#define snd_ctl_ioctl_compat NULL
#endif
下面的"controlC%i"声卡对应的控制节点fops的compat_ioctl,当没有定义CONFIG_COMPAT时,将被置为NULL
hal层的IOCTRL会对应KERNEL层的core/control.c文件
frameworks/base/services/java/com/android/server/WiredAccessoryObserver.java会监听耳机拔插事件
而打开hal层audio_hw.c的JNI层代码在frameworks/base/services/audioflinger/AudioFlinger.cpp中
audio_hw.c是按照hardware/libhardware/include/hardware/audio.h定义的接口实现就行了
5.
getprop 命令可以查看系统属性状态
6.
运行JAVA代码:
javac xxx.java(xxx为类的名字)java xxx
7.
编译时候提示,You have tried to change the API from what has been previously approved.
To make these errors go away, you have two choices: 1) You can add "@hide" javadoc comments to the methods, etc. listed in the errors above. 2) You can update current.txt by executing the following command: make update-api To submit the revised current.txt to the main Android repository, you will need approval. ******************************
如果你是想要让这个新增的API只能内部使用,则加上 /** {@hide} */,如我的代码:frameworks/base/core/java/android/os中新增了IHelloService.aidl这个服务,如下
为其中的内容:
1 package android.os; 2 3 /** {@hide} */ 4 interface IHelloService { 5 void setVal(int val, String path); 6 int getVal(String path); 7 }
还有新增JNI层的一些用法:
66 {"init_native", "()Z", (void*)hello_init}, //无型参,Z表示返回值为boolean型 67 {"setVal_native", "(ILjava/lang/String;)V", (void*)hello_setVal},// I表示第一个型参为int,Ljava/lang/String表示第二个型参为String, 68 {"getVal_native", "(Ljava/lang/String;)I", (void*)hello_getVal},
由上面可以知道hello_setVal对应的HAL层原型为hello_setVal(int, char *), 在JNI中还要将String转换为char*才能使用,语法如下:
28 /*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/ 29 static jint hello_getVal(JNIEnv* env, jobject clazz, jstring path) { 30 const char *extraInfoStr = env->GetStringUTFChars(path, NULL);//将String转换为char* 31 int val = 0; 32 if(!hello_device) { 33 LOGI("Hello JNI: device is not open."); 34 return val; 35 } 36 hello_device->get_val(hello_device, &val, extraInfoStr); 37 38 LOGI("Hello JNI: get value %d from device.", val); 39 env->ReleaseStringUTFChars(path, extraInfoStr);//用完后要释放 40 41 return val; 42 }
8.
系统build.prop文件属性的读取文件在base/core/java/android/os/SystemProperties.java,属性接口在frameworks/base/core/java/android/os/Build.java中
9.
A10申请外部中断的步骤:
中断号的定义在drivers/input/touchscreen/ctp_platform_ops.h中:#define PIO_BASE_ADDRESS (0xf1c20800)//这是外部所有IO口中断的入口 #define PIOA_CFG1_REG (PIO_BASE_ADDRESS+0x4) #define PIOA_DATA (PIO_BASE_ADDRESS+0x10) #define DELAY_PERIOD (5) #define SW_INT_IRQNO_PIO 28 #define PIO_INT_STAT_OFFSET (0x214) #define CTP_IRQ_NO (IRQ_EINT21) 309 static int ctp_judge_int_occur(void) 310 { 311 //int reg_val[3]; 312 int reg_val; 313 int ret = -1; 314 315 reg_val = readl(gpio_addr + PIO_INT_STAT_OFFSET);//偏移PIO_INT_STAT_OFFSET处的地址就是外部IO口中断的状态寄存器 316 if(reg_val&(1<<(CTP_IRQ_NO))){//CTP_IRQ_NO=IRQ_EINT21,就是TP的中断脚EINT21,配置脚本中有 ctp_int_port = port:PH21<6><default> 317 ret = 0; 318 } 319 return ret; 320 } 1681 static irqreturn_t ft5x_ts_interrupt(int irq, void *dev_id) 1682 { 1683 struct ft5x_ts_data *ft5x_ts = dev_id; 1684 1685 // print_int_info("==========------ft5x_ts TS Interrupt-----============\n"); 1686 if(!ctp_ops.judge_int_occur()){ //要判断是外部哪一个IO口所引起的中断 1687 // print_int_info("==IRQ_EINT21=\n"); 1688 ctp_ops.clear_penirq(); 1689 if (!work_pending(&ft5x_ts->pen_event_work)) 1690 { 1691 // print_int_info("Enter work\n"); 1692 queue_work(ft5x_ts->ts_workqueue, &ft5x_ts->pen_event_work); 1693 } 1694 }else{ 1695 // print_int_info("Other Interrupt\n"); 1696 return IRQ_NONE; 1697 } 1698 1699 return IRQ_HANDLED; 1700 } 1895 err = request_irq(SW_INT_IRQNO_PIO, ft5x_ts_interrupt, IRQF_TRIGGER_FALLING | IRQF_SHARED, "ft5x_ts", ft5x_ts);
10.
格式化userdata分区名字的方法在init.sun4i.rc中修改即可,如代码:format_userdata /dev/block/nandi IPND5,IPND5即为电脑中看到的盘符名字
11.
全志平台的关机代码在drivers/power/axp_power/axp-mfd.c中:
311 /* PM hookup */ 312 if(!pm_power_off) 313 pm_power_off = axp_power_off;axp_power_off定义如下:
static void axp_power_off(void) { uint8_t val; #if defined (CONFIG_AW_AXP18) axp_set_bits(&axp->dev, POWER18_ONOFF, 0x80); #endif #if defined (CONFIG_AW_AXP19) axp_set_bits(&axp->dev, POWER19_OFF_CTL, 0x80); #endif #if defined (CONFIG_AW_AXP20) if(pmu_pwroff_vol >= 2600 && pmu_pwroff_vol <= 3300){ if (pmu_pwroff_vol > 3200){ val = 0x7; } else if (pmu_pwroff_vol > 3100){ val = 0x6; } else if (pmu_pwroff_vol > 3000){ val = 0x5; } else if (pmu_pwroff_vol > 2900){ val = 0x4; } else if (pmu_pwroff_vol > 2800){ val = 0x3; } else if (pmu_pwroff_vol > 2700){ val = 0x2; } else if (pmu_pwroff_vol > 2600){ val = 0x1; } else val = 0x0; axp_update(&axp->dev, POWER20_VOFF_SET, val, 0x7); } val = 0xff; if (!use_cou){ axp_read(&axp->dev, POWER20_COULOMB_CTL, &val); val &= 0x3f; axp_write(&axp->dev, POWER20_COULOMB_CTL, val); val |= 0x80; val &= 0xbf; axp_write(&axp->dev, POWER20_COULOMB_CTL, val); } //led auto axp_clr_bits(&axp->dev,0x32,0x38); axp_clr_bits(&axp->dev,0xb9,0x80); printk("[axp] send power-off command!\n"); mdelay(20); if(power_start != 1){ axp_read(&axp->dev, POWER20_STATUS, &val);//读取是否处于充电状态,如果是,则reset,进入uboot充电 if(val & 0xF0){ axp_read(&axp->dev, POWER20_MODE_CHGSTATUS, &val); if(val & 0x20){//判断电池在的话才进入充电 printk("[axp] set flag!\n"); axp_write(&axp->dev, POWER20_DATA_BUFFERC, 0x0f); mdelay(20); printk("[axp] reboot!\n"); arch_reset(0,NULL); printk("[axp] warning!!! arch can't ,reboot, maybe some error happend!\n"); } } } axp_write(&axp->dev, POWER20_DATA_BUFFERC, 0x00); mdelay(20); axp_set_bits(&axp->dev, POWER20_OFF_CTL, 0x80);//就是写1到寄存器32H,表示关闭除LDO1外的所有电源,请看AXP209手册P34 mdelay(20); printk("[axp] warning!!! axp can't power-off, maybe some error happend!\n"); #endif }
而arch_reset(0,NULL);的代码位于arch/arm/mach-sun4i/include/mach/system.h中,利用的是看门狗复位:
39 static inline void arch_reset(char mode, const char *cmd) 40 { 41 /* use watch-dog to reset system */ 42 #define WATCH_DOG_CTRL_REG (SW_VA_TIMERC_IO_BASE + 0x0094)//SW_VA_TIMERC_IO_BASE是虚拟地址 43 *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 0; 44 __delay(100000); 45 *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 3; 46 while(1); 47 }
12.
codec控制设置:
Number of controls: 32 ctl type num name value 注释 0 INT 1 Master Playback Volume 59 //音量大小设置 1 BOOL 1 Playback PAMUTE SWITCH On //声音输出总开关,打开才有声音输出,看手册P258 PAMUTE 2 BOOL 1 Playback MIXPAS Off //输入的声音或者经过混音器输出的声音输出的开关,如3G的语音就是要打开,看手册 //P258 MIXPAS 3 BOOL 1 Playback DACPAS On //输入的声音不经混音器直接输出,如播放音乐时要打开,看手册P258 DACPAS //但也可以选择声音从混音器输出,关掉3,使能2、5、6、7和15即可 4 INT 1 Mic Output Mix 0 //MIC1、2输入的左右声道是否打开到混音器中一起输出到外音 5 BOOL 1 Ldac Right Mixer Off //输入的声音经过混音器混音时(对应手册DACMISX开关),左声道混音,右没声音 6 BOOL 1 Rdac Right Mixer Off //输入的声音经过混音器混音时(对应手册DACMISX开关),右声道混音,左没声音 7 BOOL 1 Ldac Left Mixer Off //输入的声音经过混音器混音时(对应手册DACMISX开关),左声道、左声道的混音 8 BOOL 1 FmR Switch Off //FM到混音器是否打开,如收音时要打开,但这样上层无法控制音量 9 BOOL 1 FmL Switch Off //FM到混音器是否打开,如收音时要打开,但这样上层无法控制音量 10 BOOL 1 LineR Switch Off //线路输入到混音器是否打开,如3G通话要打开,而播放音乐不是线路输入,不用打开 11 BOOL 1 LineL Switch Off //线路输入到混音器是否打开,如3G通话要打开,而播放音乐不是线路输入,不用打开 12 INT 1 MIC output volume 3 //MIC到混音器输出的增益, 13 INT 1 Fm output Volume 3 //FM到混音器输出的增益, 14 BOOL 1 Line output Volume On //线路输入到混音器的增益,只有两个等级,-1.5db和0db 15 BOOL 1 MIX Enable Off //混音器使能 16 BOOL 1 DACALEN Enable On //数字到模拟的输出是否是能,如音乐要打开,但3G通话是直接旁路输出不用打开 17 BOOL 1 DACAREN Enable On //数字到模拟的输出是否是能,如音乐要打开,但3G通话是直接旁路输出不用打开 18 BOOL 1 PA Enable On //PA使能,只有PA打开,才有声音输出 19 BOOL 1 dither enable Off //手册没有说明 20 BOOL 1 Mic1outn Enable Off //MIC是否直接输出,如3G通话MIC直接输出到3G模块中 21 INT 1 LINEIN APM Volume 7 //线路模拟输入的增益,有7个增益等级,只是线路的增益,不会影响去他输入源的增益 22 BOOL 1 Line-in-r function define Off //输入信号差分是能,如3G通话时候要使能,否则2G卡会受到干扰,有噪音 23 INT 1 ADC Input source 0 //模拟到数字转换的输入源选择,可以有3G线路,FM输入,MIC输入等选择 24 INT 1 Capture Volume 3 //模拟到数字转换的增益,有8个等级,0-7(这是总的线路增益,影响到FM,MIC, //线路等输入源) 25 INT 1 Mic2 gain Volume 2 //MIC2增益打开的情况下,输入增益的等级设置,没有用到这一路 26 INT 1 Mic1 gain Volume 2 //MIC2增益打开的情况下,输入增益的设置,有4个等级,3G通话时候有增益的话会有噪音 27 BOOL 1 VMic enable On //MIC电源,用MIC时候就要打开 28 BOOL 1 Mic2 amplifier enable Off //MIC2输入增益是否打开 29 BOOL 1 Mic1 amplifier enable On //MIC2输入增益是否打开 30 BOOL 1 ADCL enable Off //模拟到数字的左声道是否打开,如录音时候有耳麦或则没有耳麦都就要打开 31 BOOL 1 ADCR enable Off //模拟到数字的左声道是否打开,如录音时候有耳麦或则没有耳麦都就要打开
13.
camera的设置:
drivers/media/video/sun4i_csi目录下对应有csi0 、csi1两个文件夹,分别是两个摄像头对应的平台代码,他们对应的节点为/dev/video0前置ov5640, /dev/video1后置ov2655或者ov2643,sun4i_drv_csi.c中控制camera的主要是csi_power_en脚和csi_stby脚,其中两个camera的csi_power_en相同,在csi_open函数中会打开电源上电,
但打开后马上用 v4l2_subdev_call(dev->sd,core, s_power, CSI_SUBDEV_STBY_ON);来进入standby 模式,只有在摄像头切换中,启用它后调用
static int internal_s_input(struct csi_dev *dev, unsigned int i)函数,在该函数中使用v4l2_subdev_call(dev->sd,core, s_power, CSI_SUBDEV_STBY_OFF);
才真正工作,所以两个摄像头同时只有一个工作,另一个处于standby模式
14.
property_get/property_set会从以下文件读取属性:
1: /default.prop2: /system/build.prop
3: /system/default.prop
4: /data/local.prop
15.
android的电量警告和严重电量警告的设置在/frameworks/base/core/res/res/values/config.xml中,在frameworks/base/services/java/com/android/server/
BatteryService.java中读取这些值,而关机电量是在BatteryService.java文件中判断,在update函数中被调用,如下:208 private final void shutdownIfNoPower() { 209 // shut down gracefully if our battery is critically low and we are not powered. 210 // wait until the system has booted before attempting to display the shutdown dialog. 211 if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {//mBatteryLevel为0就关机 212 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); 213 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); 214 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 215 mContext.startActivity(intent); 216 } 217 }
16.
APK签名机制:
<uses-sdk android:minSdkVersion="7" android:sharedUserId="android.uid.system"/>java -jar signapk.jar platform.x509.pem platform.pk8 old.apk new.apk
17.
按键事件的上报,如果要上报某一个键值,要用set_bit来设置,否则不上报该键值,如:
for (i = 0; i < KEY_MAX_CNT; i++) set_bit(sun4i_scankeycodes[i], sun4ikbd_dev->keybit);
18.
camera拍照的各种声音播放在frameworks/base/core/java/android/hardware/CameraSound.java中实现
19.
JNI_OnLoad函数是在android VM执行*so中的System.loadLibrary函数时候执行的,所以一般在该函数中做一些初始化设置和返回JNI版本,
20.
u-boot流程:
头文件在include/configs/sun4i.h中从arch/arm/cpu/armv7/start.S开始
跳到arch/arm/lib/board.c,
21.
android源代码中编译jar包的方法:
在需要导出jar包的目录下新建android,内容如下:LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_MODULE_TAGS := optional LOCAL_MODULE :=my_fmradio include $(BUILD_JAVA_LIBRARY)
22.
android背光调节代码路线:
packages/apps/Settings/src/com/android/settings/BrightnessPreference.java -> setBrightness(myState.progress + mScreenBrightnessDim); private void setBrightness(int brightness) { try { IPowerManager power = IPowerManager.Stub.asInterface( ServiceManager.getService("power")); if (power != null) { power.setBacklightBrightness(brightness); } } catch (RemoteException doe) { } }
调用IPowerManager类接口,实现在frameworks/base/services/java/com/android/server/PowerManagerService.java
brightness = Math.max(brightness, mScreenBrightnessDim); mLcdLight.setBrightness(brightness); mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0); mButtonLight.setBrightness(brightness);
可以看到,不但是设置了LCD的背光,如果有,键盘,按键的背光设置了,其中的private LightsService.Light mLcdLight;所以看到目录下的LightsService.java
public void setBrightness(int brightness, int brightnessMode) -> setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode) ->
setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
看看JNI层的实现,进入frameworks/base/services/jni/com_android_server_LightsService.cpp
这里看看JNI是如何找到HAL层的 hw_module_t结构体的:
hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);//这里传进去的是二维指针,才能返回HAL层的指针哦,关于这个不多说
看看hw_get_module的定义,在hardware/libhardware/hardware.c中:
hw_get_module -> hw_get_module_by_class:
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) { if (i < HAL_VARIANT_KEYS_COUNT) { if (property_get(variant_keys[i], prop, NULL) == 0) { continue; } snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH2, name, prop); if (access(path, R_OK) == 0) break; snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH1,name,prop); if (access(path, R_OK) == 0) break; } else { snprintf(path, sizeof(path), "%s/%s.default.so", HAL_LIBRARY_PATH1, name); if (access(path, R_OK) == 0) break; } }
查找并加载hal层的so库,最后load(class_id, path, module);用dlopen动态加载so库,这里会比较打开的so库中id的名字和传进来的是否一样,如果都是"lights",才能加
载成功。
看看so库的实现,进入HAL层,在device/softwinner/crane-common/hardware/libhardware/lights/lights.c中:
这里打开的是dev->fd = open("/dev/disp", O_RDONLY);
他在kernel中对应的文件为:drivers/video/sun4i/disp/dev_disp.c
上面是手动调节背光的流程,如果是设备有自动感光设备(light-sensor),那么调节代码也是在PowerManagerService.java中开始,如下:
mSensorManager.registerListener(mLightListener, mLightSensor,
LIGHT_SENSOR_RATE);
LIGHT_SENSOR_RATE为监听时间间隔,而mLightListener定义为:
3133 SensorEventListener mLightListener = new SensorEventListener() {3134 public void onSensorChanged(SensorEvent event) {3135 synchronized (mLocks) {3136 // ignore light sensor while screen is turning off3137 if (isScreenTurningOffLocked()) {3138 return; 3139 }3140 3141 int value = (int)event.values[0];3142 long milliseconds = SystemClock.elapsedRealtime();3143 if (mDebugLightSensor) {................................................3159 mHandler.removeCallbacks(mAutoBrightnessTask);3160 mLightSensorPendingDecrease = (value < mLightSensorValue);3161 mLightSensorPendingIncrease = (value > mLightSensorValue);3162 if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {3163 mLightSensorPendingValue = value;3164 mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);..................................................}
我们可以看到,如果光感有数据变化,会调用onSensorChanged方法,value = (int)event.values[0];读取光感数值,然后到3164行
mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);去调节背光亮度,这里的mAutoBrightnessTask定义:
2469 private Runnable mAutoBrightnessTask = new Runnable() { 2470 public void run() {2471 synchronized (mLocks) {2472 if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {2473 int value = (int)mLightSensorPendingValue;2474 mLightSensorPendingDecrease = false;2475 mLightSensorPendingIncrease = false;2476 lightSensorChangedLocked(value);2477 }2478 }2479 }2480 };
最后是在2476中进行背光设置,在PowerManagerService.java中,我们也看到距离感(主要应用是放耳边听电话时候自动关闭背光,防止乱触摸现象)应也是在这个文件中读取
的:
SensorEventListener mProximityListener = new SensorEventListener() {3090 public void onSensorChanged(SensorEvent event) {................................}
23.
编译,分析uboot:
./build.sh -p sun4i_crane或者 make sun4i
board_init_r 从汇编跳到C入口,入口在文件arch/arm/lib/board.c中
启动用到的三个设置环境命令:
include/configs/sun4i.h中:
bootcmd=run setargs boot_normal; board/allwinner/a10-evb/a10-evb.c -> check_android_misc()中 setenv("bootcmd", "run setargs boot_recovery"); setenv("bootcmd", "run setargs boot_fastboot");
他们分别为:
boot_normal=nand read 50000000 boot; boota 50000000//nand对应cmd_nand命令,会查找boot分区,把它读到RAM地址0x50000000中 boot_recovery=nand read 50000000 recovery; boota 50000000//nand对应cmd_nand命令,会查找recovery分区,把它读到RAM地址0x50000000中 boot_fastboot=fastboot//启动cmd_fastboot命令,等待USB发送fastboot命令到来
24.
编译kernel
./build.sh -p sun4i_crane25.
binder通讯机制的理解:
25.1.
rocessState::ProcessState() : mDriverFD(open_driver()) , mVMStart(MAP_FAILED) , mManagesContexts(false) , mBinderContextCheckFunc(NULL) , mBinderContextUserData(NULL) , mThreadPoolStarted(false) , mThreadPoolSeq(1)
这里打开的可以是service的fd,也是和servicemanager通讯的FD,和进程相关,一个进程映射一个FD就可以了,service要把自己添加到servicemanager中,就是打开该
fd,但也传进了另外一个参数mHandle(0),表示自己是要和servicemanager通讯,并且把自己的binder实体也传进去,以便挂在servicemanager端,然后service也是利用该FD
等待和client的通讯,整个系统一共有20多处打开FD的LOG。而service等待client数据并且处理数据的地方是下面这两句:
ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool();
就是启动线程等待client的请求
25. 2.
每个进程都有自己的defaultServiceManager(),如frameworks/base/media/mediaserver/main_mediaserver.cpp这个编译出来的执行文件就是一个单独的进程:
int main(int argc, char** argv) { sp<ProcessState> proc(ProcessState::self()); sp<IServiceManager> sm = defaultServiceManager(); LOGI("ServiceManager: %p", sm.get()); AudioFlinger::instantiate(); MediaPlayerService::instantiate(); CameraService::instantiate(); AudioPolicyService::instantiate(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); }
其他的进程会有自己独立的defaultServiceManager(),这一点从加入的log打印出来有20多个就可以证明:
34 sp<IServiceManager> defaultServiceManager() 35 { 36 if (gDefaultServiceManager != NULL) return gDefaultServiceManager; 37 38 { 39 AutoMutex _l(gDefaultServiceManagerLock); 40 if (gDefaultServiceManager == NULL) { 41 LOGI("luis:go to here");//打印出来有20多个 42 gDefaultServiceManager = interface_cast<IServiceManager>( 43 ProcessState::self()->getContextObject(NULL)); 44 } 45 }
25.3.
service传输的数据要拷贝到内核空间的MMAP共享的空间,servicemaneger和client则通过指针就可以保存可操作MMAP空间这些数据,所以MMAP只有一次数据拷贝,一就是
binder进程的通讯只有一次数据拷贝4. client要从servicemanager获得service接口,也要打开一个FD才能与servicemanager通讯,它也调用IPCThreadState::talkWithDriver这个与binder驱动交互,但打开
的FD是这个进程打开defaultServiceManager时候,函数创建ProcessState对象时,在ProcessState构造函数通过open文件操作函数打开设备文件/dev/binder时设置好的FD,
而且传进来的handle值为0,表示目标Binder对象是ServiceManager,但它自己并不像service一样有binder传给servicemanager,而相反,要从servicemanager中取得一个
binder实体的handle
26.
init.sun4i.rc中format_userdata /dev/block/nandi IPND5格式化UMS分区,他对应的命令在system/core/init/builtins.c中:
int do_format_userdata(int argc, char **argv) { const char *devicePath = argv[1]; char bootsector[512]; char lable[32]; int fd; int num; pid_t child; int status; fd = open(devicePath, O_RDONLY); if( fd <= 0 ) { ERROR("open device error :%s", strerror(errno)); return 1; } memset(bootsector, 0, 512); read(fd, bootsector, 512); close(fd); if( (bootsector[510]==0x55) && (bootsector[511]==0xaa) ) { ERROR("dont need format %s", devicePath); return 1; } else // 格式化 { ERROR("start format %s", devicePath); child = fork(); if (child == 0) { ERROR("fork to format %s", devicePath); execl("/system/bin/logwrapper","/system/bin/logwrapper","/system/bin/newfs_msdos","-F","32","-O","android","-c","8", "-L",argv[2],argv [1], NULL); exit(-1); } ERROR("wait for format %s", devicePath); while (waitpid(-1, &status, 0) != child) ; ERROR("format %s ok", devicePath); return 1; } }
27. sensor的代码结构:
frameworks/base/services/sensorservice/SensorService.cpp中
void SensorService::onFirstRef() 67 { 68 LOGD("nuSensorService starting..."); 69 70 SensorDevice& dev(SensorDevice::getInstance());//取得sensor列表,就是调用hw_get_module加载so库, switch (list[i].type) { 87 case SENSOR_TYPE_ORIENTATION: 88 orientationIndex = i; 89 break; 90 case SENSOR_TYPE_GYROSCOPE: 91 hasGyro = true; 92 break; 93 case SENSOR_TYPE_GRAVITY: 94 case SENSOR_TYPE_LINEAR_ACCELERATION://根据type来判断是那种类型的sensor,所以我们在device下的so库中要注意该类型的赋值 95 case SENSOR_TYPE_ROTATION_VECTOR: 96 virtualSensorsNeeds &= ~(1<<list[i].type); 97 break;
28. 上层读取input事件时,一般是通过约定的名字来确定到底和/dev/input下的哪一个设备关联,如imapx15的gsensor,hardware/bosch_sensors/sensors.cpp中,
打开/dev/input目录后,遍历所有设备,根据约定的字符名字匹配:
539 if(ioctl(d06_read_fd, EVIOCGNAME(sizeof(name)),name) > 0) 540 { 541 ALOGD("devname=%s\n",devname); 542 ALOGD("name=%s\n",name); 543 if(!strcmp(name,INPUT_NAME_ACC))//"DMT_Gsensor")) 544 { 545 ALOGD("%s:name=%s,fd=%d\n",__func__,name,d06_read_fd); 546 break; 547 }
而在底层的driver中有:
291 /* Set InputDevice Name */ 292 input->name = INPUT_NAME_ACC;
这样就能匹配成功啦
29.
infotmic的LCD配置:
路径在drivers/InfotmMedia/lcd_api/source中,新增自己的c配置文件,填写手册中的配置在结构体lcdc_config_t中在drivers/InfotmMedia/lcd_api/source/lcd_cfg.h中增加新增的文件
而在drivers/InfotmMedia/external/project/ids_drv_i800/ids.c中,会对item文件进行解析,以确认到底用那个LCD配置
28.
javah生成JNI文件,其他一切都是浮云
在eclipse中编译好apk后,我的package为 com.example.javajni,class为HellojniActivity,看看生成的bin目录:├── bin
│ ├── AndroidManifest.xml
│ ├── classes
│ │ ├── com
│ │ │ └── example
│ │ │ └── javajni
│ │ │ ├── BuildConfig.class
│ │ │ ├── HellojniActivity.class
│ │ │ ├── R$attr.class
│ │ │ ├── R.class
│ │ │ ├── R$drawable.class
│ │ │ ├── R$id.class
│ │ │ ├── R$layout.class
│ │ │ ├── R$menu.class
│ │ │ ├── R$string.class
│ │ │ └── R$style.class
│ │ └── com_example_javajni_HellojniActivity.h
我们要在bin/classs目录下运行javah命令,如下javah -classpath ./ -jni com.example.javajni.HellojniActivity,这样才能根据后面的参数找到com/example/
javajni目录下的HellojniActivity.class文件
29.
kernel的makefile中链接.a库的写法:
1 obj-$(CONFIG_TOUCHSCREEN_AW5306) += aw5306_ts.o2 aw5306_ts-objs := AW5306_ts.o AW5306_userpara.o $@libAW5306.a
libAW5306.a就是该目录下的一个库
30.
allwiner的touch I2C注册过程:
I2C的注册分静态和动态,静态的i2c_register_board_info就不多说了,先创建好i2c device,后续的i2c_driver寻找匹配的device,从该device就可以找到对应的adapter;
而动态的注册是怎样的呢,driver和device都在同一个文件里面注册,而且不管注册先后,都可以找到对方,过程如下:
定义好driver:
static struct i2c_driver pcf8563_driver = { .driver = { .name = "rtc-pcf8563", }, .probe = pcf8563_probe, .remove = pcf8563_remove, .id_table = pcf8563_id,};用i2c_add_driver注册该driver;
接着创建一个device:
struct i2c_board_info info; struct i2c_adapter *adapter; memset(&info, 0, sizeof(struct i2c_board_info)); info.addr = 0x51; strlcpy(info.type, I2C_DEVICE_NAME, I2C_NAME_SIZE); adapter = i2c_get_adapter(1); if (!adapter) { printk("%s : can't get i2c adapter %d\n",__func__,1); goto err_driver; } client = i2c_new_device(adapter, &info); i2c_put_adapter(adapter); if (!client) { printk("%s : can't add i2c device at 0x%x\n",__func__, (unsigned int)info.addr); goto err_driver; } printk("%s i2c add success! \n",__func__); return 0;err_driver: return -ENODEV;
上述关键在于I2C_DEVICE_NAME名字要和i2c_driver中pcf8563_id定义的一致,还有i2c_get_adapter(1)是获得编号为1的adapter,也就是pcf8563设备挂载那条i2c总线上,adapter是板级的,和芯片有关,启动时候肯定都注册了的,所以可以获得到。
这样就可以一个文件中完成I2C代码,可以编译成ko加载。
31.
让机器永不休眠并且没有锁屏界面:
frameworks/base/packages/SettingsProvider/res/values/defaults.xml中修改def_screen_off_timeout为-1 frameworks/base/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java中mExternallyEnabled设置为false
32.ND6的以太网服务分析frameworks/base/目录下:
首先在ethernet/java/android/net/ethernet中定义了aidl文件:
EthernetDevInfo.aidl //定义Parcelable传输对象流
EthernetDevInfo.java
EthernetManager.java
EthernetMonitor.java
EthernetNative.java
EthernetStateTracker.java
IEthernetManager.aidl //定义服务接口函数interface IEthernetManager
看到IEthernetManager.aidl就知道有服务要实现这些接口函数,它在frameworks/base/services/java/com/android/server/EthernetService.java中:
public class EthernetService<syncronized> extends IEthernetManager.Stub{........}
而这个服务在services/java/com/android/server/ConnectivityService.java中注册添加:
521 ServiceManager.addService(Context.ETHERNET_SERVICE, ethService);
这样以后我们就可以用getService的方法来获得该服务了。
回来看EthernetManager.java中的函数,它定义了对外提供访问的共有函数:
95 public EthernetDevInfo getSavedConfig() { 96 try { 97 return mService.getSavedConfig(); 98 } catch (RemoteException e) { 99 Slog.i(TAG, "Can not get eth config"); 100 } 101 return null; 102 }
可以看到都是同过调用mService的方法,而mService正是我们上面的ETHERNET_SERVICE,它在那里获得呢?看文件:
core/java/android/app/ContextImpl.java
487 if (Items.ItemExist("eth.model") == 1) { 488 registerService(ETHERNET_SERVICE, new ServiceFetcher() { 489 public Object createService(ContextImpl ctx) { 490 IBinder b = ServiceManager.getService(ETHERNET_SERVICE); 491 if (b == null) 492 { 493 Log.w(TAG, "Error getting service name:" + ETHERNET_SERVICE); 494 } 495 IEthernetManager service = IEthernetManager.Stub.asInterface(b); 496 return new EthernetManager(service, ctx.mMainThread.getHandler()); 497 }}); 498 }
果然是通过ServiceManager.getService(ETHERNET_SERVICE)来获得的,而且注册了EthernetManager服务,这样我们的应用中用getSystemService方法即可获得一个
EthernetManager.java中的对象了,当然还要把EthernetManager.java中的函数等声明为公开,应用程序才能调用得到,api/current.txt中:
12765 public class EthernetManager { 12766 ctor public EthernetManager(android.net.ethernet.IEthernetManager, android.os.Handler); 12767 method public java.lang.String[] getDeviceNameList(); 12768 method public android.net.ethernet.EthernetDevInfo getSavedConfig(); 12769 method public int getState(); 12770 method public int getTotalInterface(); 12771 method public boolean isConfigured(); 12772 method public void updateDevInfo(android.net.ethernet.EthernetDevInfo); 12773 field public static final int ETHERNET_DEVICE_SCAN_RESULT_READY = 0; // 0x0 12774 field public static final java.lang.String ETHERNET_STATE_CHANGED_ACTION = "android.net.ethernet.ETHERNET_STATE_CHANGED"; 12775 field public static final int ETHERNET_STATE_DISABLED = 1; // 0x1 12776 field public static final int ETHERNET_STATE_ENABLED = 2; // 0x2 12777 field public static final int ETHERNET_STATE_UNKNOWN = 0; // 0x0 12778 field public static final java.lang.String EXTRA_ETHERNET_STATE = "ETHERNET_state"; 12779 field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; 12780 field public static final java.lang.String EXTRA_PREVIOUS_ETHERNET_STATE = "previous_ETHERNET_state"; 12781 field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.ethernet.STATE_CHANGE"; 12782 field public static final java.lang.String TAG = "EthernetManager"; 12783 }
这样,应用中getSystemService(Context.ETHERNET_SERVICE)即可获得服务
33.
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中power key的处理分析:
2994 case KeyEvent.KEYCODE_POWER: { ........... 3023 interceptPowerKeyDown(!isScreenOn || hungUp //处理按键按下去的动作 3024 || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
interceptPowerKeyDown函数如下:
605 private void interceptPowerKeyDown(boolean handled) { 606 mPowerKeyHandled = handled; 607 if (!handled) { 608 mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());//表示长按多久后弹出关机确认对话框 609 } 610 }
接着:
3028 if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {//处理按键抬起的动作 3029 result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP; 3030 }
interceptPowerKeyUp函数如下:
612 private boolean interceptPowerKeyUp(boolean canceled) { 613 if (!mPowerKeyHandled) { 614 mHandler.removeCallbacks(mPowerLongPress);//如果还没有弹出关机确认对话框,取消掉它 615 return !canceled; 616 } 617 return false; 618 }
34.
camera打开device/infotm/imapx800/etc/media_profiles.xml配置的位置:
apps/Camera/src/com/android/camera/VideoCamera.java中mMediaRecorder.setProfile(mProfile); //mMediaRecorder为打开录像的录音功能其中mProfile为 mProfile = CamcorderProfile.get(mCameraId, quality);它是通过调用JNI -> HAL层来解析media_profiles.xml文件的
35.
infotmic factroy reset流程:
1. packages/apps/Settings/src/com/android/settings/MasterClearConfirm.java------>getActivity().sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
2. frameworks/base/core/res/AndroidManifest.xml------>
1748 <receiverandroid:name="com.android.server.MasterClearReceiver"//这里的名字即为activity的路径 1749 android:permission="android.permission.MASTER_CLEAR" 1750 android:priority="100" >
3. frameworks/base/services/java/com/android/server/MasterClearReceiver.java------->
RecoverySystem.rebootWipeUserData(context);
4. frameworks/base/core/java/android/os/RecoverySystem.java------->
bootCommand(context, "--wipe_data");
378 public static void bootCommand(Context context, String arg) throws IOException { 379 RECOVERY_DIR.mkdirs(); // In case we need it 380 COMMAND_FILE.delete(); // In case it's not writable 381 LOG_FILE.delete(); 382 383 FileWriter command = new FileWriter(COMMAND_FILE); 384 try { 385 command.write(arg); 386 command.write("\n"); 387 } finally { 388 command.close(); 389 } 390 391 // Having written the command file, go ahead and reboot 392 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 393 pm.reboot("recovery"); 394 395 throw new IOException("Reboot failed (no permissions?)"); 396 }
其中383行COMMAND_FILE定义:
71 private static File RECOVERY_DIR = new File("/cache/recovery"); 72 private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
385行将字符串--wipe_data写到/chache/recovery分区,接着看393行。
5. frameworks/base/services/java/com/android/server/PowerManagerService.java------->
ShutdownThread.reboot(mContext, finalReason, false);
6. frameworks/base/services/java/com/android/server/pm/ShutdownThread.java------->
reboot() -> shutdownInner() -> beginShutdownSequence() -> sInstance.start() -> run() -> rebootOrShutdown() ->
PowerManagerService.lowLevelShutdown() -> 到PowerManagerService.java中 -> nativeShutdown()
7. JNI frameworks/base/services/jni/com_android_server_PowerManagerService.cpp ------>
android_reboot(ANDROID_RB_POWEROFF, 0, 0);
8. system/core/libcutils/android_reboot.c //adb等命令也是通过该函数来reboot的
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART2, arg);//系统调用,进入kernel
9. kernel/kernel/sys.c -------->
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); imap_reset(!strncmp(buffer, "recover", 7));
10. arch/arm/mach-imapx800/cpu.c ------->
void imap_reset(int type) { imapfb_shutdown(); writel(type, IO_ADDRESS(SYSMGR_RTC_BASE + 0x3c)); printk(KERN_EMERG "sysreboot: %s\n", (type == 2)? "charger": ((type == 1)? "recovery": "normal")); //这里肯定是recovery了 writel(0x1, IO_ADDRESS(SYSMGR_RTC_BASE + 0x2c)); writel(0x1, IO_ADDRESS(SYSMGR_RTC_BASE + 0x44)); imap_set_retry_param_default(); writel(0x3, IO_ADDRESS(SYSMGR_RTC_BASE)); while(1); }
如果是全志平台4.0.3代码,从第6点rebootOrShutdown函数开始有点不一样,他是调用Power.shutdown(),所以跑到了Power.java中,在这个文件中调用reboot->rebootNative
这样到了JNI的方法android_os_Power.cpp,全志在这个文件里做了一些自己的改动,后面就和盈方微平台的一样了,这可能也是andorid4.1.2和4.0.3的改动吧
而且在kernel中也有点不同,A10的路线如下:
kernel/sys.c中:
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); break;kernel_restart:
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);
kernel_restart_prepare函数将cmd命令(比如 adb reboot recovery,则cmd = "recovery")写入MISC分区,关闭外设,同步文件等操作:
void kernel_restart_prepare(char *cmd){ blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); system_state = SYSTEM_RESTART; usermodehelper_disable(); device_shutdown(); syscore_shutdown();}
由于在arch/arm/mach-sun4i/reboot.c中注册了reboot监听事件:
static struct notifier_block sun4i_reboot_notifier = { .notifier_call = sun4i_reboot,};static int sun4i_reboot_init(void){ return register_reboot_notifier(&sun4i_reboot_notifier);}
所以blocking_notifier_call_chain调用后发出通知,sun4i_reboot函数就会被调用,他将recovery字符串写入MISC分区。
然后machine_restart调用的是arch/arm/kernel/process.c中:
void machine_restart(char *cmd) { machine_shutdown(); arm_pm_restart(reboot_mode, cmd);}
void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart;EXPORT_SYMBOL_GPL(arm_pm_restart);
最后调用arch/arm/mach-sun4i/include/mach/system.h平台的arch_reset函数:
static inline void arch_reset(char mode, const char *cmd) { /* use watch-dog to reset system */ #define WATCH_DOG_CTRL_REG (SW_VA_TIMERC_IO_BASE + 0x0094) *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 0; __delay(100000); *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 3; while(1);}
36.
infotmic 4.1.2代码Wifi流程:
首先从系统设置中,打开WIFI界面时候,调用了WifiSettings.java的onActivityCreated方法,做一些初始化设置后调用:mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);
packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java:
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
华东WIFI的switch按钮后,调用onCheckedChanged函数:
mWifiManager.setWifiEnabled(isChecked);
通过AIDL,调用frameworks/base/wifi/java/android/net/wifi/WifiManager.java的setWifiEnabled:
mService.setWifiEnabled(enabled);
最终调用的是frameworks/base/services/java/com/android/server/WifiService.java的setWifiEnabled:
mWifiStateMachine.setWifiEnabled(enable);
mWifiStateMachine类在frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java:
743 public void setWifiEnabled(boolean enable) { 744 mLastEnableUid.set(Binder.getCallingUid()); 745 if (enable) { 746 /* Argument is the state that is entered prior to load */ 747 sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0)); 748 sendMessage(CMD_START_SUPPLICANT); 749 } else { 750 sendMessage(CMD_STOP_SUPPLICANT); 751 /* Argument is the state that is entered upon success */ 752 sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0)); 753 } 754 }
这是一个WIFI状态机的管理类,管理着WIFI的各种状态切换,每个状态都是一个类,类中有enter和
processMessage两个函数用来处理该状态的事件,先看他的构造函数中有:
setInitialState(mInitialState);
它表示第一次进入该类时候的初始状态,这里我们第一次进入,所以会调用该类中的enter:
1994 class InitialState extends State{ 1995 @Override 1996 //TODO: could move logging into a common class 1997 public void enter() { 1998 if (DBG) log(getName() + "\n"); 1999 // [31-8] Reserved for future use 2000 // [7 - 0] HSM state change 2001 // 50021 wifi_state_changed (custom|1|5) 2002 EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); 2003 2004 if (mWifiNative.isDriverLoaded()) { 2005 transitionTo(mDriverLoadedState); 2006 } 2007 else { 2008 transitionTo(mDriverUnloadedState); 2009 } ................ }
这里进入2008行,状态切换到了mDriverUnloadedState类中,所以进入该类看看:
enter函数没做什么,只是打印LOG,processMessage中状态切换到mDriverLoadingState:
在enter中看看:
2044 new Thread(new Runnable() { 2045 public void run() { 2046 mWakeLock.acquire(); 2047 //enabling state 2048 switch(message.arg1) { 2049 case WIFI_STATE_ENABLING: 2050 setWifiState(WIFI_STATE_ENABLING); 2051 break; 2052 case WIFI_AP_STATE_ENABLING: 2053 setWifiApState(WIFI_AP_STATE_ENABLING); 2054 break; 2055 } 2056 2057 if(mWifiNative.loadDriver()) { 2058 if (DBG) log("Driver load successful"); 2059 sendMessage(CMD_LOAD_DRIVER_SUCCESS); 2060 } else {
这里switch中会获得message.arg1为前面setWifiEnabled设置的WIFI_STATE_ENABLING,setWifiState函数会广播出去,应用中会收到该广播,暂时不管应用怎么处理该广播,
然后2057行加载驱动:
进入JNI层core/jni/android_net_wifi_Wifi.cpp:
return (jboolean)(::wifi_load_driver() == 0);
进入hardware/libhardware_legacy/wifi/wifi.c中,这里就是加载wifi的ko模块的地方了
回到2057行并且成功后发送CMD_LOAD_DRIVER_SUCCESS消息,这样进入processMessage函数,看看CMD_LOAD_DRIVER_SUCCESS分支:
case CMD_LOAD_DRIVER_SUCCESS: transitionTo(mDriverLoadedState);所以进入mDriverLoadedState类中,enter没做什么事情,看看processMessage函数,发现没有CMD_LOAD_DRIVER_SUCCESS分支,所以状态就停留在mDriverLoadedState中。
然后回到setWifiEnabled函数,还有一句sendMessage(CMD_START_SUPPLICANT),所以进入前面的停留状态mDriverLoadedState中:
2123 case CMD_START_SUPPLICANT: 2124 try { 2125 mNwService.wifiFirmwareReload(mInterfaceName, "STA"); 2126 } catch (Exception e) { 2127 loge("Failed to reload STA firmware " + e); 2128 // continue 2129 } 2130 try { 2131 //A runtime crash can leave the interface up and 2132 //this affects connectivity when supplicant starts up. 2133 //Ensure interface is down before a supplicant start. 2134 mNwService.setInterfaceDown(mInterfaceName); 2135 //Set privacy extensions 2136 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); 2137 } catch (RemoteException re) { 2138 loge("Unable to change interface settings: " + re); 2139 } catch (IllegalStateException ie) { 2140 loge("Unable to change interface settings: " + ie); 2141 } 2142 2143 if(mWifiNative.startSupplicant(mP2pSupported)) { 2144 if (DBG) log("Supplicant start successful"); 2145 mWifiMonitor.startMonitoring(); 2146 transitionTo(mSupplicantStartingState); 2147 } else { 2148 loge("Failed to start supplicant!"); 2149 sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0)); 2150 } 2151 break;
看看2143行,流程和前面加载ko差不多,最后调用wifi.c中wifi_start_supplicant:
property_set("ctl.start", supplicant_name);
这里supplicant_name为wpa_supplicant,属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务,所有服务必须在init.rc中定义,所以这里启动了init.rc中的:
service wpa_supplicant /system/bin/wpa_supplicant -Dwext -iwlan0 -c /data/misc/wifi/wpa_supplicant.conf
2145行启动监听,在frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java中:
350 public void startMonitoring() { 351 new MonitorThread().start(); 352 } 353 354 class MonitorThread extends Thread { 355 public MonitorThread() { 356 super("WifiMonitor"); 357 } ........................... 359 public void run() { 360 361 if (connectToSupplicant()) { 362 // Send a message indicating that it is now possible to send commands 363 // to the supplicant 364 mStateMachine.sendMessage(SUP_CONNECTION_EVENT); 365 } else { 366 mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT); 367 return; 368 } 369 370 //noinspection InfiniteLoopStatement 371 for (;;) { 372 String eventStr = mWifiNative.waitForEvent();
可以看到,是开启了一个线程,用来监听底层的wpa_supplicant事件通知,这里启动成功的话执行364行,状态变为SUP_CONNECTION_EVENT
回到前面的mDriverLoadedState状态2146行,来到mSupplicantStartingState中:
37 .
allwinner音频控制流程:
hal层的so库文件在device/softwinner/common/hardware/audio中编译生成,该路径下的audio_hw.c对上主要实现了android hal层so库的标准接口供audiofliger调用,对下主要通过调用android标准的tinymix接口来控制底层驱动,从而实现音量控制,音频通路的切换等,tinymix驱动路径在external/tinyalsa中,它会编译生成tinyalsa可执行文件和
libtinyalsa.so库文件,其中可执行文件可以用来在终端命令行直接控制底层音频(命令格式和方法看这篇笔记的第12条),而so库供提供库函数和audio_hw.c一起编译,从而实现通过audio_hw.c调用。
38.
原子位操作
为了实现位操作,内核提供了一组可原子地修改和测试单个位的函数。
原子位操作非常快,只要底层硬件允许,这种操作就可以使用单个机器指令来执行,并且不需要禁止中断。这些函数依赖于具体的架构,因此在<asm/bitops.h>中声明。即使是在SMP计算机上,这些函数也可确保为原子的,因此,能提供跨处理器的一致性。
这些函数使用的数据类型也是依赖于具体架构的。nr参数(用来描述要操作的位)通常被定义为int,但在少数架构上被定义为unsigned long。要修改的地址通常是指向unsigned long指针,但在某些架构上却使用void *来代替。
可用的位操作如下:
复制代码
void set_bit(nr, void *addr); /*设置第 nr 位在 addr 指向的数据项中。*/
void clear_bit(nr, void *addr); /*清除指定位在 addr 处的无符号长型数据.*/
void change_bit(nr, void *addr);/*翻转nr位.*/
test_bit(nr, void *addr); /*这个函数是唯一一个不需要是原子的位操作; 它简单地返回这个位的当前值.*/
/*以下原子操作如同前面列出的, 除了它们还返回这个位以前的值.*/
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
39. android 4.4.2安全模式分析:
在services/java/com/android/server/wm/WindowManagerService.java:
public boolean detectSafeMode() { if (!mInputMonitor.waitForInputDevicesReady( INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) { Slog.w(TAG, "Devices still not ready after waiting " + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS + " milliseconds before attempting to detect safe mode."); } int menuState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_MENU); int sState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_S); int dpadState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, KeyEvent.KEYCODE_DPAD_CENTER); int trackballState = mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL, InputManagerService.BTN_MOUSE); int volumeDownState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_VOLUME_DOWN); mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0 || volumeDownState > 0; try { if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0) { mSafeMode = true; SystemProperties.set(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, ""); } } catch (IllegalArgumentException e) { } if (mSafeMode) { Log.i(TAG, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState + " dpad=" + dpadState + " trackball=" + trackballState + ")"); } else { Log.i(TAG, "SAFE MODE not enabled"); } mPolicy.setSafeMode(mSafeMode); return mSafeMode; }可以看到,启动时候只要按着menu键、enter键、鼠标或者Volume down键都可以进入安全模式。
该函数是services/java/com/android/server/SystemServer.java调用的:
// Before things start rolling, be sure we have decided whether // we are in safe mode. final boolean safeMode = wm.detectSafeMode(); if (safeMode) { ActivityManagerService.self().enterSafeMode(); // Post the safe mode state in the Zygote class Zygote.systemInSafeMode = true; // Disable the JIT for the system_server process VMRuntime.getRuntime().disableJitCompilation(); } else { // Enable the JIT for the system_server process VMRuntime.getRuntime().startJitCompilation(); }所以要禁用安全模式,把safeMode设为false即可
更多相关文章
- C语言函数的递归(上)
- Android(安卓)8.0 添加一个可以让phone进程访问的hal service需
- Android(安卓)修改默认按键不震动
- [Android(安卓)Studio]添加选项菜单
- 解决 Linux/Ubuntu: adb shell insufficient permissions for dev
- Android系统的开机画面显示过程分析(7)
- Android(安卓)handler src track
- Android中FTP上传、下载(含进度)
- mac下eclipse 搭建 maven开发环境