工作笔记-code

来源:互联网 发布:算法导论13.3章答案 编辑:程序博客网 时间:2024/06/06 02:50

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.prop

    2: /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_crane





25.

 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.o                                                                              
      2 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即可