工作笔记-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.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 off
3137                 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即可



  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值