展会信息港展会大全

Android开发中Wifi物理按键 开启/关闭wifi
来源:互联网   发布日期:2015-09-28 16:05:01   浏览:6742次  

导读:实现方案需求:Android机器上有个Wifi物理按键,现在需求通过点击wifi物理按键能够快速的开启 关闭wifi。经过思考之后,拟出下面几种方案:方案一,在linux kernel的驱动中捕获wifi物理按键。在kernel的按键驱动 ...

实现方案需求: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状态,是全局的。

架构图:

具体实现:

通过驱动,将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 structcombo_module__t {

unsigned charstatus_wifikey;

}combo_module_t;

staticcombo_module_tcombo_module;

// 设置wifi状态。

// 这是对外提供的接口

void set_wifikey(int on)

{

printk("%s on=%d\n", __func__, on);

combo_module.status_wifikey = on;

}

EXPORT_SYMBOL(set_wifikey);

// 应用层读取wifi节点的回调函数

staticssize_t show_wifikey_onoff(struct device *dev, struct device_attribute *attr, char *buf)

{

returnsprintf(buf, "%d\n", combo_module.status_wifikey);

}

// 应用层设置wifi节点的回调函数

staticssize_t set_wifikey_onoff(struct device *dev, struct device_attribute *attr,

const char *buf, size_t count)

{

unsigned intval;

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的读取/设置函数和节点对应

staticssize_t show_wifikey_onoff(struct device *dev, struct device_attribute *attr, char *buf);

staticssize_t set_wifikey_onoff(struct device *dev, struct device_attribute *attr,

const char *buf, size_t count);

staticDEVICE_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)

{

returnsysfs_create_group(&pdev->dev.kobj, &control_sysfs_attr_group);

}

// 对应的remove函数。主要作用是将attribute_group从sysfs中注销

staticintcontrol_sysfs_remove(struct platform_device *pdev)

{

sysfs_remove_group(&pdev->dev.kobj, &control_sysfs_attr_group);

return0;

}

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标签

#defineTAG"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)) < 0) {

LOGE("%s : Cannot access \"%s\"", __func__, WIFI_ONOFF_CONTROL);

return; // fd open fail

}

nwr = sprintf(buf, "%d\n", 0);

write(fd, buf, nwr);

LOGE("%s \n", __func__);

close(fd);

}

// Java和JNI函数的绑定表

static JNINativeMethod method_table[] = {

// wifi按键相关函数

{ "is_wifi_key_down", "()I", (void*)is_wifi_key_down },

{ "clr_wifi_key_status", "()V", (void*)clr_wifi_key_status },

};

// 注册native方法到java中

static int registerNativeMethods(JNIEnv* env, const char* className,

JNINativeMethod* gMethods, int numMethods)

{

jclass clazz;

clazz = (*env)->FindClass(env, className);

if (clazz == NULL) {

return JNI_FALSE;

}

if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {

return JNI_FALSE;

}

return JNI_TRUE;

}

int register_wifi_control(JNIEnv *env)

{

// 调用注册方法

return registerNativeMethods(env, JNIREG_CLASS,

method_table, NELEM(method_table));

}

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)

{

JNIEnv* env = NULL;

jint result = -1;

if ((*vm)->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_service

LOCAL_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学习

03 Android读取wifi的开关/状态

在Android创建一个com.skywang.control.ControlService.java。例如,在Launcher的目录下创建 packages/apps/Launcher2/src/com/skywang/control/ControlService.java

ControlService.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 voidrun() {

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的代码:

<service android:name="com.skywang.control.ControlService">

<intent-filter >

<action android:name="com.skywang.control.CONTROLSERVICE" />

</intent-filter>

</service>

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

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

方案结论: 工作正常,但消耗系统资源较多,会增加系统功耗!

经过测试发现,此方案运行很正常。但存在一个问题:由于添加了一个不停运行的服务,消耗很多系统资源,导致机器的功能也增加了很多。

因此,再实现方案三,对比看看效果如何。

方案三

方案思路: 在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系统中的按键

架构图:

具体实现:

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,

},

...

};

从中,我们可以看出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;

}

if(bdata->state != button->adc_state)

mod_timer(&bdata->timer,

jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));

}

return;

}

这里的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 值。

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 }

};

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;

}

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

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

最终总结:方案三最好!

赞助本站

人工智能实验室

相关热词: 物理按键 Wifi Android

AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港