Android 关于蓝牙的部分使用的是BlueZ协议栈。可是直到目前2.3.3都没有扩展HID的profile,只是实现了最基本的Handset和d2dp的profile,因此咱们的工做涉及到从应用到jni三层的修改,具体修改文件如图所示,绿色表示新建的类,橙色表示修改的类。 java
一. 本地层
路径:framework/base/core/jni/
参照Android_server_BluetoothA2dpService.cpp新建android_server_bluetoothHidServer.cpp。该类中主要是经过dbus对bluez协议栈的访问,dbus 的通用方法都在android_bluetooth_common.cpp中实现,咱们作的仅仅是经过dbus_func_args_async调用到bluez提供的input接口。
主要实现如下两个方法函数: android
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
staticjboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if(nat) {
constchar*c_path = env->GetStringUTFChars(path, NULL);
bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
c_path,"org.bluez.Input","Connect",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
returnret ? JNI_TRUE : JNI_FALSE;
}
#endif
returnJNI_FALSE;
}
staticjboolean disconnectSinkNative(JNIEnv *env, jobject object,
jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if(nat) {
constchar*c_path = env->GetStringUTFChars(path, NULL);
bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
c_path,"org.bluez.Input","Disconnect",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
returnret ? JNI_TRUE : JNI_FALSE;
}
#endif
returnJNI_FALSE;
}
|
这里要注意将该文件添加到AndroidRuntime.cpp和Android.mk中,不然不会编译到动态库中。
此部分编译后最终生成libAndroid_runtime.so并替换到system/libs下
二.Framework的java部分
路径framework/base/java/Android/server/中添加BluetoothHidService.java文件
路径framework/base/java/Android/bluetooth/中添加BluetoothHid.java和IBluetoothHid.aidl文件。 框架
01
02
03
04
05
06
07
08
09
10
11
12
13
|
interfaceIBluetoothHid {
booleanconnect(in BluetoothDevice device);
booleandisconnect(in BluetoothDevice device);
intgetState(in BluetoothDevice device);
booleansetPriority(in BluetoothDevice device,intpriority);
intgetPriority(in BluetoothDevice device);
}
|
BluetoothHid.java中主要的两个方法connect和disconnect间接地经过aidl访问BluetoothHidService。这里主要是实现跨进程并为上层提供可直接访问的方法。
由此framework的主要部分打包生成framework.Jar并最终部署到system/framework里。
三.应用(Settings.apk)
最后须要修改应用部分,应用部分的修改点比较分散,不想框架层那样整块模仿A2DP的样子那么方便,但也不是说jni部分有多么容易。反而对于我这种对C语言不熟悉的人来讲,修改jni是最头疼得事了。好在蓝牙HID 这部分框架层的修改都是整块进行的,理解上还算比价容易。
总的来讲在Settings.apk中要修改的文件主要是这么几个:
LocalBluetoothProfileManager.java 这里主要提供一个HID的profile以便应用层访问。建一个HIDProfile的class调用framework中的BluetoothHID。实际上就是经过bender机制调用了BluetoothHidService。
CashedBluetoothDevice中添加显示蓝牙键盘的图标,BluetoothPairingDialog中则须要添加一段蓝牙配对验证处理的代码,我是参照i9000中先弹出一个随机数,而后在键盘中敲入相同的随机数即配对成功,具体实现以下: dom
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
Private view createView(){
if(mType == BluetoothDevice.PAIRING_VARIANT_PIN) {
……
// HID
if(isDeviceKeyboard(mDevice)) {
String pin = String.format("%06d", Long.valueOf(Math
.abs(newRandom().nextLong() % 1000000L)));
mPairingView.setVisibility(View.GONE);
messageView.setText(getString(
R.string.bluetooth_enter_keyboard_pin_msg, pin, name));
byte[] bytePin = BluetoothDevice.convertPinToBytes(pin);
if(bytePin !=null) {
mDevice.setPin(bytePin);
}
}
……
}
|
以上为Android中实现蓝牙键盘的具体步骤。 async