• <td id="ae6ms"><li id="ae6ms"></li></td>
  • <xmp id="ae6ms"><td id="ae6ms"></td><table id="ae6ms"></table>
  • <table id="ae6ms"></table>
  • <td id="ae6ms"></td>
    <td id="ae6ms"></td>
  • <table id="ae6ms"></table><table id="ae6ms"><td id="ae6ms"></td></table>
  • <td id="ae6ms"></td>
  • <table id="ae6ms"><li id="ae6ms"></li></table>
  • <table id="ae6ms"></table>
    西西軟件園多重安全檢測下載網站、值得信賴的軟件下載站!
    軟件
    軟件
    文章
    搜索

    首頁編程開發Android → Android按鍵添加和處理的三個解決方案

    Android按鍵添加和處理的三個解決方案

    相關軟件相關文章發表評論 來源:西西整理時間:2013/6/18 19:10:36字體大?。?em class="fontsize">A-A+

    作者:西西點擊:470次評論:0次標簽: 按鍵

    91按鍵映射器V3.02 綠色版
    • 類型:鼠標鍵盤大?。?i>405KB語言:中文 評分:10.0
    • 標簽:
    立即下載

    Android按鍵添加和處理的方案

    實現方案需求: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 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 <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <jni.h>
    #include <fcntl.h>
    #include <assert.h>
    
    
    // 獲取數組的大小
    # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
    // 指定要注冊的類,對應完整的java類名
    #define JNIREG_CLASS "com/skywang/control/ControlService"
    
    // 引入log頭文件
    #include <android/log.h>  
    
    // log標簽
    #define  TAG    "WifiControl"
    // 定義debug信息
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
    // 定義error信息
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
    
    #define WIFI_ONOFF_CONTROL      "/sys/devices/platform/misc_ctl/wifikey_onoff"
    
    
    // 設置wifi電源開關
    JNIEXPORT jint JNICALL is_wifi_key_down(JNIEnv *env, jclass clazz)
    {
        int fd;
        int ret;
        char buf[2];
    
    //    LOGD("%s \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 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的代碼:

    <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,并對其進行處理。


    方案結論: 方案可行。而且運行效率比“方案二”高,不會造成功耗很大的問題。


    最終總結:方案三最好!

      相關評論

      閱讀本文后您有什么感想? 已有人給出評價!

      • 8 喜歡喜歡
      • 3 頂
      • 1 難過難過
      • 5 囧
      • 3 圍觀圍觀
      • 2 無聊無聊

      熱門評論

      最新評論

      發表評論 查看所有評論(0)

      昵稱:
      表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
      字數: 0/500 (您的評論需要經過審核才能顯示)
      女人让男人桶30分钟免费视频,女人张开腿让男人桶个爽,一进一出又大又粗爽视频
    • <td id="ae6ms"><li id="ae6ms"></li></td>
    • <xmp id="ae6ms"><td id="ae6ms"></td><table id="ae6ms"></table>
    • <table id="ae6ms"></table>
    • <td id="ae6ms"></td>
      <td id="ae6ms"></td>
    • <table id="ae6ms"></table><table id="ae6ms"><td id="ae6ms"></td></table>
    • <td id="ae6ms"></td>
    • <table id="ae6ms"><li id="ae6ms"></li></table>
    • <table id="ae6ms"></table>