转载请标明出处:http://blog.csdn.net/lansefeiyang08/article/details/76609900
从15年6月到现在已经两年没有写过博客了,看了一下自己有将近15万的访问量,觉得自己以前写过的东西,以前做过的东西对大家还是很有帮助的。
所以后面我打算继续写一些技术博客,来帮助大家解决一些实际开发中遇到的问题。
今天我们就来讲讲蓝牙HID如何自己写个APP就可以实现和系统设置一样的连接控制功能。
做过系统蓝牙的人,对于HFP、HID和BLE应该会比较熟悉,HFP和HID在Settings里的连接实现也可能有一些了解,但是如果某些功能需要在自己APP来实现HFP或者HID设备的连接和断开,有些人可能就会比较纠结了。
为什么会纠结呢?
因为自己HID的接口没有,找不到HID的类呀。
那么现在我就来告诉大家一个小技巧,来实现以前只有在系统源码才能完成的事情(此方法通用于其他类似情况)。
如果以前想开发Android Bluetooth HID的人,都知道自己开发APP会找不到一个BluetoothInputDevice的类,所以无法获得BluetoothHID的相关信息,那么我们就要解决第一个问题,如何先找到这个类。
在Android系统开发中,会生成很多的中间静态jar,这些jar包很多人不关心也不会使用到,但是今天我们就会使用到这里的东西。
首先你需要有一套Android源码,编译成功后到out/target/common/obj/JAVA_LIBRARIES/路径下,我们要用的包就是在这个里面,找到framework_intermediates这个文件夹,你会发现在文件夹里有一个classes.jar ,恭喜你,你已经找到你要用的最关键的一个东西。把整个framework_intermediates拷贝出来,作为一个额外jar包,加到你的应用中。
既然是技术贴,那顺便讲讲这个jar包,这个jar包是android系统大部分功能的API,其中就包含不对APP开发者开发的API。所以你加入会发现,classes.jar的API怎么和自己SDK
中的android.jar的接口差不多呢,你这么细心很难得,确实差不多,而且比android.jar多。(如果大家对这块感兴趣呢,可以留言,我会肯根据人数多少写一篇关于Android自己生成SDK API介绍的帖子来满足大家)。
讲到这里,其实你应该就应该可以想到Android系统的Settings其实就是调用了这里的Bluetooth HID接口。
大家可能又第二个问题,系统Settings会有很多的权限,并且BluetoothInputDevice是隐藏类,里面的方法我们怎么用呀?
其实大家别疑惑,蓝牙用的权限其实就那么几个,所以权限不是问题,隐藏类我们刚才通过加入jar包解决了,那么就剩下里面的方法怎么用了。
其实找到隐藏类,只要不是hide接口,都是可以正常调用的,如果有hide,大家可以通过反射来实现即可。
下面我贴一下BluetoothInputDevice都有啥,能不能满足大家的功能实现:
33/**
34 * This class provides the public APIs to control the Bluetooth Input
35 * Device Profile.
36 *
37 *BluetoothInputDevice is a proxy object for controlling the Bluetooth
38 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
39 * the BluetoothInputDevice proxy object.
40 *
41 *
Each method is protected with its appropriate permission.
42 *@hide
43 */
44public final class BluetoothInputDevice implements BluetoothProfile {
45 private static final String TAG = "BluetoothInputDevice";
46 private static final boolean DBG = true;
47 private static final boolean VDBG = false;
48
49 /**
50 * Intent used to broadcast the change in connection state of the Input
51 * Device profile.
52 *
53 *
This intent will have 3 extras:
54 *
55 * - {@link #EXTRA_STATE} - The current state of the profile.
56 * - {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
57 * - {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
58 *
59 *
60 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
61 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
62 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
63 *
64 *
Requires {@link android.Manifest.permission#BLUETOOTH} permission to
65 * receive.
66 */
67 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
68 public static final String ACTION_CONNECTION_STATE_CHANGED =
69 "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
70
71 /**
72 * @hide
73 */
74 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
75 public static final String ACTION_PROTOCOL_MODE_CHANGED =
76 "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED";
77
78 /**
79 * @hide
80 */
81 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
82 public static final String ACTION_HANDSHAKE =
83 "android.bluetooth.input.profile.action.HANDSHAKE";
84
85 /**
86 * @hide
87 */
88 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
89 public static final String ACTION_REPORT =
90 "android.bluetooth.input.profile.action.REPORT";
91
92 /**
93 * @hide
94 */
95 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
96 public static final String ACTION_VIRTUAL_UNPLUG_STATUS =
97 "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS";
98
99 /**
100 * @hide
101 */
102 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
103 public static final String ACTION_IDLE_TIME_CHANGED =
104 "codeaurora.bluetooth.input.profile.action.IDLE_TIME_CHANGED";
105
106 /**
107 * Return codes for the connect and disconnect Bluez / Dbus calls.
108 * @hide
109 */
110 public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000;
111
112 /**
113 * @hide
114 */
115 public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001;
116
117 /**
118 * @hide
119 */
120 public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002;
121
122 /**
123 * @hide
124 */
125 public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003;
126
127 /**
128 * @hide
129 */
130 public static final int INPUT_OPERATION_SUCCESS = 5004;
131
132 /**
133 * @hide
134 */
135 public static final int PROTOCOL_REPORT_MODE = 0;
136
137 /**
138 * @hide
139 */
140 public static final int PROTOCOL_BOOT_MODE = 1;
141
142 /**
143 * @hide
144 */
145 public static final int PROTOCOL_UNSUPPORTED_MODE = 255;
146
147 /* int reportType, int reportType, int bufferSize */
148 /**
149 * @hide
150 */
151 public static final byte REPORT_TYPE_INPUT = 1;
152
153 /**
154 * @hide
155 */
156 public static final byte REPORT_TYPE_OUTPUT = 2;
157
158 /**
159 * @hide
160 */
161 public static final byte REPORT_TYPE_FEATURE = 3;
162
163 /**
164 * @hide
165 */
166 public static final int VIRTUAL_UNPLUG_STATUS_SUCCESS = 0;
167
168 /**
169 * @hide
170 */
171 public static final int VIRTUAL_UNPLUG_STATUS_FAIL = 1;
172
173 /**
174 * @hide
175 */
176 public static final String EXTRA_PROTOCOL_MODE = "android.bluetooth.BluetoothInputDevice.extra.PROTOCOL_MODE";
177
178 /**
179 * @hide
180 */
181 public static final String EXTRA_REPORT_TYPE = "android.bluetooth.BluetoothInputDevice.extra.REPORT_TYPE";
182
183 /**
184 * @hide
185 */
186 public static final String EXTRA_REPORT_ID = "android.bluetooth.BluetoothInputDevice.extra.REPORT_ID";
187
188 /**
189 * @hide
190 */
191 public static final String EXTRA_REPORT_BUFFER_SIZE = "android.bluetooth.BluetoothInputDevice.extra.REPORT_BUFFER_SIZE";
192
193 /**
194 * @hide
195 */
196 public static final String EXTRA_REPORT = "android.bluetooth.BluetoothInputDevice.extra.REPORT";
197
198 /**
199 * @hide
200 */
201 public static final String EXTRA_STATUS = "android.bluetooth.BluetoothInputDevice.extra.STATUS";
202
203 /**
204 * @hide
205 */
206 public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS";
207
208 /**
209 * @hide
210 */
211 public static final String EXTRA_IDLE_TIME = "codeaurora.bluetooth.BluetoothInputDevice.extra.IDLE_TIME";
212
213 private Context mContext;
214 private ServiceListener mServiceListener;
215 private BluetoothAdapter mAdapter;
216 private IBluetoothInputDevice mService;
217
218 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
219 new IBluetoothStateChangeCallback.Stub() {
220 public void onBluetoothStateChange(boolean up) {
221 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
222 if (!up) {
223 if (VDBG) Log.d(TAG,"Unbinding service...");
224 synchronized (mConnection) {
225 try {
226 mService = null;
227 mContext.unbindService(mConnection);
228 } catch (Exception re) {
229 Log.e(TAG,"",re);
230 }
231 }
232 } else {
233 synchronized (mConnection) {
234 try {
235 if (mService == null) {
236 if (VDBG) Log.d(TAG,"Binding service...");
237 doBind();
238 }
239 } catch (Exception re) {
240 Log.e(TAG,"",re);
241 }
242 }
243 }
244 }
245 };
246
247 /**
248 * Create a BluetoothInputDevice proxy object for interacting with the local
249 * Bluetooth Service which handles the InputDevice profile
250 *
251 */
252 /*package*/ BluetoothInputDevice(Context context, ServiceListener l) {
253 mContext = context;
254 mServiceListener = l;
255 mAdapter = BluetoothAdapter.getDefaultAdapter();
256
257 IBluetoothManager mgr = mAdapter.getBluetoothManager();
258 if (mgr != null) {
259 try {
260 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
261 } catch (RemoteException e) {
262 Log.e(TAG,"",e);
263 }
264 }
265
266 doBind();
267 }
268
269 boolean doBind()
280
281 /*package*/ void close()
304
305 /**
306 * Initiate connection to a profile of the remote bluetooth device.
307 *
308 *
The system supports connection to multiple input devices.
309 *
310 *
This API returns false in scenarios like the profile on the
311 * device is already connected or Bluetooth is not turned on.
312 * When this API returns true, it is guaranteed that
313 * connection state intent for the profile will be broadcasted with
314 * the state. Users can get the connection state of the profile
315 * from this intent.
316 *
317 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
318 * permission.
319 *
320 * @param device Remote Bluetooth Device
321 * @return false on immediate error,
322 * true otherwise
323 * @hide
324 */
325 public boolean connect(BluetoothDevice device)
338
339 /**
340 * Initiate disconnection from a profile
341 *
342 *
This API will return false in scenarios like the profile on the
343 * Bluetooth device is not in connected state etc. When this API returns,
344 * true, it is guaranteed that the connection state change
345 * intent will be broadcasted with the state. Users can get the
346 * disconnection state of the profile from this intent.
347 *
348 *
If the disconnection is initiated by a remote device, the state
349 * will transition from {@link #STATE_CONNECTED} to
350 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
351 * host (local) device the state will transition from
352 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
353 * state {@link #STATE_DISCONNECTED}. The transition to
354 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
355 * two scenarios.
356 *
357 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
358 * permission.
359 *
360 * @param device Remote Bluetooth Device
361 * @return false on immediate error,
362 * true otherwise
363 * @hide
364 */
365 public boolean disconnect(BluetoothDevice device)
378
379 /**
380 * {@inheritDoc}
381 */
382 public List getConnectedDevices()
395
396 /**
397 * {@inheritDoc}
398 */
399 public List getDevicesMatchingConnectionStates(int[] states)
412
413 /**
414 * {@inheritDoc}
415 */
416 public int getConnectionState(BluetoothDevice device)
429
430 /**
431 * Set priority of the profile
432 *
433 * The device should already be paired.
434 * Priority can be one of {@link #PRIORITY_ON} or
435 * {@link #PRIORITY_OFF},
436 *
437 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
438 * permission.
439 *
440 * @param device Paired bluetooth device
441 * @param priority
442 * @return true if priority is set, false on error
443 * @hide
444 */
445 public boolean setPriority(BluetoothDevice device, int priority)
462
463 /**
464 * Get the priority of the profile.
465 *
466 *
The priority can be any of:
467 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
468 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
469 *
470 *
Requires {@link android.Manifest.permission#BLUETOOTH} permission.
471 *
472 * @param device Bluetooth device
473 * @return priority of the device
474 * @hide
475 */
476 public int getPriority(BluetoothDevice device)
489
490 private final ServiceConnection mConnection = new ServiceConnection()
507
508 private boolean isEnabled()
519
520
521 /**
522 * Initiate virtual unplug for a HID input device.
523 *
524 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
525 *
526 * @param device Remote Bluetooth Device
527 * @return false on immediate error,
528 * true otherwise
529 * @hide
530 */
531 public boolean virtualUnplug(BluetoothDevice device)
546
547 /**
548 * Send Get_Protocol_Mode command to the connected HID input device.
549 *
550 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
551 *
552 * @param device Remote Bluetooth Device
553 * @return false on immediate error,
554 *true otherwise
555 * @hide
556 */
557 public boolean getProtocolMode(BluetoothDevice device)
570
571 /**
572 * Send Set_Protocol_Mode command to the connected HID input device.
573 *
574 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
575 *
576 * @param device Remote Bluetooth Device
577 * @return false on immediate error,
578 * true otherwise
579 * @hide
580 */
581 public boolean setProtocolMode(BluetoothDevice device, int protocolMode)
594
595 /**
596 * Send Get_Report command to the connected HID input device.
597 *
598 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
599 *
600 * @param device Remote Bluetooth Device
601 * @param reportType Report type
602 * @param reportId Report ID
603 * @param bufferSize Report receiving buffer size
604 * @return false on immediate error,
605 * true otherwise
606 * @hide
607 */
608 public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize)
621
622 /**
623 * Send Set_Report command to the connected HID input device.
624 *
625 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
626 *
627 * @param device Remote Bluetooth Device
628 * @param reportType Report type
629 * @param report Report receiving buffer size
630 * @return false on immediate error,
631 * true otherwise
632 * @hide
633 */
634 public boolean setReport(BluetoothDevice device, byte reportType, String report)
647
648 /**
649 * Send Send_Data command to the connected HID input device.
650 *
651 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
652 *
653 * @param device Remote Bluetooth Device
654 * @param report Report to send
655 * @return false on immediate error,
656 * true otherwise
657 * @hide
658 */
659 public boolean sendData(BluetoothDevice device, String report)
672
673 /**
674 * Send Get_Idle_Time command to the connected HID input device.
675 *
676 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
677 *
678 * @param device Remote Bluetooth Device
679 * @return false on immediate error,
680 * true otherwise
681 * @hide
682 */
683 public boolean getIdleTime(BluetoothDevice device)
697 /**
698 * Send Set_Idle_Time command to the connected HID input device.
699 *
700 *
Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
701 *
702 * @param device Remote Bluetooth Device
703 * @param idleTime Idle time to be set on HID Device
704 * @return false on immediate error,
705 * true otherwise
706 * @hide
707 */
708 public boolean setIdleTime(BluetoothDevice device, byte idleTime)
}
为了减少篇幅,我给大家把方法实现给裁剪了,大家可以通过方法和解释来判断一下。
那么下面来重点了,讲了这么多,我代码改怎么写呢,下面我把关键代码写一下,大家自己可以参考一下:
1.获得proxy
mBluetoothAdapter.getProfileProxy(mContext, new remoteDeviceServiceListener(), BluetoothProfile.INPUT_DEVICE);
2.获得监听且执行连接
private final class remoteHidDeviceServiceListener implements BluetoothProfile.ServiceListener {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (null == mBluetoothDevice) {
return;
}
List connectedDevices = proxy.getConnectedDevices();
if (!connectedDevices.isEmpty()) {
for (BluetoothDevice connectedDevice : connectedDevices) {
if (!connectedDevice.getAddress().equals(mBluetoothDevice.getAddress())) {
if (BluetoothProfile.INPUT_DEVICE == profile) {
mBluetoothInputDevice = (BluetoothInputDevice) proxy;
mBluetoothInputDevice.connect(mBluetoothDevice);
}
}
}
} else {
if (BluetoothProfile.INPUT_DEVICE == profile) {
mBluetoothInputDevice = (BluetoothInputDevice) proxy;
mBluetoothInputDevice.connect(mBluetoothDevice);
}
}
}
@Override
public void onServiceDisconnected(int profile) {
}
};
我只写了关键代码,其他的大家可以根据提供的API来补充自己想做的功能。
希望写的东西对大家有用。
我的技术群是307822447,欢迎大家进群交流。