理论:

一个Android产品所有的按键分为三大类:
  1. global key
     通用按键,例如AKEYCODE_TV,这些key我们可以修改frameworks\base\core\res\res\xml\Global_keys.xml自己指定。
  2. system key
     系统按键,例如AKEYCODE_HOME(home键) AKEYCODE_VOLUME_DOWN(音量键)。
  3. user key
     用户按键,例如AKEYCODE_A AKEYCODE_B AKEYCODE_1等。

其中system key和user key都是有特殊用途的,所以我们要想实现一键启动的需要必须使用global key,物理硬件上需要有一个额外的没有其他功能的按键来作为启动键。

思路:

硬件: 一个没有其他功能的按键来作为启动键(global key)。


Linux: 写一个按键驱动,捕获按键事件,并上报,假设上报KEY_TV。


Android:获取Linux驱动上报的按键事件,截获AKEYCODE_TV(Linux上报KEY_TV,Android中对应AKEYCODE_TV)按键事件,然后处理(启动一个app,或者打开设置界面等)。

说明:
 之前使用过广播的形式,在framework中捕获到对应的按键事件就发送一个广播,然后写一个app,静态注册一个广播接收器,当接收到framework发送的广播说明有用户按下按键,这时在广播接收器中启动对应的app。但是Android5.0之后,Google对发送广播做出了一些处理,当注册广播接收器的app被后台杀死后就无法再接收广播,也就无法再通过这个广播接收器启动其他app了。为了解决这个问题,我摒弃了发送广播的方法,采用修改Android系统源码,在framework层截取输入事件,直接启动其他APP。

关于按键事件:
Linux驱动上报的按键事件称之为EV_KEY,或者scancode 例如KEY_1 KEY_A等。
/system/usr/keylayout/Generic.kl(keylayout文件,Linux层的scancode到Android层的AKEYCODE的对应关系)。

表示按键      对应Linux层(scancode KEY_2)   对应Android层(AKEYCODE_1   AKEYCODE_TV)key          2                              1key          377                            TV

如果你想自定义global key,则需要按照上面的格式修改Generic.kl添加一项即可。

Android的framework接收的按键事件称之为AKEYCODE(Android KEY CODE), 例如AKEYCODE_1 AKEYCODE_A
/system/usr/keychars/Generic.kcm(key character map文件,用来表示Android按键即AKEYCODE_A对应哪个字符,或者同时按下其他按键(shift/capslock)后,对应哪个字符也就是说,你按下某个按键后,最终在屏幕上显示某个字符是由这个kcm文件所决定的)。

key A {    label:                              'A'  //印在按键上的文字,提示作用       base:                               'a'  //如果没有其他键(shift capslock)同时按下,则显示字符'b'    shift, capslock:                    'A'  //如果有shift/capslock按键同时按下,则显示字符'B'}

通过以上分析,必须明确Linux kernel驱动上报的按键事件和Android中对应的code的对应关系,比如Linux 驱动上报key_377按键事件,那么查询Generic.kl得知,Android层接收的是AKEYCODE_TV,这一点在修改代码捕获对应的按键事件至关重要。

实现:

1、硬件上选择一个有其他功能的按键来作为启动键。
2、写一个按键驱动key.c,上报一个按键事件(比如KEY_TV,即377,上面分析过),添加到内核中,编译到uImage中。

a. 分配 设置 注册一个input_dev结构体

/* 1. 分配一个input_dev结构体 */input_bt_dev = input_allocate_device();;/* 2. 设置 *//* 2.1 能产生哪类事件   -- 能产生按键事件 */set_bit(EV_KEY, input_bt_dev->evbit);set_bit(EV_REP, input_bt_dev->evbit);/* 2.2 能产生哪些按键事件  -- 能产生377这个按键事件 */set_bit(377, input_bt_dev->keybit);   //KEY_TV   377/* 2.3 为android构造一些设备信息  -- Android的framework会解析这些信息 */input_bt_dev->name = "hanpeng";input_bt_dev->id.bustype = 1;input_bt_dev->id.vendor  = 0x1234;input_bt_dev->id.product = 0x5678;input_bt_dev->id.version = 1;/* 3. 注册 */input_register_device(input_bt_dev);

b. 实现一个中断处理函数,获取内核中的中断号,然后注册中断。

static irqreturn_t buttons_irq(int irq, void *dev_id){    /* 读取按键接的gpio的val,判断按键是按下了还是松开,关于按键按下了引脚是高电平还是低电平要看具体的原理图 */    int pinval = gpio_get_value(pindesc->pin);    /* 高电平表示按键松开了 */    if (pinval)    {        /* 上报KEY_TV按键松开事件 : 最后一个参数: 0表示上报松开, 1表示上报按下 */        printk("KEY_TV DOWN key_val = %d\n", pindesc->key_val);        input_event(input_bt_dev, EV_KEY, pindesc->key_val, 0);        input_sync(input_bt_dev);         /* 表示一次上报结束 */    }    else    {        /* 上报KEY_TV按键按下事件 */        printk("KEY_TV UP key_val = %d\n", pindesc->key_val);        input_event(input_bt_dev, EV_KEY, pindesc->key_val, 1);        input_sync(input_bt_dev);    }    return IRQ_RETVAL(IRQ_HANDLED);}
/* 注册中断 * 中断号的获取: * 老内核(3.x以前),根据芯片手册获取硬件中断号,然后查询内核源码中的irq.h找到对应的内核中的中断号,然后进行注册 * 新内核(3.x以后),硬件信息一般都在设备树中描述了,如果在设备树中描述了这个案件,那么驱动就要先去匹配设备树,然 * 后用platform_get_resource获取中断号了。 */request_irq(irq, buttons_irq, IRQ_TYPE_EDGE_BOTH, "KEY_TV", NULL);
3、分析framework中InputManagerService部分相关代码(主要是InputReaer线程和InputDispatcher线程源码),找到对global key的处理,然后修改代码,截取Linux驱动上报的KEY_TV即在Android中对应的AKEYCODE_TV按键事件。

分析过程:看我另一篇 Android输入系统源码分析
http://blog.csdn.net/hanp_linux/article/details/77915919

修改代码:

分析得知:
InputDispatcher线程对所有的按键事件要先处理,然后再分发给具体的APP,让对应的APP做出相应的处理,但是在分发前,由PhoneWindowManager.java中的interceptKeyBeforeDispatching方法先行处理。

frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindowManager.java

public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {     /* 如果解析后的按键是global key,则调用mGlobalKeyManager.handleGlobalKey这个方法进行处理 */     if (mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {            return -1;   }}

frameworks\base\policy\src\com\android\internal\policy\impl\GlobalKeyManager.java

boolean handleGlobalKey(Context context, int keyCode, KeyEvent event) {    //添加如下代码    /* 判断是否是KEY_TV按键按下了,因为是在Android中,所以判断的是AKEYCODE值 */    if (keyCode == 170) /* frameworks\native\include\android\Keycodes.h  AKEYCODE_TV = 170 */    {        /* 如果是,则在这个地方干你想干的事情,或者启动一个app,或者打开一个设置界面等 */        //打开蓝牙设置界面的intent,同样,可以改变参数打开WiFi设置等        //Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);          //打开另外一个app的intent                          Intent intent = new Intent(context, com.android.hanp.test.MainActivity.class);              /* 如果不加这一句将会报异常:Calling startActivity() from outside of an Activity context        * requires the FLAG_ACTIVITY_NEW_TASK flag.         * 原因:在Activity的context(上下文环境)之外调用startActivity()方法时需要给Intent设置一个flag创建        * 一个新的任务栈        */        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        context.startActivity(intent);        /* 返回true表示已经对这个按键事件处理了,InputDispatcher线程则根据返回的true将这个事件给丢弃,不会           再分发给应用程序了。 */        return true;    }    .....}

操作:

1、将按键驱动 key.c放入Linux的driver/char目录,然后修改Makefile,make uImage,将最终生成的uImage或者zImage从新烧写到板子上。

2、修改frameworks\base\policy\src\com\android\internal\policy\impl\GlobalKeyManager.java
  mmm frameworks\base\policy 进行编译
  最终生成:out/target/product/tiny4412/system/framework/android.policy.jar
  最后将这个android.policy.jar替换板子的/system/framework/android.policy.jar

3、 重启板子,按下对应的按键,就启动了对应的app。

大功告成!

更多相关文章

  1. Android测试-Monkey Test
  2. Android复习资料1
  3. android review--基础知识
  4. Android:SwipeRefreshLayout和ViewPager滑动冲突的原因和正确的解
  5. Android实践 -- 监听外置sdcard(TF卡)的插拔事件
  6. Android(安卓)EventBus使用
  7. Android触摸事件分发之View篇
  8. ViewGroup中的onInterceptTouchEvent和onTouchEvent调用时序
  9. Android处理按钮重复点击事件

随机推荐

  1. 帮助ADT改进DDMS中的Logcat中文乱码问题
  2. Android So简单加固
  3. Android的Binder的起源-android学习之旅(1
  4. Android 自定义阴影Shadow颜色,大小等样式
  5. Android 11 Beta 版正式发布!以及众多面向
  6. 对2016年android实习就业的一些看法
  7. Android的Parcelable用法
  8. Android中多次弹出相同Toast提示框长时间
  9. 导入的ANDROID 项目没有ANDROID的JAR包
  10. Android牟利之道--广告平台的介绍