Android按键添加和处理的方案

摘要:
添加和处理Android密钥的方案版本号描述了作者的日期1.0添加和处理安卓密钥的方案SkyWang 2013/06/18要求:安卓机器上有一个Wifi物理按钮。现在需要通过单击“Wifi物理按钮”来快速打开/关闭Wifi。方案2:在Android中添加一项服务,以监听wifi密钥消息。方案3:在Android输入子系统的框架层中捕获wifi密钥并相应地处理它们。03.在内核中向Android系统报告wifi的状态。

Android按键添加和处理的方案

 版本号说明作者日期 
1.0 

Android按键添加和处理的方案

Sky Wang 2013/06/18 
    

需求:Android机器上有个Wifi物理按键,现在需求通过点击“wifi物理按键”能够快速的开启/关闭wifi。

实现方案
经过思考之后,拟出下面几种方案:
方案一,在linux kernel的驱动中捕获“wifi物理按键”。在kernel的按键驱动中截获“wifi”按键,并对其进行处理:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。
方案二,在Android中添加一个服务,监听wifi按键消息。若监听到“wifi”按键,则读取wifi的状态:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。
方案三,在Android的input输入子系统的框架层中捕获wifi按键,并进行相应处理。若捕获到“wifi”按键,则读取wifi的状态:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。

 


方案一

方案思路: 在linux kernel的驱动中捕获“wifi物理按键”。在kernel的按键驱动中截获“wifi”按键,并对其进行处理:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。

方案分析: 若采用此方案需要解决以下问题
01,在kerne的按键驱动中捕获“wifi”按键。
-- 这个问题很好实现。在kernel的按键驱动中,对按键值进行判断,若是wifi按键,则进行相应处理。
02,在kernel中读取并设置wifi的开/关状态。
-- 这个较难实现。因为wifi驱动的开/关相关的API很难获取到。一般来来说,wifi模组的驱动都是wifi厂家写好并以.ko文件加载的。若需要获取wifi的操作API,需要更厂家一起合作;让它们将接口开放,并让其它设备在kernel中可以读取到。
03,在kernel中将wifi的状态上报到Android系统中。若单单只是实现02步,只是简单的能开/关wifi了;但还需要向办法让Android系统直到wifi的开/关行为。
-- 可以实现,但是太麻烦了。

方案结论: 实现难度太大!

 

 


方案二

方案思路: 在Android中添加一个服务,监听wifi按键消息。若监听到“wifi”按键,则读取wifi的状态:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。

方案分析: 若采用此方案需要解决以下问题
01,将kernel的wifi按键上传到Android系统中。
-- 这个可以实现。首先,我们将wifi按键映射到一个sys文件节点上:按下wifi按键时,sys文件节点的值为1;未按下wifi按键时,sys文件节点的值为0。其次,通过NDK编程,读取该sys文件节点,并将读取的接口映射注册到JNI中。最后,通过JNI,将该接口对应注册到Android系统中,使应用程序能够读取该接口。
02,在Android系统中添加一个服务,不断读取wifi按键状态。
-- 这个也可以实现。由于“01”中,我们已经将wifi的按键状态通过JNI注册到Android系统中;我们这里就可以读取到。
03,读取并设置wifi的开/关状态。
-- 这个也可以实现。在Android系统中,我们可以通过WifiManager去读取/设置wifi的开/关状态。通过WifiManager设置的wifi状态,是全局的。

架构图:

Android按键添加和处理的方案第1张 

具体实现:
通过驱动,将wifi按键状态映射到文件节点。由于不同平台差异,具体的代码接口可能有所差异;我所工作的平台是RK3066,所以还是以此来进行介绍。

01 将kernel的wifi按键上传到Android系统中

在按键驱动中编辑wifi按键的驱动:主要的目的是将wifi按键映射到某个键值上,方便后面Android系统调用。因为Android系统使用的按键值和Linux内核使用的按键值不一样,Android会通过一个键值映射表,将Linux的按键值和Android的按键值映射起来。

我们的项目中,wifi按键是通过ADC值来捕获的,而不是中断。下面是“wifi按键相关信息”,代码如下:

static struct rk29_keys_button key_button[] = { 

    ...
    // 将 wifi 开关按键定义为KEY_F16,
    // 处理时,捕获KEY_F16进行处理即可。
    {   
        .desc   = "wifi",
        .code   = KEY_F16,
        .adc_value  = 4,
        .gpio = INVALID_GPIO,
        .active_low = PRESS_LEV_LOW,
    },  
    ...
};

从中,我们可以看出wifi的adc值大概是4,它所对应的按键值(即code值)是KEY_F16。
这里,KEY_F16是我们自己定义的(因为linux中没有wifi开关按键),你也可以定义为别的值。记得两点:一,这里的所定义的wifi的code,必须和Android中要处理的按键值(后面会讲到)保持一致;二,不要使用系统中已用到的值。另外,KEY_F16的值为186,可以参考“include/linux/input.h”文件去查看。


在按键驱动中,会将key_button注册到系统中。在按键驱动中,我们将下面的callback函数注册到adc总线上;adc驱动会通过工作队列,判断的读取adc值,并调用callback,从而判断是否有响应的按键按下。下面是callback函数:

static void callback(struct adc_client *client, void *client_param, int result)
{
    struct rk29_keys_drvdata *ddata = (struct rk29_keys_drvdata *)client_param;
    int i;

    if(result < EMPTY_ADVALUE)
        ddata->result = result;

    // 依次查找key_button中的按键,判断是否需要响应
    for (i = 0; i < ddata->nbuttons; i++) {
        struct rk29_button_data *bdata = &ddata->data[i];
        struct rk29_keys_button *button = bdata->button;
        if(!button->adc_value)
            continue;
        int pre_state = button->adc_state;
        if(result < button->adc_value + DRIFT_ADVALUE &&
            result > button->adc_value - DRIFT_ADVALUE) {

            button->adc_state = 1;
        } else {
            button->adc_state = 0;
        }   
        // 同步按键状态
        synKeyDone(button->code, pre_state, button->adc_state); 

        if(bdata->state != button->adc_state)
            mod_timer(&bdata->timer,
                jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
    }   
    return;
}

前面已经说过,这个callback会不断的被adc检测的工作队列调用。若检测到adc值在“某按键定义的adc值范围”内,则该按键被按下;否则,没有按下。
下面是synKeyDone()的代码:

static void synKeyDone(int keycode, int pre_status, int cur_status) 
{
    if (cur_status == pre_status)
        return ;
          
    if (keycode==KEY_F16)
        set_wifikey(cur_status);     
}

它的作用是同步wifi按键按下状态,根据wifi按键状态,通过set_wifikey()改变对应wifi节点状态。
例如:wifi键按下时,sys/devices/platform/misc_ctl/wifikey_onoff为1; wifi未按下时,sys/devices/platform/misc_ctl/wifikey_onoff为0。

 

set_wifikey()本身以及它相关的函数如下:

// 保存按键状态的结构体
typedef struct  combo_module__t {
    unsigned char           status_wifikey;
}   combo_module_t  ;

static  combo_module_t  combo_module;


// 设置wifi状态。
// 这是对外提供的接口
void set_wifikey(int on)             
{
    printk("%s on=%d
", __func__, on);
    combo_module.status_wifikey = on;
}         
EXPORT_SYMBOL(set_wifikey);          
          
// 应用层读取wifi节点的回调函数
static  ssize_t show_wifikey_onoff      (struct device *dev, struct device_attribute *attr, char *buf)             
{
    return  sprintf(buf, "%d
", combo_module.status_wifikey);
}         
          
// 应用层设置wifi节点的回调函数
static  ssize_t set_wifikey_onoff       (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    unsigned int    val;             
    if(!(sscanf(buf, "%d
", &val))) {
        printk("%s error
", __func__); 
        return  -EINVAL; 
    }     

    if(!val) {
        combo_module.status_wifikey = 0;
    } else {
        combo_module.status_wifikey = 1;
    }
    printk("%s status_wifikey=%d
", __func__, combo_module.status_wifikey);

    return 0;
}

// 将wifi的读取/设置函数和节点对应
static  ssize_t show_wifikey_onoff  (struct device *dev, struct device_attribute *attr, char *buf);
static  ssize_t set_wifikey_onoff   (struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static  DEVICE_ATTR(wifikey_onoff, S_IRWXUGO, show_wifikey_onoff, set_wifikey_onoff);

代码说明:
(01) set_wifikey()提供的对外接口。用于在按键驱动中,当wifi按键按下/松开时调用;这样,就对应的改变wifi节点的值。
(02) DEVICE_ATTR(wifikey_onoff, S_IRWXUGO, show_wifikey_onoff, set_wifikey_onoff); 声明wifi的节点为wifikey_onoff节点,并且设置节点的权限为S_IRWXUGO,设置“应用程序读取节点时的回调函数”为show_wifikey_onoff(),设置“应用程序设置节点时的回调函数”为set_wifikey_onoff(),


DEVICE_ATTR只是声明了wifi节点,具体的注册要先将wifikey_onoff注册到attribute_group中;并且将attribute_group注册到sysfs中才能在系统中看到文件节点。下面是实现代码:

// 将wifikey_onoff注册到attribute中
static struct attribute *control_sysfs_entries[] = {
    &dev_attr_wifikey_onoff.attr,
    NULL
};

static struct attribute_group control_sysfs_attr_group = {
    .name   = NULL,
    .attrs  = control_sysfs_entries,
};


// 对应的probe函数。主要作用是将attribute_group注册到sysfs中
static int control_sysfs_probe(struct platform_device *pdev)
{
    return  sysfs_create_group(&pdev->dev.kobj, &control_sysfs_attr_group);
}

// 对应的remove函数。主要作用是将attribute_group从sysfs中注销
static  int     control_sysfs_remove        (struct platform_device *pdev)
{
    sysfs_remove_group(&pdev->dev.kobj, &control_sysfs_attr_group);

    return  0;
}

02 将Wifi读取接口注册到Android系统中
通过JNI,将wifi读取接口注册到Android系统中,下面是对应的JNI函数control_service.c的代码:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <fcntl.h>
#include <assert.h>


// 获取数组的大小
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
// 指定要注册的类,对应完整的java类名
#define JNIREG_CLASS "com/skywang/control/ControlService"

// 引入log头文件
#include <android/log.h>  

// log标签
#define  TAG    "WifiControl"
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)

#define WIFI_ONOFF_CONTROL      "/sys/devices/platform/misc_ctl/wifikey_onoff"


// 设置wifi电源开关
JNIEXPORT jint JNICALL is_wifi_key_down(JNIEnv *env, jclass clazz)
{
    int fd;
    int ret;
    char buf[2];

//    LOGD("%s 
", __func__);
    if((fd = open(WIFI_ONOFF_CONTROL, O_RDONLY)) < 0) {
        LOGE("%s : Cannot access "%s"", __func__, WIFI_ONOFF_CONTROL);
        return; // fd open fail
    }

    memset((void *)buf, 0x00, sizeof(buf));
    ssize_t count = read(fd, buf, 1);
    if (count == 1) {
        buf[count] = '

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Swing-布局管理器之FlowLayout(流式布局)-入门使用Fiddler进行APP弱网测试下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

理解杀进程的实现原理

copy from : http://gityuan.com/2016/04/16/kill-signal/ 基于Android 6.0的源码剖析, 分析kill进程的实现原理,以及讲讲系统调用(syscall)过程,涉及源码: /framework/base/core/java/android/os/Process.java /framework/bas...

Android MVP 架构一 View与Presenter

View:主要负责界面的显示及跟数据无关的逻辑,比如设置控件的点击事件等 Presenter:主要负责View与Model的交互 Model:数据部分 ------- MVP的核心是: View层不持有Model层对象任何引用,当然参数里面和临时变量里可以有Model层对象,只持有Presenter层对象引用,任何需要更新或者操作数据的,都间接通过Pres...

Android内核移植

google的android很多人都希望在gphone没有出来之前,把它移植到相关的硬件平台上去。网上看了不少文章,总的感觉是:在这一步走得最远的就是openmoko的一个大师级别的黑客Ben “Benno” Leslie,他曾经试图把目前google发布的android移植到openmoko的平台上去,并且做了10000多行代码的尝试。最终虽然由于ope...

报错kernel:NMI watchdog: BUG: soft lockup

近期在服务器跑大量高负载程序,造成cpu soft lockup。如果确认不是软件的问题。 解决办法: #追加到配置文件中 echo 30 > /proc/sys/kernel/watchdog_thresh  #查看 [root@git-node1 data]# tail -1 /proc/sys/kernel/watchdog_thresh30...

python并发编程:阻塞IO

阻塞IO(blocking  IO) 在Linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:   当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的udp包),这个时候kernel...

linux内核驱动中对文件的读写 【转】

本文转载自:http://blog.chinaunix.net/uid-13059007-id-5766941.html 有时候需要在Linux kernel--大多是在需要调试的驱动程序--中读写文件数据。在kernel中操作文件没有标准库可用,需要利用kernel的一些函数,这些函数主 要有: filp_open() filp_close(), vfs...