豪仕知识网--知识就是力量!

微信
手机版
生活常识

Android按键添加和处理的三个解决方案_安卓按键键值

作者 :东方不败 2024-01-07 20:27:27 围观 : 评论

Android按键添加和处理的三个解决方案_安卓按键键值

豪士君测试所用平台

Android按键添加和处理的三个解决方案,一起来看看吧,希望能帮助到您,更多请关注豪仕知识网。

Android按键添加和处理的方案

http://www.haoz.net●☛█▼▲◐●☛█▼▲◐◐◐◐●☛█▼▲◐豪仕知识网●☛█▼▲豪仕知识网

实现方案需求: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。

方案一

HTTP://WWW.haoz.net豪仕知识网采集不好玩哦◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐撒旦法师打发斯蒂芬

方案思路: 在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的开/关行为。-- 可以实现,但是太麻烦了。

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

HTTP://WWW.haoz.net豪仕知识网采集不好玩哦◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐撒旦法师打发斯蒂芬

方案二

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

◐◐◐◐●☛█▼▲豪仕知识网███████豪仕知识http://www.Haoz.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

方案分析: 若采用此方案需要解决以下问题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状态,是全局的。

架构图:

◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲HTTP://WWW.hAoz.net███████████████████████████东方金报网

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

◐◐◐◐●☛█▼▲豪仕知识网███████豪仕知识http://www.Haoz.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

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

◐◐◐◐●☛█▼▲豪仕知识网███████http://www.haOZ.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

在按键驱动中编辑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 result = result;    // 依次查找key_button中的按键,判断是否需要响应    for (i = 0; i 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 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。

◐◐◐◐●☛█▼▲豪仕知识网HT●☛█▼▲◐◐◐◐●☛█▼▲

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\n", __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\n", 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\n", &val))) {        printk("%s error\n", __func__);         return  -EINVAL;     }         if(!val) {        combo_module.status_wifikey = 0;    } else {        combo_module.status_wifikey = 1;    }    printk("%s status_wifikey=%d\n", __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);
HTTP://WWW.haoz.net豪仕知识网采集不好玩哦◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐撒旦法师打发斯蒂芬

代码说明:(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;}
◐◐◐◐●☛█▼▲豪仕知识网███████http://www.haOZ.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

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

#include #include #include #include #include #include // 获取数组的大小# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))// 指定要注册的类,对应完整的java类名#define JNIREG_CLASS "com/skywang/control/ControlService"// 引入log头文件#include   // 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 \n", __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] = '\0';        ret = atoi(buf);    } else {        buf[0] = '\0';    }//    LOGD("%s buf=%s, ret=%d\n", __func__, buf, ret);    close(fd);        return ret;}// 清除wifi的按下状态JNIEXPORT void JNICALL clr_wifi_key_status(JNIEnv *env, jclass clazz){    int fd;    int nwr;    char buf[2];    if((fd = open(WIFI_ONOFF_CONTROL, O_RDWR)) FindClass(env, className);    if (clazz == NULL) {        return JNI_FALSE;    }    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {        return result;    }       register_wifi_control(env);    // 返回jni的版本    return JNI_VERSION_1_4;}

代码说明:(01) Android 的JVM会回调JNI_OnLoad()函数。在JNI_OnLoad()中,调用register_wifi_control(env)。(02) register_wifi_control(env)调用 registerNativeMethods(env, JNIREG_CLASS, method_table, NELEM(method_table)) 将method_table表格中的函数注册到Android的JNIREG_CLASS类中。JNIREG_CLASS为com/skywang/control/ControlService,所以它对应的类是com.skywang.control.ControlService.java。(03) method_table的内容如下:

JNINativeMethod method_table[] = {    // wifi按键相关函数    { "is_wifi_key_down", "()I", (void*)is_wifi_key_down },    { "clr_wifi_key_status", "()V", (void*)clr_wifi_key_status },}

这意味着,将该文件中的is_wifi_key_down()函数和JNIREG_CLASS类的is_wifi_key_down()绑定。将该文件中的clr_wifi_key_status()函数和JNIREG_CLASS类的clr_wifi_key_status()绑定。

●☛█▼▲豪仕知识网◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲

该文件对应的Android.mk的代码如下:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := control_serviceLOCAL_SRC_FILES := control_service.c# 添加对log库的支持LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog#  注:若生成static的.a,只需添加 LOCAL_LDLIBS:=-llog include $(BUILD_SHARED_LIBRARY)LOCAL_PATH := $(call my-dir)

用ndk-build编译上面两个文件,得到so库文件libcontrol_service.so。

关于Android NDK编程更详细的内容,请参考“Android JNI和NDK学习

豪仕知识网http://www.haoz.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

03 Android读取wifi的开关/状态在Android创建一个com.skywang.control.ControlService.java。例如,在Launcher的目录下创建packages/apps/Launcher2/src/com/skywang/control/ControlService.javaControlService.java代码如下:

package com.skywang.control;import android.os.IBinder;import android.app.Service;import android.content.Intent;import android.content.Context;import android.net.wifi.WifiManager;import android.util.Log;public class ControlService extends Service {    private static final String TAG = "ControlService";    private WifiManager mWM;    private ReadThread mReadThread;    private boolean bWifi;    @Override    public void onCreate() {        super.onCreate();        Log.e(TAG, "start ControlService");        mWM = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);        mReadThread = new ReadThread();        mReadThread.start();        bWifi = mWM.isWifiEnabled();    }    @Override    public void onDestroy() {        super.onDestroy();        if (mReadThread != null)            mReadThread.interrupt();    }    @Override    public IBinder onBind(Intent intent) {        return null;    }    // 处理wifi按键    private synchronized void handleWifiKey() {        if (is_wifi_key_down()==1) {            // 清空wifi的按下状态。目的是“防止不断的产生wifi按下事件”            clr_wifi_key_status();            Log.d(TAG, "wifi key down");            if (!mWM.isWifiEnabled()) {                Log.e(TAG, "open wifi");                mWM.setWifiEnabled(true);            } else {                Log.e(TAG, "close wifi");                mWM.setWifiEnabled(false);            }        }    }    // 和Activity界面通信的接口    private class ReadThread extends Thread {        @Override        public void  run() {            super.run();            while (!isInterrupted()) {                handleWifiKey();            }        }    }    // wifi按键相关函数    private native int is_wifi_key_down();    private native void clr_wifi_key_status();    static {        // 加载本地.so库文件        System.loadLibrary("control_service");    }}

代码说明:(01) System.loadLibrary("control_service"); 这是在ControlService启动的时候自动执行的,目的是加载libcontrol_service.so库。即上一步所生成的so库文件。(02) ControlService.java是服务程序,它继承于Service。ReadThread是启动时会自动开启的线程。ReadThread的作用就是不断的调用handleWifiKey()处理wifi按键值。

接下来,我们在AndroidManifest.xml中声明该服务,就可以在其它地方调用执行了。下面是manifest中声明ControlService的代码:

                
●☛█▼▲豪仕知识网◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲

我们在Launcher.java的onCreate()函数中启动该服务。这样,随着系统系统服务就会一直运行了。启动服务的代码如下:

startService(new Intent("com.skywang.control.CONTROLSERVICE"));

方案结论: 工作正常,但消耗系统资源较多,会增加系统功耗!经过测试发现,此方案运行很正常。但存在一个问题:由于添加了一个不停运行的服务,消耗很多系统资源,导致机器的功能也增加了很多。因此,再实现方案三,对比看看效果如何。

◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲豪仕知识网HtTp://▲▼▲▼▲

方案三

◐◐◐◐●☛█▼▲豪仕知识网███████http://www.haOZ.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

方案思路: 在Android的input输入子系统的框架层中捕获wifi按键,并进行相应处理。若捕获到“wifi”按键,则读取wifi的状态:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。

豪仕知识网http://www.haoz.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

方案分析: 若采用此方案需要解决以下问题01, 将kernel的wifi按键值映射到Android系统的某键值上。-- 这个可以实现。和“方案二”一样,我们通过ADC驱动将wifi按键映射到键值KEY_F16上;然后,将kernel的KEY_F16和Android的某一键值对应。02, 在Android的framework层的键值处理函数中,捕获按键,并进行相应处理。-- 这个可以实现。在input子系统的framework层,捕获到wifi按键对应的Android系统中的按键

●☛█▼▲豪仕知识网◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲

架构图:

具体实现:01, 将kernel的wifi按键值映射到Android系统的某键值上。01.01, wifi按键驱动

http://www.haoz.net●☛█▼▲◐●☛█▼▲◐◐◐◐●☛█▼▲◐豪仕知识网●☛█▼▲豪仕知识网

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

◐◐◐◐●☛█▼▲豪仕知识网███████http://www.haOZ.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

我们的项目中,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,    },      ...};
◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲HtTp://wWW.haoz.net豪仕知识网●●●●●●●●●●●●●●●●●●●●●●●●●●

从中,我们可以看出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 result = result;    // 依次查找key_button中的按键,判断是否需要响应    for (i = 0; i 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 adc_value + DRIFT_ADVALUE &&            result > button->adc_value - DRIFT_ADVALUE) {            button->adc_state = 1;        } else {            button->adc_state = 0;        }           if(bdata->state != button->adc_state)            mod_timer(&bdata->timer,                jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));    }       return;}

这里的callback和“方案二”中的callback有区别。这里没有调用synKeyDone()函数。

◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲HtTp://wWW.haoz.net豪仕知识网●●●●●●●●●●●●●●●●●●●●●●●●●●

01.02, 键值映射映射文件:Linux中的按键值和Android中的按键值不一样。它们是通过.kl映射文件,建立对应关系的。默认的映射文件是 qwerty.kl;但不同的平台可能有效的映射文件不同。用户可以通过查看"/system/usr/keylayout/"目录下的.kl映射文件,来进行验证哪个是有效的。映射方法:一,可以通过查看调用.kl的代码。二,修改.kl文件来验证。在rk3066中,有效的映射文件是“rk29-keypad.kl”。在“rk29-keypad.kl”中添加以下代码将wifi按键和Android中的“AVR_POWER按键”对应。key 186 AVR_POWER

说明:key-- 是关键字。固定值,不需要改变。186-- wifi按键在linux驱动中对应的键值,这里对应KEY_F16的键值,即wifi对应的按键值。关于linux驱动中的各个键值,可以查看“include/linux/input.h”AVR_POWER-- wifi按键映射到Android中的按键,它对应是“KeycodeLabels.h”文件中的KEYCODES表格元素的“literal”值。

KeycodeLabels.h中KEYCODES定义如下:

static const KeycodeLabel KEYCODES[] = {    { "SOFT_LEFT", 1 },    { "SOFT_RIGHT", 2 },    { "HOME", 3 },    { "BACK", 4 },    { "CALL", 5 },    { "ENDCALL", 6 },    { "0", 7 },    { "1", 8 },    { "2", 9 },    { "3", 10 },    { "4", 11 },    { "5", 12 },    { "6", 13 },    { "7", 14 },    { "8", 15 },    { "9", 16 },    { "STAR", 17 },    { "POUND", 18 },    { "DPAD_UP", 19 },    { "DPAD_DOWN", 20 },    { "DPAD_LEFT", 21 },    { "DPAD_RIGHT", 22 },    { "DPAD_CENTER", 23 },    { "VOLUME_UP", 24 },    { "VOLUME_DOWN", 25 },    { "POWER", 26 },    { "CAMERA", 27 },    { "CLEAR", 28 },    { "A", 29 },    { "B", 30 },    { "C", 31 },    { "D", 32 },    { "E", 33 },    { "F", 34 },    { "G", 35 },    { "H", 36 },    { "I", 37 },    { "J", 38 },    { "K", 39 },    { "L", 40 },    { "M", 41 },    { "N", 42 },    { "O", 43 },    { "P", 44 },    { "Q", 45 },    { "R", 46 },    { "S", 47 },    { "T", 48 },    { "U", 49 },    { "V", 50 },    { "W", 51 },    { "X", 52 },    { "Y", 53 },    { "Z", 54 },    { "COMMA", 55 },    { "PERIOD", 56 },    { "ALT_LEFT", 57 },    { "ALT_RIGHT", 58 },    { "SHIFT_LEFT", 59 },    { "SHIFT_RIGHT", 60 },    { "TAB", 61 },    { "SPACE", 62 },    { "SYM", 63 },    { "EXPLORER", 64 },    { "ENVELOPE", 65 },    { "ENTER", 66 },    { "DEL", 67 },    { "GRAVE", 68 },    { "MINUS", 69 },    { "EQUALS", 70 },    { "LEFT_BRACKET", 71 },    { "RIGHT_BRACKET", 72 },    { "BACKSLASH", 73 },    { "SEMICOLON", 74 },    { "APOSTROPHE", 75 },    { "SLASH", 76 },    { "AT", 77 },    { "NUM", 78 },    { "HEADSETHOOK", 79 },    { "FOCUS", 80 },    { "PLUS", 81 },    { "MENU", 82 },    { "NOTIFICATION", 83 },    { "SEARCH", 84 },    { "MEDIA_PLAY_PAUSE", 85 },    { "MEDIA_STOP", 86 },    { "MEDIA_NEXT", 87 },    { "MEDIA_PREVIOUS", 88 },    { "MEDIA_REWIND", 89 },    { "MEDIA_FAST_FORWARD", 90 },    { "MUTE", 91 },    { "PAGE_UP", 92 },    { "PAGE_DOWN", 93 },    { "PICTSYMBOLS", 94 },    { "SWITCH_CHARSET", 95 },    { "BUTTON_A", 96 },    { "BUTTON_B", 97 },    { "BUTTON_C", 98 },    { "BUTTON_X", 99 },    { "BUTTON_Y", 100 },    { "BUTTON_Z", 101 },    { "BUTTON_L1", 102 },    { "BUTTON_R1", 103 },    { "BUTTON_L2", 104 },    { "BUTTON_R2", 105 },    { "BUTTON_THUMBL", 106 },    { "BUTTON_THUMBR", 107 },    { "BUTTON_START", 108 },    { "BUTTON_SELECT", 109 },    { "BUTTON_MODE", 110 },    { "ESCAPE", 111 },    { "FORWARD_DEL", 112 },    { "CTRL_LEFT", 113 },    { "CTRL_RIGHT", 114 },    { "CAPS_LOCK", 115 },    { "SCROLL_LOCK", 116 },    { "META_LEFT", 117 },    { "META_RIGHT", 118 },    { "FUNCTION", 119 },    { "SYSRQ", 120 },    { "BREAK", 121 },    { "MOVE_HOME", 122 },    { "MOVE_END", 123 },    { "INSERT", 124 },    { "FORWARD", 125 },    { "MEDIA_PLAY", 126 },    { "MEDIA_PAUSE", 127 },    { "MEDIA_CLOSE", 128 },    { "MEDIA_EJECT", 129 },    { "MEDIA_RECORD", 130 },    { "F1", 131 },    { "F2", 132 },    { "F3", 133 },    { "F4", 134 },    { "F5", 135 },    { "F6", 136 },    { "F7", 137 },    { "F8", 138 },    { "F9", 139 },    { "F10", 140 },    { "F11", 141 },    { "F12", 142 },    { "NUM_LOCK", 143 },    { "NUMPAD_0", 144 },    { "NUMPAD_1", 145 },    { "NUMPAD_2", 146 },    { "NUMPAD_3", 147 },    { "NUMPAD_4", 148 },    { "NUMPAD_5", 149 },    { "NUMPAD_6", 150 },    { "NUMPAD_7", 151 },    { "NUMPAD_8", 152 },    { "NUMPAD_9", 153 },    { "NUMPAD_DIVIDE", 154 },    { "NUMPAD_MULTIPLY", 155 },    { "NUMPAD_SUBTRACT", 156 },    { "NUMPAD_ADD", 157 },    { "NUMPAD_DOT", 158 },    { "NUMPAD_COMMA", 159 },    { "NUMPAD_ENTER", 160 },    { "NUMPAD_EQUALS", 161 },    { "NUMPAD_LEFT_PAREN", 162 },    { "NUMPAD_RIGHT_PAREN", 163 },    { "VOLUME_MUTE", 164 },    { "INFO", 165 },    { "CHANNEL_UP", 166 },    { "CHANNEL_DOWN", 167 },    { "ZOOM_IN", 168 },    { "ZOOM_OUT", 169 },    { "TV", 170 },    { "WINDOW", 171 },    { "GUIDE", 172 },    { "DVR", 173 },    { "BOOKMARK", 174 },    { "CAPTIONS", 175 },    { "SETTINGS", 176 },    { "TV_POWER", 177 },    { "TV_INPUT", 178 },    { "STB_POWER", 179 },    { "STB_INPUT", 180 },    { "AVR_POWER", 181 },    { "AVR_INPUT", 182 },    { "PROG_RED", 183 },    { "PROG_GREEN", 184 },    { "PROG_YELLOW", 185 },    { "PROG_BLUE", 186 },    { "APP_SWITCH", 187 },    { "BUTTON_1", 188 },    { "BUTTON_2", 189 },    { "BUTTON_3", 190 },    { "BUTTON_4", 191 },    { "BUTTON_5", 192 },    { "BUTTON_6", 193 },    { "BUTTON_7", 194 },    { "BUTTON_8", 195 },    { "BUTTON_9", 196 },    { "BUTTON_10", 197 },    { "BUTTON_11", 198 },    { "BUTTON_12", 199 },    { "BUTTON_13", 200 },    { "BUTTON_14", 201 },    { "BUTTON_15", 202 },    { "BUTTON_16", 203 },    { "LANGUAGE_SWITCH", 204 },    { "MANNER_MODE", 205 },    { "3D_MODE", 206 },    { "CONTACTS", 207 },    { "CALENDAR", 208 },    { "MUSIC", 209 },    { "CALCULATOR", 210 },    { "ZENKAKU_HANKAKU", 211 },    { "EISU", 212 },    { "MUHENKAN", 213 },    { "HENKAN", 214 },    { "KATAKANA_HIRAGANA", 215 },    { "YEN", 216 },    { "RO", 217 },    { "KANA", 218 },    { "ASSIST", 219 },    // NOTE: If you add a new keycode here you must also add it to several other files.    //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.    { NULL, 0 }};
◐◐◐◐●☛█▼▲豪仕知识网███████豪仕知识网HTtp://www.haoZ.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

KeycodeLabels.h中的按键与Android框架层的KeyEvent.java中的按键值对应。例如:“AVR_POWER”对应“KeyEvent.java中的键值”如下:public static final int KEYCODE_AVR_POWER = 181;

这样,我们发现:将驱动的wifi按键就和Android系统中的KEYCODE_AVR_POWER就对应起来了!

02, 在Android的framework层的键值处理函数中,捕获按键,并进行相应处理。在framework层的input系统中,加入对wifi按键的捕获。添加的文件是:frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java添加的具体方法:在PhoneWindowManager.java的interceptKeyBeforeQueueing()函数中,捕获wifi按键。代码如下:

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;    final boolean canceled = event.isCanceled();    final int keyCode = event.getKeyCode();    final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;    final boolean keyguardActive = (mKeyguardMediator == null ? false :                        (isScreenOn ?                            mKeyguardMediator.isShowingAndNotHidden() :                            mKeyguardMediator.isShowing()));    if (!mSystemBooted) {        return 0;    }    if (DEBUG_INPUT) {        Log.d(TAG, "interceptKeyTq keycode=" + keyCode              + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive);    }    if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0            && event.getRepeatCount() == 0) {        performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);    }    if (keyCode == KeyEvent.KEYCODE_POWER) {        policyFlags |= WindowManagerPolicy.FLAG_WAKE;    }    final boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE            | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;    int result;    if ((isScreenOn && !mHeadless) || (isInjected && !isWakeKey)) {        // When the screen is on or if the key is injected pass the key to the application.        result = ACTION_PASS_TO_USER;    } else {        // When the screen is off and the key is not injected, determine whether        // to wake the device but don't pass the key to the application.        result = 0;        if (down && isWakeKey) {            if (keyguardActive) {                // If the keyguard is showing, let it decide what to do with the wake key.                mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode,                        mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED);            } else {                // Otherwise, wake the device ourselves.                result |= ACTION_POKE_USER_ACTIVITY;            }        }    }    // Handle special keys.    switch (keyCode) {        case KeyEvent.KEYCODE_SYSRQ: {            if (!down) {                printScreenSysRq();            }            break;        }        case KeyEvent.KEYCODE_AVR_POWER: {            Log.d("##SKYWANG##", "global keycode:"+keyCode);            if (keyCode == KeyEvent.KEYCODE_AVR_POWER && down==false) {                // Wifi按键处理                WifiManager mWM = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);                boolean bWifi = mWM.isWifiEnabled();                mWM.setWifiEnabled(!bWifi);            }             break;        }        case KeyEvent.KEYCODE_VOLUME_DOWN:        case KeyEvent.KEYCODE_VOLUME_UP:        case KeyEvent.KEYCODE_VOLUME_MUTE: {            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {                if (down) {                    if (isScreenOn && !mVolumeDownKeyTriggered                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                        mVolumeDownKeyTriggered = true;                        mVolumeDownKeyTime = event.getDownTime();                        mVolumeDownKeyConsumedByScreenshotChord = false;                        cancelPendingPowerKeyAction();                        interceptScreenshotChord();                    }                } else {                    mVolumeDownKeyTriggered = false;                    cancelPendingScreenshotChordAction();                }            } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {                if (down) {                    if (isScreenOn && !mVolumeUpKeyTriggered                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                        mVolumeUpKeyTriggered = true;                        cancelPendingPowerKeyAction();                        cancelPendingScreenshotChordAction();                    }                } else {                    mVolumeUpKeyTriggered = false;                    cancelPendingScreenshotChordAction();                }            } else if (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {                // add by skywang                if (!down)                    handleMuteKey() ;            }            if (down) {                ITelephony telephonyService = getTelephonyService();                if (telephonyService != null) {                    try {                        if (telephonyService.isRinging()) {                            // If an incoming call is ringing, either VOLUME key means                            // "silence ringer".  We handle these keys here, rather than                            // in the InCallScreen, to make sure we'll respond to them                            // even if the InCallScreen hasn't come to the foreground yet.                            // Look for the DOWN event here, to agree with the "fallback"                            // behavior in the InCallScreen.                            Log.i(TAG, "interceptKeyBeforeQueueing:"                                  + " VOLUME key-down while ringing: Silence ringer!");                            // Silence the ringer.  (It's safe to call this                            // even if the ringer has already been silenced.)                            telephonyService.silenceRinger();                            // And *don't* pass this key thru to the current activity                            // (which is probably the InCallScreen.)                            result &= ~ACTION_PASS_TO_USER;                            break;                        }                        if (telephonyService.isOffhook()                                && (result & ACTION_PASS_TO_USER) == 0) {                            // If we are in call but we decided not to pass the key to                            // the application, handle the volume change here.                            handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);                            break;                        }                    } catch (RemoteException ex) {                        Log.w(TAG, "ITelephony threw RemoteException", ex);                    }                }                if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) {                    // If music is playing but we decided not to pass the key to the                    // application, handle the volume change here.                    handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);                    break;                }            }            break;        }        case KeyEvent.KEYCODE_ENDCALL: {            result &= ~ACTION_PASS_TO_USER;            if (down) {                ITelephony telephonyService = getTelephonyService();                boolean hungUp = false;                if (telephonyService != null) {                    try {                        hungUp = telephonyService.endCall();                    } catch (RemoteException ex) {                        Log.w(TAG, "ITelephony threw RemoteException", ex);                    }                }                interceptPowerKeyDown(!isScreenOn || hungUp);            } else {                if (interceptPowerKeyUp(canceled)) {                    if ((mEndcallBehavior                            & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {                        if (goHome()) {                            break;                        }                    }                    if ((mEndcallBehavior                            & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {                        result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;                    }                }            }            break;        }        case KeyEvent.KEYCODE_POWER: {            if(mHdmiPlugged&&SystemProperties.get("ro.hdmi.power_disable","false").equals("true")){               Log.d("hdmi","power disable---------------");               result=0;               break;            }            result &= ~ACTION_PASS_TO_USER;            if (down) {                if (isScreenOn && !mPowerKeyTriggered                        && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                    mPowerKeyTriggered = true;                    mPowerKeyTime = event.getDownTime();                    interceptScreenshotChord();                }                ITelephony telephonyService = getTelephonyService();                boolean hungUp = false;                if (telephonyService != null) {                    try {                        if (telephonyService.isRinging()) {                            // Pressing Power while there's a ringing incoming                            // call should silence the ringer.                            telephonyService.silenceRinger();                        } else if ((mIncallPowerBehavior                                & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0                                && telephonyService.isOffhook()) {                            // Otherwise, if "Power button ends call" is enabled,                            // the Power button will hang up any current active call.                            hungUp = telephonyService.endCall();                        }                    } catch (RemoteException ex) {                        Log.w(TAG, "ITelephony threw RemoteException", ex);                    }                }                interceptPowerKeyDown(!isScreenOn || hungUp                        || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);            } else {                mPowerKeyTriggered = false;                cancelPendingScreenshotChordAction();                if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {                    result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;                }                mPendingPowerKeyUpCanceled = false;            }            break;        }        case KeyEvent.KEYCODE_MEDIA_PLAY:        case KeyEvent.KEYCODE_MEDIA_PAUSE:        case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:            if (down) {                ITelephony telephonyService = getTelephonyService();                if (telephonyService != null) {                    try {                        if (!telephonyService.isIdle()) {                            // Suppress PLAY/PAUSE toggle when phone is ringing or in-call                            // to avoid music playback.                            break;                        }                    } catch (RemoteException ex) {                        Log.w(TAG, "ITelephony threw RemoteException", ex);                    }                }            }        case KeyEvent.KEYCODE_HEADSETHOOK:        case KeyEvent.KEYCODE_MUTE:        case KeyEvent.KEYCODE_MEDIA_STOP:        case KeyEvent.KEYCODE_MEDIA_NEXT:        case KeyEvent.KEYCODE_MEDIA_PREVIOUS:        case KeyEvent.KEYCODE_MEDIA_REWIND:        case KeyEvent.KEYCODE_MEDIA_RECORD:        case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {            if ((result & ACTION_PASS_TO_USER) == 0) {                // Only do this if we would otherwise not pass it to the user. In that                // case, the PhoneWindow class will do the same thing, except it will                // only do it if the showing app doesn't process the key on its own.                // Note that we need to make a copy of the key event here because the                // original key event will be recycled when we return.                mBroadcastWakeLock.acquire();                Message msg = mHandler.obtainMessage(MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK,                        new KeyEvent(event));                msg.setAsynchronous(true);                msg.sendToTarget();            }            break;        }        case KeyEvent.KEYCODE_CALL: {            if (down) {                ITelephony telephonyService = getTelephonyService();                if (telephonyService != null) {                    try {                        if (telephonyService.isRinging()) {                            Log.i(TAG, "interceptKeyBeforeQueueing:"                                  + " CALL key-down while ringing: Answer the call!");                            telephonyService.answerRingingCall();                            // And *don't* pass this key thru to the current activity                            // (which is presumably the InCallScreen.)                            result &= ~ACTION_PASS_TO_USER;                        }                    } catch (RemoteException ex) {                        Log.w(TAG, "ITelephony threw RemoteException", ex);                    }                }            }            break;        }    }    return result;}
◐◐◐◐●☛█▼▲豪仕知识网HT●☛█▼▲◐◐◐◐●☛█▼▲

在上面的代码中,我们捕获了KeyEvent.KEYCODE_AVR_POWER,并对其进行处理。

方案结论: 方案可行。而且运行效率比“方案二”高,不会造成功耗很大的问题。

HTTP://WWW.haoz.net◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐豪仕知识网

最终总结:方案三最好!

Android按键添加和处理的方案

http://www.haoz.net●☛█▼▲◐●☛█▼▲◐◐◐◐●☛█▼▲◐豪仕知识网●☛█▼▲豪仕知识网

实现方案需求: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。

方案一

◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲HtTp://wWW.haoz.net豪仕知识网●●●●●●●●●●●●●●●●●●●●●●●●●●

方案思路: 在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的开/关行为。-- 可以实现,但是太麻烦了。

◐◐◐◐●☛█▼▲豪仕知识网███████豪仕知识网HTtp://www.haoZ.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

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

方案二

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

◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲HTTP://WWW.hAoz.net███████████████████████████东方金报网

方案分析: 若采用此方案需要解决以下问题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状态,是全局的。

架构图:

HTTP://WWW.haoz.net◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐豪仕知识网

具体实现:通过驱动,将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,    },      ...};
◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲HTTP://WWW.hAoz.net███████████████████████████东方金报网

从中,我们可以看出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 result = result;    // 依次查找key_button中的按键,判断是否需要响应    for (i = 0; i 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 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;}
◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲HtTp://wWW.haoz.net豪仕知识网●●●●●●●●●●●●●●●●●●●●●●●●●●

前面已经说过,这个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。

◐◐◐◐●☛█▼▲豪仕知识网HT●☛█▼▲◐◐◐◐●☛█▼▲

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\n", __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\n", 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\n", &val))) {        printk("%s error\n", __func__);         return  -EINVAL;     }         if(!val) {        combo_module.status_wifikey = 0;    } else {        combo_module.status_wifikey = 1;    }    printk("%s status_wifikey=%d\n", __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 #include #include #include #include #include // 获取数组的大小# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))// 指定要注册的类,对应完整的java类名#define JNIREG_CLASS "com/skywang/control/ControlService"// 引入log头文件#include   // 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 \n", __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] = '\0';        ret = atoi(buf);    } else {        buf[0] = '\0';    }//    LOGD("%s buf=%s, ret=%d\n", __func__, buf, ret);    close(fd);        return ret;}// 清除wifi的按下状态JNIEXPORT void JNICALL clr_wifi_key_status(JNIEnv *env, jclass clazz){    int fd;    int nwr;    char buf[2];    if((fd = open(WIFI_ONOFF_CONTROL, O_RDWR)) FindClass(env, className);    if (clazz == NULL) {        return JNI_FALSE;    }    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {        return result;    }       register_wifi_control(env);    // 返回jni的版本    return JNI_VERSION_1_4;}
●☛█▼▲豪仕知识网◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲

代码说明:(01) Android 的JVM会回调JNI_OnLoad()函数。在JNI_OnLoad()中,调用register_wifi_control(env)。(02) register_wifi_control(env)调用 registerNativeMethods(env, JNIREG_CLASS, method_table, NELEM(method_table)) 将method_table表格中的函数注册到Android的JNIREG_CLASS类中。JNIREG_CLASS为com/skywang/control/ControlService,所以它对应的类是com.skywang.control.ControlService.java。(03) method_table的内容如下:

JNINativeMethod method_table[] = {    // wifi按键相关函数    { "is_wifi_key_down", "()I", (void*)is_wifi_key_down },    { "clr_wifi_key_status", "()V", (void*)clr_wifi_key_status },}

这意味着,将该文件中的is_wifi_key_down()函数和JNIREG_CLASS类的is_wifi_key_down()绑定。将该文件中的clr_wifi_key_status()函数和JNIREG_CLASS类的clr_wifi_key_status()绑定。

该文件对应的Android.mk的代码如下:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := control_serviceLOCAL_SRC_FILES := control_service.c# 添加对log库的支持LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog#  注:若生成static的.a,只需添加 LOCAL_LDLIBS:=-llog include $(BUILD_SHARED_LIBRARY)LOCAL_PATH := $(call my-dir)
HTTP://WWW.haoz.net豪仕知识网采集不好玩哦◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐◐撒旦法师打发斯蒂芬

用ndk-build编译上面两个文件,得到so库文件libcontrol_service.so。

关于Android NDK编程更详细的内容,请参考“Android JNI和NDK学习

03 Android读取wifi的开关/状态在Android创建一个com.skywang.control.ControlService.java。例如,在Launcher的目录下创建packages/apps/Launcher2/src/com/skywang/control/ControlService.javaControlService.java代码如下:

package com.skywang.control;import android.os.IBinder;import android.app.Service;import android.content.Intent;import android.content.Context;import android.net.wifi.WifiManager;import android.util.Log;public class ControlService extends Service {    private static final String TAG = "ControlService";    private WifiManager mWM;    private ReadThread mReadThread;    private boolean bWifi;    @Override    public void onCreate() {        super.onCreate();        Log.e(TAG, "start ControlService");        mWM = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);        mReadThread = new ReadThread();        mReadThread.start();        bWifi = mWM.isWifiEnabled();    }    @Override    public void onDestroy() {        super.onDestroy();        if (mReadThread != null)            mReadThread.interrupt();    }    @Override    public IBinder onBind(Intent intent) {        return null;    }    // 处理wifi按键    private synchronized void handleWifiKey() {        if (is_wifi_key_down()==1) {            // 清空wifi的按下状态。目的是“防止不断的产生wifi按下事件”            clr_wifi_key_status();            Log.d(TAG, "wifi key down");            if (!mWM.isWifiEnabled()) {                Log.e(TAG, "open wifi");                mWM.setWifiEnabled(true);            } else {                Log.e(TAG, "close wifi");                mWM.setWifiEnabled(false);            }        }    }    // 和Activity界面通信的接口    private class ReadThread extends Thread {        @Override        public void  run() {            super.run();            while (!isInterrupted()) {                handleWifiKey();            }        }    }    // wifi按键相关函数    private native int is_wifi_key_down();    private native void clr_wifi_key_status();    static {        // 加载本地.so库文件        System.loadLibrary("control_service");    }}
豪仕知识网http://www.haoz.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

代码说明:(01) System.loadLibrary("control_service"); 这是在ControlService启动的时候自动执行的,目的是加载libcontrol_service.so库。即上一步所生成的so库文件。(02) ControlService.java是服务程序,它继承于Service。ReadThread是启动时会自动开启的线程。ReadThread的作用就是不断的调用handleWifiKey()处理wifi按键值。

◐◐◐◐●☛█▼▲豪仕知识网http://www.haOz.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

接下来,我们在AndroidManifest.xml中声明该服务,就可以在其它地方调用执行了。下面是manifest中声明ControlService的代码:

                

我们在Launcher.java的onCreate()函数中启动该服务。这样,随着系统系统服务就会一直运行了。启动服务的代码如下:

startService(new Intent("com.skywang.control.CONTROLSERVICE"));

方案结论: 工作正常,但消耗系统资源较多,会增加系统功耗!经过测试发现,此方案运行很正常。但存在一个问题:由于添加了一个不停运行的服务,消耗很多系统资源,导致机器的功能也增加了很多。因此,再实现方案三,对比看看效果如何。

◐◐◐◐●☛█▼▲豪仕知识网http://www.haOz.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

方案三

◐◐◐◐●☛█▼▲豪仕知识网███████豪仕知识网HTtp://www.haoZ.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

方案思路: 在Android的input输入子系统的框架层中捕获wifi按键,并进行相应处理。若捕获到“wifi”按键,则读取wifi的状态:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。

方案分析: 若采用此方案需要解决以下问题01, 将kernel的wifi按键值映射到Android系统的某键值上。-- 这个可以实现。和“方案二”一样,我们通过ADC驱动将wifi按键映射到键值KEY_F16上;然后,将kernel的KEY_F16和Android的某一键值对应。02, 在Android的framework层的键值处理函数中,捕获按键,并进行相应处理。-- 这个可以实现。在input子系统的framework层,捕获到wifi按键对应的Android系统中的按键

架构图:

◐◐◐◐●☛█▼▲豪仕知识网███████豪仕知识http://www.Haoz.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

具体实现:01, 将kernel的wifi按键值映射到Android系统的某键值上。01.01, wifi按键驱动

在按键驱动中编辑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,    },      ...};
◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲豪仕知识网HtTp://▲▼▲▼▲

从中,我们可以看出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 result = result;    // 依次查找key_button中的按键,判断是否需要响应    for (i = 0; i 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 adc_value + DRIFT_ADVALUE &&            result > button->adc_value - DRIFT_ADVALUE) {            button->adc_state = 1;        } else {            button->adc_state = 0;        }           if(bdata->state != button->adc_state)            mod_timer(&bdata->timer,                jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));    }       return;}
◐◐◐◐●☛█▼▲豪仕知识网http://www.haOz.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

这里的callback和“方案二”中的callback有区别。这里没有调用synKeyDone()函数。

01.02, 键值映射映射文件:Linux中的按键值和Android中的按键值不一样。它们是通过.kl映射文件,建立对应关系的。默认的映射文件是 qwerty.kl;但不同的平台可能有效的映射文件不同。用户可以通过查看"/system/usr/keylayout/"目录下的.kl映射文件,来进行验证哪个是有效的。映射方法:一,可以通过查看调用.kl的代码。二,修改.kl文件来验证。在rk3066中,有效的映射文件是“rk29-keypad.kl”。在“rk29-keypad.kl”中添加以下代码将wifi按键和Android中的“AVR_POWER按键”对应。key 186 AVR_POWER

说明:key-- 是关键字。固定值,不需要改变。186-- wifi按键在linux驱动中对应的键值,这里对应KEY_F16的键值,即wifi对应的按键值。关于linux驱动中的各个键值,可以查看“include/linux/input.h”AVR_POWER-- wifi按键映射到Android中的按键,它对应是“KeycodeLabels.h”文件中的KEYCODES表格元素的“literal”值。

◐◐◐◐●☛█▼▲豪仕知识网███████http://www.haOZ.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

KeycodeLabels.h中KEYCODES定义如下:

static const KeycodeLabel KEYCODES[] = {    { "SOFT_LEFT", 1 },    { "SOFT_RIGHT", 2 },    { "HOME", 3 },    { "BACK", 4 },    { "CALL", 5 },    { "ENDCALL", 6 },    { "0", 7 },    { "1", 8 },    { "2", 9 },    { "3", 10 },    { "4", 11 },    { "5", 12 },    { "6", 13 },    { "7", 14 },    { "8", 15 },    { "9", 16 },    { "STAR", 17 },    { "POUND", 18 },    { "DPAD_UP", 19 },    { "DPAD_DOWN", 20 },    { "DPAD_LEFT", 21 },    { "DPAD_RIGHT", 22 },    { "DPAD_CENTER", 23 },    { "VOLUME_UP", 24 },    { "VOLUME_DOWN", 25 },    { "POWER", 26 },    { "CAMERA", 27 },    { "CLEAR", 28 },    { "A", 29 },    { "B", 30 },    { "C", 31 },    { "D", 32 },    { "E", 33 },    { "F", 34 },    { "G", 35 },    { "H", 36 },    { "I", 37 },    { "J", 38 },    { "K", 39 },    { "L", 40 },    { "M", 41 },    { "N", 42 },    { "O", 43 },    { "P", 44 },    { "Q", 45 },    { "R", 46 },    { "S", 47 },    { "T", 48 },    { "U", 49 },    { "V", 50 },    { "W", 51 },    { "X", 52 },    { "Y", 53 },    { "Z", 54 },    { "COMMA", 55 },    { "PERIOD", 56 },    { "ALT_LEFT", 57 },    { "ALT_RIGHT", 58 },    { "SHIFT_LEFT", 59 },    { "SHIFT_RIGHT", 60 },    { "TAB", 61 },    { "SPACE", 62 },    { "SYM", 63 },    { "EXPLORER", 64 },    { "ENVELOPE", 65 },    { "ENTER", 66 },    { "DEL", 67 },    { "GRAVE", 68 },    { "MINUS", 69 },    { "EQUALS", 70 },    { "LEFT_BRACKET", 71 },    { "RIGHT_BRACKET", 72 },    { "BACKSLASH", 73 },    { "SEMICOLON", 74 },    { "APOSTROPHE", 75 },    { "SLASH", 76 },    { "AT", 77 },    { "NUM", 78 },    { "HEADSETHOOK", 79 },    { "FOCUS", 80 },    { "PLUS", 81 },    { "MENU", 82 },    { "NOTIFICATION", 83 },    { "SEARCH", 84 },    { "MEDIA_PLAY_PAUSE", 85 },    { "MEDIA_STOP", 86 },    { "MEDIA_NEXT", 87 },    { "MEDIA_PREVIOUS", 88 },    { "MEDIA_REWIND", 89 },    { "MEDIA_FAST_FORWARD", 90 },    { "MUTE", 91 },    { "PAGE_UP", 92 },    { "PAGE_DOWN", 93 },    { "PICTSYMBOLS", 94 },    { "SWITCH_CHARSET", 95 },    { "BUTTON_A", 96 },    { "BUTTON_B", 97 },    { "BUTTON_C", 98 },    { "BUTTON_X", 99 },    { "BUTTON_Y", 100 },    { "BUTTON_Z", 101 },    { "BUTTON_L1", 102 },    { "BUTTON_R1", 103 },    { "BUTTON_L2", 104 },    { "BUTTON_R2", 105 },    { "BUTTON_THUMBL", 106 },    { "BUTTON_THUMBR", 107 },    { "BUTTON_START", 108 },    { "BUTTON_SELECT", 109 },    { "BUTTON_MODE", 110 },    { "ESCAPE", 111 },    { "FORWARD_DEL", 112 },    { "CTRL_LEFT", 113 },    { "CTRL_RIGHT", 114 },    { "CAPS_LOCK", 115 },    { "SCROLL_LOCK", 116 },    { "META_LEFT", 117 },    { "META_RIGHT", 118 },    { "FUNCTION", 119 },    { "SYSRQ", 120 },    { "BREAK", 121 },    { "MOVE_HOME", 122 },    { "MOVE_END", 123 },    { "INSERT", 124 },    { "FORWARD", 125 },    { "MEDIA_PLAY", 126 },    { "MEDIA_PAUSE", 127 },    { "MEDIA_CLOSE", 128 },    { "MEDIA_EJECT", 129 },    { "MEDIA_RECORD", 130 },    { "F1", 131 },    { "F2", 132 },    { "F3", 133 },    { "F4", 134 },    { "F5", 135 },    { "F6", 136 },    { "F7", 137 },    { "F8", 138 },    { "F9", 139 },    { "F10", 140 },    { "F11", 141 },    { "F12", 142 },    { "NUM_LOCK", 143 },    { "NUMPAD_0", 144 },    { "NUMPAD_1", 145 },    { "NUMPAD_2", 146 },    { "NUMPAD_3", 147 },    { "NUMPAD_4", 148 },    { "NUMPAD_5", 149 },    { "NUMPAD_6", 150 },    { "NUMPAD_7", 151 },    { "NUMPAD_8", 152 },    { "NUMPAD_9", 153 },    { "NUMPAD_DIVIDE", 154 },    { "NUMPAD_MULTIPLY", 155 },    { "NUMPAD_SUBTRACT", 156 },    { "NUMPAD_ADD", 157 },    { "NUMPAD_DOT", 158 },    { "NUMPAD_COMMA", 159 },    { "NUMPAD_ENTER", 160 },    { "NUMPAD_EQUALS", 161 },    { "NUMPAD_LEFT_PAREN", 162 },    { "NUMPAD_RIGHT_PAREN", 163 },    { "VOLUME_MUTE", 164 },    { "INFO", 165 },    { "CHANNEL_UP", 166 },    { "CHANNEL_DOWN", 167 },    { "ZOOM_IN", 168 },    { "ZOOM_OUT", 169 },    { "TV", 170 },    { "WINDOW", 171 },    { "GUIDE", 172 },    { "DVR", 173 },    { "BOOKMARK", 174 },    { "CAPTIONS", 175 },    { "SETTINGS", 176 },    { "TV_POWER", 177 },    { "TV_INPUT", 178 },    { "STB_POWER", 179 },    { "STB_INPUT", 180 },    { "AVR_POWER", 181 },    { "AVR_INPUT", 182 },    { "PROG_RED", 183 },    { "PROG_GREEN", 184 },    { "PROG_YELLOW", 185 },    { "PROG_BLUE", 186 },    { "APP_SWITCH", 187 },    { "BUTTON_1", 188 },    { "BUTTON_2", 189 },    { "BUTTON_3", 190 },    { "BUTTON_4", 191 },    { "BUTTON_5", 192 },    { "BUTTON_6", 193 },    { "BUTTON_7", 194 },    { "BUTTON_8", 195 },    { "BUTTON_9", 196 },    { "BUTTON_10", 197 },    { "BUTTON_11", 198 },    { "BUTTON_12", 199 },    { "BUTTON_13", 200 },    { "BUTTON_14", 201 },    { "BUTTON_15", 202 },    { "BUTTON_16", 203 },    { "LANGUAGE_SWITCH", 204 },    { "MANNER_MODE", 205 },    { "3D_MODE", 206 },    { "CONTACTS", 207 },    { "CALENDAR", 208 },    { "MUSIC", 209 },    { "CALCULATOR", 210 },    { "ZENKAKU_HANKAKU", 211 },    { "EISU", 212 },    { "MUHENKAN", 213 },    { "HENKAN", 214 },    { "KATAKANA_HIRAGANA", 215 },    { "YEN", 216 },    { "RO", 217 },    { "KANA", 218 },    { "ASSIST", 219 },    // NOTE: If you add a new keycode here you must also add it to several other files.    //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.    { NULL, 0 }};
◐◐◐◐●☛█▼▲◐◐◐◐●☛█▼▲HtTp://wWW.haoz.net豪仕知识网●●●●●●●●●●●●●●●●●●●●●●●●●●

KeycodeLabels.h中的按键与Android框架层的KeyEvent.java中的按键值对应。例如:“AVR_POWER”对应“KeyEvent.java中的键值”如下:public static final int KEYCODE_AVR_POWER = 181;

这样,我们发现:将驱动的wifi按键就和Android系统中的KEYCODE_AVR_POWER就对应起来了!

02, 在Android的framework层的键值处理函数中,捕获按键,并进行相应处理。在framework层的input系统中,加入对wifi按键的捕获。添加的文件是:frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java添加的具体方法:在PhoneWindowManager.java的interceptKeyBeforeQueueing()函数中,捕获wifi按键。代码如下:

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;    final boolean canceled = event.isCanceled();    final int keyCode = event.getKeyCode();    final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;    final boolean keyguardActive = (mKeyguardMediator == null ? false :                        (isScreenOn ?                            mKeyguardMediator.isShowingAndNotHidden() :                            mKeyguardMediator.isShowing()));    if (!mSystemBooted) {        return 0;    }    if (DEBUG_INPUT) {        Log.d(TAG, "interceptKeyTq keycode=" + keyCode              + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive);    }    if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0            && event.getRepeatCount() == 0) {        performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);    }    if (keyCode == KeyEvent.KEYCODE_POWER) {        policyFlags |= WindowManagerPolicy.FLAG_WAKE;    }    final boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE            | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;    int result;    if ((isScreenOn && !mHeadless) || (isInjected && !isWakeKey)) {        // When the screen is on or if the key is injected pass the key to the application.        result = ACTION_PASS_TO_USER;    } else {        // When the screen is off and the key is not injected, determine whether        // to wake the device but don't pass the key to the application.        result = 0;        if (down && isWakeKey) {            if (keyguardActive) {                // If the keyguard is showing, let it decide what to do with the wake key.                mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode,                        mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED);            } else {                // Otherwise, wake the device ourselves.                result |= ACTION_POKE_USER_ACTIVITY;            }        }    }    // Handle special keys.    switch (keyCode) {        case KeyEvent.KEYCODE_SYSRQ: {            if (!down) {                printScreenSysRq();            }            break;        }        case KeyEvent.KEYCODE_AVR_POWER: {            Log.d("##SKYWANG##", "global keycode:"+keyCode);            if (keyCode == KeyEvent.KEYCODE_AVR_POWER && down==false) {                // Wifi按键处理                WifiManager mWM = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);                boolean bWifi = mWM.isWifiEnabled();                mWM.setWifiEnabled(!bWifi);            }             break;        }        case KeyEvent.KEYCODE_VOLUME_DOWN:        case KeyEvent.KEYCODE_VOLUME_UP:        case KeyEvent.KEYCODE_VOLUME_MUTE: {            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {                if (down) {                    if (isScreenOn && !mVolumeDownKeyTriggered                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                        mVolumeDownKeyTriggered = true;                        mVolumeDownKeyTime = event.getDownTime();                        mVolumeDownKeyConsumedByScreenshotChord = false;                        cancelPendingPowerKeyAction();                        interceptScreenshotChord();                    }                } else {                    mVolumeDownKeyTriggered = false;                    cancelPendingScreenshotChordAction();                }            } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {                if (down) {                    if (isScreenOn && !mVolumeUpKeyTriggered                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                        mVolumeUpKeyTriggered = true;                        cancelPendingPowerKeyAction();                        cancelPendingScreenshotChordAction();                    }                } else {                    mVolumeUpKeyTriggered = false;                    cancelPendingScreenshotChordAction();                }            } else if (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {                // add by skywang                if (!down)                    handleMuteKey() ;            }            if (down) {                ITelephony telephonyService = getTelephonyService();                if (telephonyService != null) {                    try {                        if (telephonyService.isRinging()) {                            // If an incoming call is ringing, either VOLUME key means                            // "silence ringer".  We handle these keys here, rather than                            // in the InCallScreen, to make sure we'll respond to them                            // even if the InCallScreen hasn't come to the foreground yet.                            // Look for the DOWN event here, to agree with the "fallback"                            // behavior in the InCallScreen.                            Log.i(TAG, "interceptKeyBeforeQueueing:"                                  + " VOLUME key-down while ringing: Silence ringer!");                            // Silence the ringer.  (It's safe to call this                            // even if the ringer has already been silenced.)                            telephonyService.silenceRinger();                            // And *don't* pass this key thru to the current activity                            // (which is probably the InCallScreen.)                            result &= ~ACTION_PASS_TO_USER;                            break;                        }                        if (telephonyService.isOffhook()                                && (result & ACTION_PASS_TO_USER) == 0) {                            // If we are in call but we decided not to pass the key to                            // the application, handle the volume change here.                            handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);                            break;                        }                    } catch (RemoteException ex) {                        Log.w(TAG, "ITelephony threw RemoteException", ex);                    }                }                if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) {                    // If music is playing but we decided not to pass the key to the                    // application, handle the volume change here.                    handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);                    break;                }            }            break;        }        case KeyEvent.KEYCODE_ENDCALL: {            result &= ~ACTION_PASS_TO_USER;            if (down) {                ITelephony telephonyService = getTelephonyService();                boolean hungUp = false;                if (telephonyService != null) {                    try {                        hungUp = telephonyService.endCall();                    } catch (RemoteException ex) {                        Log.w(TAG, "ITelephony threw RemoteException", ex);                    }                }                interceptPowerKeyDown(!isScreenOn || hungUp);            } else {                if (interceptPowerKeyUp(canceled)) {                    if ((mEndcallBehavior                            & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {                        if (goHome()) {                            break;                        }                    }                    if ((mEndcallBehavior                            & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {                        result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;                    }                }            }            break;        }        case KeyEvent.KEYCODE_POWER: {            if(mHdmiPlugged&&SystemProperties.get("ro.hdmi.power_disable","false").equals("true")){               Log.d("hdmi","power disable---------------");               result=0;               break;            }            result &= ~ACTION_PASS_TO_USER;            if (down) {                if (isScreenOn && !mPowerKeyTriggered                        && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                    mPowerKeyTriggered = true;                    mPowerKeyTime = event.getDownTime();                    interceptScreenshotChord();                }                ITelephony telephonyService = getTelephonyService();                boolean hungUp = false;                if (telephonyService != null) {                    try {                        if (telephonyService.isRinging()) {                            // Pressing Power while there's a ringing incoming                            // call should silence the ringer.                            telephonyService.silenceRinger();                        } else if ((mIncallPowerBehavior                                & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0                                && telephonyService.isOffhook()) {                            // Otherwise, if "Power button ends call" is enabled,                            // the Power button will hang up any current active call.                            hungUp = telephonyService.endCall();                        }                    } catch (RemoteException ex) {                        Log.w(TAG, "ITelephony threw RemoteException", ex);                    }                }                interceptPowerKeyDown(!isScreenOn || hungUp                        || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);            } else {                mPowerKeyTriggered = false;                cancelPendingScreenshotChordAction();                if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {                    result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;                }                mPendingPowerKeyUpCanceled = false;            }            break;        }        case KeyEvent.KEYCODE_MEDIA_PLAY:        case KeyEvent.KEYCODE_MEDIA_PAUSE:        case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:            if (down) {                ITelephony telephonyService = getTelephonyService();                if (telephonyService != null) {                    try {                        if (!telephonyService.isIdle()) {                            // Suppress PLAY/PAUSE toggle when phone is ringing or in-call                            // to avoid music playback.                            break;                        }                    } catch (RemoteException ex) {                        Log.w(TAG, "ITelephony threw RemoteException", ex);                    }                }            }        case KeyEvent.KEYCODE_HEADSETHOOK:        case KeyEvent.KEYCODE_MUTE:        case KeyEvent.KEYCODE_MEDIA_STOP:        case KeyEvent.KEYCODE_MEDIA_NEXT:        case KeyEvent.KEYCODE_MEDIA_PREVIOUS:        case KeyEvent.KEYCODE_MEDIA_REWIND:        case KeyEvent.KEYCODE_MEDIA_RECORD:        case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {            if ((result & ACTION_PASS_TO_USER) == 0) {                // Only do this if we would otherwise not pass it to the user. In that                // case, the PhoneWindow class will do the same thing, except it will                // only do it if the showing app doesn't process the key on its own.                // Note that we need to make a copy of the key event here because the                // original key event will be recycled when we return.                mBroadcastWakeLock.acquire();                Message msg = mHandler.obtainMessage(MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK,                        new KeyEvent(event));                msg.setAsynchronous(true);                msg.sendToTarget();            }            break;        }        case KeyEvent.KEYCODE_CALL: {            if (down) {                ITelephony telephonyService = getTelephonyService();                if (telephonyService != null) {                    try {                        if (telephonyService.isRinging()) {                            Log.i(TAG, "interceptKeyBeforeQueueing:"                                  + " CALL key-down while ringing: Answer the call!");                            telephonyService.answerRingingCall();                            // And *don't* pass this key thru to the current activity                            // (which is presumably the InCallScreen.)                            result &= ~ACTION_PASS_TO_USER;                        }                    } catch (RemoteException ex) {                        Log.w(TAG, "ITelephony threw RemoteException", ex);                    }                }            }            break;        }    }    return result;}
◐◐◐◐●☛█▼▲豪仕知识网███████http://www.haOZ.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

在上面的代码中,我们捕获了KeyEvent.KEYCODE_AVR_POWER,并对其进行处理。

方案结论: 方案可行。而且运行效率比“方案二”高,不会造成功耗很大的问题。

◐◐◐◐●☛█▼▲豪仕知识网███████http://www.haOZ.net▼▲▼▲▼▲▼▲▼●●●●●●●▼▲▼▲▼▲

最终总结:方案三最好!

http://www.haoz.net●☛█▼▲◐●☛█▼▲◐◐◐◐●☛█▼▲◐豪仕知识网●☛█▼▲豪仕知识网

本文Android按键添加和处理的三个解决方案整理到此结束,字数约73347字,希望对大家有所帮助。豪仕知识网往后会继续推荐Android按键添加和处理的三个解决方案相关内容。

相关文章