会议平板按键相关

1 篇文章 0 订阅

会议平板的按键加载会在多个不同地方注册及上报按键,需要逐个去解析。

  • 遥控
  • 五向
  • 广播

一.从遥控器获得按键

1.设备对应keylayout映射文件

1) 布局文件保存路径

遥控器的KeyMap具体kl文件保存路径为device/whaley/generic/prebuilts/keylayout目录下保存。kl文件的保存格式为Vendor_[vendorID]_Product_[productID].kl** (e.g.) Vendor_2ba5_Product_0882.kl**
查看蓝牙设备的Vendor ID和Product ID
adb shell cat /proc/bus/input/devices
(e.g.)

I: Bus=0005 Vendor=2ba5 Product=0882 Version=0100
N: Name="微鲸遥控器"
P: Phys=
S: Sysfs=/devices/virtual/misc/uhid/0005:2BA5:0882.0005/input/input9
U: Uniq=dc:2c:26:fa:6e:a2
H: Handlers=sysrq kbd event5
B: PROP=0
B: EV=12001b
B: KEY=1000002000087 ff9f387ad9415fff febeffdfffefffff fffffffffffffffe
B: ABS=10000000000
B: MSC=10
B: LED=1f
2) 源码按键布局选择

开始从frameworks/native/libs/input/InputDevice.cpp中的getInputDeviceConfigurationFilePathByDeviceIdentifier方法得到布局位置

String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
        const InputDeviceIdentifier& deviceIdentifier,
        InputDeviceConfigurationFileType type) {
    if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
        if (deviceIdentifier.version != 0) {
            // Try vendor product version.
            String8 versionPath(getInputDeviceConfigurationFilePathByName(
                    String8::format("Vendor_%04x_Product_%04x_Version_%04x",
                            deviceIdentifier.vendor, deviceIdentifier.product,
                            deviceIdentifier.version),
                    type));
            if (!versionPath.isEmpty()) {
                return versionPath;
            }
        }

        // Try vendor product.
        String8 productPath(getInputDeviceConfigurationFilePathByName(
                String8::format("Vendor_%04x_Product_%04x",
                        deviceIdentifier.vendor, deviceIdentifier.product),
                type));
        if (!productPath.isEmpty()) {
            return productPath;
        }
    }

    // Try device name.
    return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
}

从productPath方法得到的按键布局位置/system/usr/keylayout/,再通过frameworks/native/services/inputflinger/EventHub.cpp中的loadConfigurationLocked去解析文件内容

void EventHub::loadConfigurationLocked(Device* device) {
    device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
            device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
    if (device->configurationFile.isEmpty()) {
        ALOGD("No input device configuration file found for device '%s'.",
                device->identifier.name.string());
    } else {
        status_t status = PropertyMap::load(device->configurationFile,
                &device->configuration);
        if (status) {
            ALOGE("Error loading input device configuration file for device '%s'.  "
                    "Using default configuration.",
                    device->identifier.name.string());
        }
    }
}
3) 按键布局文件解读

Android配置蓝牙键值的步骤和一般的键值基本相同,但是有几点区别需要注意。我们在驱动层用多个红外键值对应了一个Android键值,在应用层靠KeyEvent.getScanCode()函数获取linux键值来加以区分。红外这么做已经比较熟,有方案商提供的文档。但是蓝牙用的是Linux默认的一套流程,需要花时间去搞。
首先先看下kl文件的差别
(e.g.)

#格式为基本key event上报
key 103   DPAD_UP
#格式为Bluetooth key event 上报 第一列表示这是一个键值,第二列可以先不管,第三列是蓝牙键值,第四列是Android键名
key usage 0x000700ea WHALEYMIC

2.会议平板的键值上报策略

并非通过event上报遥控器键值,经过跟踪源码发现而是通过驱动层直接回调方法给FWK层,去模拟按键触发,接下来一起看下按键流程吧。
按键流程是一致的但是调用的方法不同,都是异曲同工,这里就介绍分享语音键的上层上报流程

1) 设置驱动层BT监听注册回调

驱动层device/hisilicon/bigfish/bluetooth/[蓝牙设备号]/bluedroid/btif/src/btif_hh.c实现注册监听

static bt_status_t init( bthh_callbacks_t* callbacks )
{
    UINT32 i;
    BTIF_TRACE_EVENT("%s", __FUNCTION__);

    bt_hh_callbacks = callbacks;
    memset(&btif_hh_cb, 0, sizeof(btif_hh_cb));
    for (i = 0; i < BTIF_HH_MAX_HID; i++){
        btif_hh_cb.devices[i].dev_status = BTHH_CONN_STATE_UNKNOWN;
    }
    /* Invoke the enable service API to the core to set the appropriate service_id */
    btif_enable_service(BTA_HID_SERVICE_ID);
    return BT_STATUS_SUCCESS;
}
2) Native层与kernel层交互

com_android_bluetooth_hid.cpp
Native层注册按键回调,创建sBluetoothHidInterface接口并初始化sBluetoothHidCallbacks

static void initializeNative(JNIEnv *env, jobject object) {
    const bt_interface_t* btInf;
    bt_status_t status;

    if ( (btInf = getBluetoothInterface()) == NULL) {
        ALOGE("Bluetooth module is not loaded");
        return;
    }

    if (sBluetoothHidInterface !=NULL) {
        ALOGW("Cleaning up Bluetooth HID Interface before initializing...");
        sBluetoothHidInterface->cleanup();
        sBluetoothHidInterface = NULL;
    }

    if (mCallbacksObj != NULL) {
        ALOGW("Cleaning up Bluetooth GID callback object");
        env->DeleteGlobalRef(mCallbacksObj);
        mCallbacksObj = NULL;
    }

    if ( (sBluetoothHidInterface = (bthh_interface_t *)
          btInf->get_profile_interface(BT_PROFILE_HIDHOST_ID)) == NULL) {
        ALOGE("Failed to get Bluetooth HID Interface");
        return;
    }

    if ( (status = sBluetoothHidInterface->init(&sBluetoothHidCallbacks)) != BT_STATUS_SUCCESS) {
        ALOGE("Failed to initialize Bluetooth HID, status: %d", status);
        sBluetoothHidInterface = NULL;
        return;
    }

    mCallbacksObj = env->NewGlobalRef(object);
}

与JNI与JAVA赋值

method_onAudioStateChanged = env->GetMethodID(clazz, "onAudioStateChanged", "([BII)V");

回调Java的方法

static void audio_state_callback(bt_bdaddr_t *bd_addr, bthh_audio_state_t state,
                                    bthh_audio_type_t audio_type) {
    jbyteArray addr;

    CHECK_CALLBACK_ENV
    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if (!addr) {
        ALOGE("Fail to new jbyteArray bd addr for HID channel state");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }
    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, addr, (jint) state,
                                                (jint) audio_type);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(addr);
}
3) FWK层方法调用

HidService.java
调用onAudioStateChanged的方法启动handle实现模拟按键的操作

final long downTime = SystemClock.uptimeMillis();
KeyEvent down = KeyEvent.obtain(downTime, downTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_WHALEYMIC, 0, 0,KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD, null);
InputManager.getInstance().injectInputEvent(down, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
down.recycle();
HidService\nFWK com_android\n_bl com_android\n_blue btif_hh\nkernel audio_state_callback() initNative() init()注册 audio_state_callback() onAudioStateChanged() HidService\nFWK com_android\n_bl com_android\n_blue btif_hh\nkernel 遥控器按键注册上报流程图

二.从五向键获得按键

待更新

三.通过广播来下发按键

上层的按键逻辑下发并不是全部由系统按键流程下发,会议平板会做一层的监听,但不会拦截,在InputManagerService类的interceptKeyBeforeQueueing的系统拦截处增加的一个监听器,应用需要在监听器中注册,从而得到相关按键上报.
应用注册的统一入口,得到InputManagerService服务,注册按键监听器InputManager.java

    public void registerListener(IWhaleyKeyListener listener, String id, boolean reg) { 
        try {
            mIm.registerListener(listener, id, reg);
        }
        catch (RemoteException ex) {
            Log.w(TAG, "Failed to registerListener", ex);
        }
    }

注册监听位置InputManagerService.java

    private RemoteCallbackList<IWhaleyKeyListener> mCallbacks;

    //由InputManager传递,处理其他应用的监听入口,需要参数监听器实例,记录索引的ID约定为应用名ID,boolean是否监听或取消监听
    @Override
    public void registerListener(IWhaleyKeyListener listener, String id, boolean reg) {
        synchronized (mCallbacks) {
            if(reg && listener != null) {
                mCallbacks.register(listener, id);
            }
            else if(!reg && listener != null){
                mCallbacks.unregister(listener);
            }
        }
    }

监听的回调

    private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        globalCallBack(event, policyFlags);
        return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
    }

枚举监听者发送得到的按键键值

    private int globalCallBack(KeyEvent event, int policyFlags) {
        int ret = 0;

        synchronized (mCallbacks) {
            if(mCallbacks.getRegisteredCallbackCount() > 0) {
                int n = mCallbacks.beginBroadcast();
                try {
                    if(n > 0) {
                        for (int i = 0; i < n; i++) {
                            String id = (String)mCallbacks.getBroadcastCookie(i);
                            ret = mCallbacks.getBroadcastItem(i).notify(event, String.valueOf(policyFlags));
                            //Log.d(TAG, "#mCallbacks.notify:" + id);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                mCallbacks.finishBroadcast();
            }
        }

        return ret;
    }

(e.g.)应用的注册按键监听的方式

private InputManager mInputManager;
private MyKeyListener mMyKeyListener;

mMyKeyListener = new MyKeyListener();
mInputManager = InputManager.getInstance();
mInputManager.registerListener(mMyKeyListener, this.getPackageName(), true);

private class MyKeyListener extends IWhaleyKeyListener.Stub { 
    public MyKeyListener() {
    }

    @Override
    public int notify(KeyEvent event, String extra) throws RemoteException {
        .......
        return 0;
    }
}

End

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值