• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.googlecode.android_scripting.facade.bluetooth;
18 
19 import android.app.Service;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothHidHost;
23 import android.bluetooth.BluetoothProfile;
24 import android.bluetooth.BluetoothUuid;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.os.ParcelUuid;
30 
31 import com.googlecode.android_scripting.BaseApplication;
32 import com.googlecode.android_scripting.FutureActivityTaskExecutor;
33 import com.googlecode.android_scripting.Log;
34 import com.googlecode.android_scripting.facade.EventFacade;
35 import com.googlecode.android_scripting.facade.FacadeManager;
36 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
37 import com.googlecode.android_scripting.rpc.Rpc;
38 import com.googlecode.android_scripting.rpc.RpcDefault;
39 import com.googlecode.android_scripting.rpc.RpcParameter;
40 
41 import java.util.Arrays;
42 import java.util.List;
43 
44 /*
45  * Class Bluetooth HidFacade
46  */
47 public class BluetoothHidFacade extends RpcReceiver {
48     public static final ParcelUuid[] UUIDS = {
49         BluetoothUuid.HID,
50         BluetoothUuid.HOGP
51     };
52 
53     private final Service mService;
54     private final BluetoothAdapter mBluetoothAdapter;
55     private final FutureActivityTaskExecutor mTaskQueue;
56     private BluetoothHidInputCounterTask mInputCounterTask;
57 
58     private static boolean sIsHidReady = false;
59     private static BluetoothHidHost sHidProfile = null;
60 
61     private final EventFacade mEventFacade;
62 
BluetoothHidFacade(FacadeManager manager)63     public BluetoothHidFacade(FacadeManager manager) {
64         super(manager);
65         mService = manager.getService();
66         mTaskQueue = ((BaseApplication) mService.getApplication()).getTaskExecutor();
67         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
68         mBluetoothAdapter.getProfileProxy(mService, new HidServiceListener(),
69         BluetoothProfile.HID_HOST);
70         IntentFilter pkgFilter = new IntentFilter();
71         pkgFilter.addAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
72         pkgFilter.addAction(BluetoothHidHost.ACTION_PROTOCOL_MODE_CHANGED);
73         pkgFilter.addAction(BluetoothHidHost.ACTION_HANDSHAKE);
74         pkgFilter.addAction(BluetoothHidHost.ACTION_REPORT);
75         pkgFilter.addAction(BluetoothHidHost.ACTION_VIRTUAL_UNPLUG_STATUS);
76         pkgFilter.addAction(BluetoothHidHost.ACTION_IDLE_TIME_CHANGED);
77         mService.registerReceiver(mHidServiceBroadcastReceiver, pkgFilter);
78         Log.d(HidServiceBroadcastReceiver.TAG + " registered");
79         mEventFacade = manager.getReceiver(EventFacade.class);
80     }
81 
82     class HidServiceListener implements BluetoothProfile.ServiceListener {
83         @Override
onServiceConnected(int profile, BluetoothProfile proxy)84         public void onServiceConnected(int profile, BluetoothProfile proxy) {
85             sHidProfile = (BluetoothHidHost) proxy;
86             sIsHidReady = true;
87         }
88 
89         @Override
onServiceDisconnected(int profile)90         public void onServiceDisconnected(int profile) {
91             sIsHidReady = false;
92         }
93     }
94 
95     class HidServiceBroadcastReceiver extends BroadcastReceiver {
96         private static final String TAG = "HidServiceBroadcastReceiver";
97 
98         @Override
onReceive(Context context, Intent intent)99         public void onReceive(Context context, Intent intent) {
100             String action = intent.getAction();
101             Log.d(TAG + " action=" + action);
102 
103             switch (action) {
104                 case BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED: {
105                     int previousState = intent.getIntExtra(
106                             BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
107                     int state = intent.getIntExtra(
108                             BluetoothProfile.EXTRA_STATE, -1);
109                     Log.d("Connection state changed: "
110                             + previousState + " -> " + state);
111                 }
112                 break;
113                 case BluetoothHidHost.ACTION_PROTOCOL_MODE_CHANGED: {
114                     int status = intent.getIntExtra(
115                             BluetoothHidHost.EXTRA_STATUS, -1);
116                     Log.d("Protocol mode changed: " + status);
117                 }
118                 break;
119                 case BluetoothHidHost.ACTION_HANDSHAKE: {
120                     int status = intent.getIntExtra(
121                             BluetoothHidHost.EXTRA_STATUS, -1);
122                     Log.d("Handshake received: " + status);
123                 }
124                 break;
125                 case BluetoothHidHost.ACTION_REPORT: {
126                     byte[] report = intent.getByteArrayExtra(
127                             BluetoothHidHost.EXTRA_REPORT);
128                     Log.d("Received report: " + Arrays.toString(report));
129                 }
130                 break;
131                 case BluetoothHidHost.ACTION_VIRTUAL_UNPLUG_STATUS: {
132                     int status = intent.getIntExtra(
133                             BluetoothHidHost.EXTRA_VIRTUAL_UNPLUG_STATUS, -1);
134                     Log.d("Virtual unplug status: " + status);
135                 }
136                 break;
137                 case BluetoothHidHost.ACTION_IDLE_TIME_CHANGED: {
138                     int idleTime = intent.getIntExtra(
139                             BluetoothHidHost.EXTRA_IDLE_TIME, -1);
140                     Log.d("Idle time changed: " + idleTime);
141                 }
142                 break;
143                 default:
144                     break;
145             }
146         }
147     }
148 
149     private final BroadcastReceiver mHidServiceBroadcastReceiver =
150             new HidServiceBroadcastReceiver();
151 
152     /**
153      * Connect to Hid Profile.
154      * @param device - the Bluetooth Device object to connect to.
155      * @return if the connection was successfull or not.
156      */
hidConnect(BluetoothDevice device)157     public Boolean hidConnect(BluetoothDevice device) {
158         if (sHidProfile == null) return false;
159         return sHidProfile.connect(device);
160     }
161 
162     /**
163      * Disconnect to Hid Profile.
164      * @param device - the Bluetooth Device object to disconnect to.
165      * @return if the disconnection was successfull or not.
166      */
hidDisconnect(BluetoothDevice device)167     public Boolean hidDisconnect(BluetoothDevice device) {
168         if (sHidProfile == null) return false;
169         return sHidProfile.disconnect(device);
170     }
171 
172     /**
173      * Is Hid profile ready.
174      * @return if Hid profile is ready or not.
175      */
176     @Rpc(description = "Is Hid profile ready.")
bluetoothHidIsReady()177     public Boolean bluetoothHidIsReady() {
178         return sIsHidReady;
179     }
180 
181     /**
182      * Connect to an HID device.
183      * @param device - Name or MAC address of a bluetooth device.
184      * @return if the connection was successfull or not.
185      */
186     @Rpc(description = "Connect to an HID device.")
bluetoothHidConnect( @pcParametername = "device", description = "Name or MAC address of a bluetooth device.") String device)187     public Boolean bluetoothHidConnect(
188             @RpcParameter(name = "device",
189                 description = "Name or MAC address of a bluetooth device.")
190                     String device)
191                         throws Exception {
192         if (sHidProfile == null) return false;
193         BluetoothDevice mDevice = BluetoothFacade.getDevice(
194                 BluetoothFacade.DiscoveredDevices, device);
195         Log.d("Connecting to device " + mDevice.getAlias());
196         return hidConnect(mDevice);
197     }
198 
199     /**
200      * Disconnect an HID device.
201      * @param device - the Bluetooth Device object to disconnect to.
202      * @return if the disconnection was successfull or not.
203      */
204     @Rpc(description = "Disconnect an HID device.")
bluetoothHidDisconnect( @pcParametername = "device", description = "Name or MAC address of a device.") String device)205     public Boolean bluetoothHidDisconnect(
206             @RpcParameter(name = "device",
207                 description = "Name or MAC address of a device.")
208                     String device)
209                         throws Exception {
210         if (sHidProfile == null) return false;
211         Log.d("Connected devices: " + sHidProfile.getConnectedDevices());
212         BluetoothDevice mDevice = BluetoothFacade.getDevice(
213                 sHidProfile.getConnectedDevices(), device);
214         return hidDisconnect(mDevice);
215     }
216 
217     /**
218      * Get all the devices connected through HID.
219      * @return List of all the devices connected through HID.
220      */
221     @Rpc(description = "Get all the devices connected through HID.")
bluetoothHidGetConnectedDevices()222     public List<BluetoothDevice> bluetoothHidGetConnectedDevices() {
223         if (!sIsHidReady) return null;
224         return sHidProfile.getConnectedDevices();
225     }
226 
227     /**
228      * Get the connection status of a device.
229      * @param deviceID - Name or MAC address of a bluetooth device.
230      * @return connection status of a device.
231      */
232     @Rpc(description = "Get the connection status of a device.")
bluetoothHidGetConnectionStatus( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)233     public Integer bluetoothHidGetConnectionStatus(
234             @RpcParameter(name = "deviceID",
235                 description = "Name or MAC address of a bluetooth device.")
236                     String deviceID) {
237         if (sHidProfile == null) {
238             return BluetoothProfile.STATE_DISCONNECTED;
239         }
240         List<BluetoothDevice> deviceList = sHidProfile.getConnectedDevices();
241         BluetoothDevice device;
242         try {
243             device = BluetoothFacade.getDevice(deviceList, deviceID);
244         } catch (Exception e) {
245             return BluetoothProfile.STATE_DISCONNECTED;
246         }
247         return sHidProfile.getConnectionState(device);
248     }
249 
250     /**
251      * Send Set_Report command to the connected HID input device.
252      * @param deviceID - Name or MAC address of a bluetooth device.
253      * @return True if successfully sent the command; otherwise false
254      */
255     @Rpc(description =
256             "Send Set_Report command to the connected HID input device.")
bluetoothHidSetReport( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID, @RpcParameter(name = "type") @RpcDefault(value = "1") Integer type, @RpcParameter(name = "report") String report)257     public Boolean bluetoothHidSetReport(
258             @RpcParameter(name = "deviceID",
259                 description = "Name or MAC address of a bluetooth device.")
260                     String deviceID,
261             @RpcParameter(name = "type")
262             @RpcDefault(value = "1") Integer type,
263             @RpcParameter(name = "report")
264                 String report) throws Exception {
265         BluetoothDevice device = BluetoothFacade.getDevice(
266                 sHidProfile.getConnectedDevices(), deviceID);
267         Log.d("type=" + type);
268         return sHidProfile.setReport(device, (byte) (int) type, report);
269     }
270 
271     /**
272      * Sends the Get_Report command to the given connected HID input device.
273      * @param deviceID name or MAC address or the HID input device
274      * @param type Bluetooth HID report type
275      * @param reportId ID for the requesting report
276      * @param buffSize advised buffer size on the Bluetooth HID host
277      * @return True if successfully sent the command; otherwise false
278      * @throws Exception error from Bluetooth HidService
279      */
280     @Rpc(description = "Send Get_Report command to the connected HID input device.")
bluetoothHidGetReport( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID, @RpcParameter(name = "type") @RpcDefault(value = "1") Integer type, @RpcParameter(name = "reportId") Integer reportId, @RpcParameter(name = "buffSize") Integer buffSize)281     public Boolean bluetoothHidGetReport(
282             @RpcParameter(name = "deviceID",
283                 description = "Name or MAC address of a bluetooth device.")
284                     String deviceID,
285             @RpcParameter(name = "type")
286             @RpcDefault(value = "1") Integer type,
287             @RpcParameter(name = "reportId")
288             Integer reportId,
289             @RpcParameter(name = "buffSize")
290             Integer buffSize) throws Exception {
291         BluetoothDevice device = BluetoothFacade.getDevice(
292                 sHidProfile.getConnectedDevices(), deviceID);
293         Log.d("type=" + type + " reportId=" + reportId);
294         return sHidProfile.getReport(
295                 device, (byte) (int) type, (byte) (int) reportId, buffSize);
296     }
297 
298     /**
299      * Sends a data report to the given connected HID input device.
300      * @param deviceID name or MAC address or the HID input device
301      * @param report the report payload
302      * @return True if successfully sent the command; otherwise false
303      * @throws Exception error from Bluetooth HidService
304      */
305     @Rpc(description = "Send data to a connected HID device.")
bluetoothHidSendData( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID, @RpcParameter(name = "report") String report)306     public Boolean bluetoothHidSendData(
307             @RpcParameter(name = "deviceID",
308                 description = "Name or MAC address of a bluetooth device.")
309                 String deviceID,
310             @RpcParameter(name = "report")
311                 String report) throws Exception {
312         BluetoothDevice device = BluetoothFacade.getDevice(
313                 sHidProfile.getConnectedDevices(), deviceID);
314         return sHidProfile.sendData(device, report);
315     }
316 
317 
318     /**
319      * Sends the virtual cable unplug command to the given connected HID input device.
320      * @param deviceID name or MAC address or the HID input device
321      * @return True if successfully sent the command; otherwise false
322      * @throws Exception error from Bluetooth HidService
323      */
324     @Rpc(description = "Send virtual unplug to a connected HID device.")
bluetoothHidVirtualUnplug( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)325         public Boolean bluetoothHidVirtualUnplug(
326                 @RpcParameter(name = "deviceID",
327           description = "Name or MAC address of a bluetooth device.")
328           String deviceID) throws Exception {
329         BluetoothDevice device = BluetoothFacade.getDevice(sHidProfile.getConnectedDevices(),
330               deviceID);
331         return sHidProfile.virtualUnplug(device);
332     }
333 
334     /**
335      * Sends the Set_Priority command to the given connected HID input device.
336      * @param deviceID name or MAC address or the HID input device
337      * @param priority priority level
338      * @return True if successfully sent the command; otherwise false
339      * @throws Exception error from Bluetooth HidService
340      */
341     @Rpc(description = "Set priority of the profile")
bluetoothHidSetPriority( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID, @RpcParameter(name = "priority") Integer priority)342     public Boolean bluetoothHidSetPriority(
343           @RpcParameter(name = "deviceID",
344                   description = "Name or MAC address of a bluetooth device.")
345                   String deviceID,
346           @RpcParameter(name = "priority")
347                   Integer priority) throws Exception {
348         BluetoothDevice device = BluetoothFacade.getDevice(sHidProfile.getConnectedDevices(),
349               deviceID);
350         return sHidProfile.setPriority(device, priority);
351     }
352 
353     /**
354      * Sends the Get_Priority command to the given connected HID input device.
355      * @param deviceID name or MAC address or the HID input device
356      * @return The value of the HID input device priority
357      * @throws Exception error from Bluetooth HidService
358      */
359     @Rpc(description = "Get priority of the profile")
bluetoothHidGetPriority( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)360     public Integer bluetoothHidGetPriority(
361           @RpcParameter(name = "deviceID",
362                   description = "Name or MAC address of a bluetooth device.")
363                   String deviceID) throws Exception {
364         BluetoothDevice device = BluetoothFacade.getDevice(sHidProfile.getConnectedDevices(),
365               deviceID);
366         return sHidProfile.getPriority(device);
367     }
368 
369     /**
370      * Sends the Set_Protocol_Mode command to the given connected HID input device.
371      * @param deviceID name or MAC address or the HID input device
372      * @param protocolMode protocol mode
373      * @return True if successfully sent the command; otherwise false
374      * @throws Exception error from Bluetooth HidService
375      */
376     @Rpc(description = "Send Set_Protocol_Mode command to the connected HID input device.")
bluetoothHidSetProtocolMode( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID, @RpcParameter(name = "protocolMode") Integer protocolMode)377     public Boolean bluetoothHidSetProtocolMode(
378           @RpcParameter(name = "deviceID",
379                   description = "Name or MAC address of a bluetooth device.")
380                   String deviceID,
381           @RpcParameter(name = "protocolMode")
382                   Integer protocolMode) throws Exception {
383         BluetoothDevice device = BluetoothFacade.getDevice(sHidProfile.getConnectedDevices(),
384               deviceID);
385         return sHidProfile.setProtocolMode(device, protocolMode);
386     }
387 
388     /**
389      * Sends the Get_Protocol_Mode command to the given connected HID input device.
390      * @param deviceID name or MAC address or the HID input device
391      * @return True if successfully sent the command; otherwise false
392      * @throws Exception error from Bluetooth HidService
393      */
394     @Rpc(description =
395             "Send Get_Protocol_Mode command to the connected HID input device.")
bluetoothHidGetProtocolMode( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)396     public Boolean bluetoothHidGetProtocolMode(
397           @RpcParameter(name = "deviceID",
398                   description = "Name or MAC address of a bluetooth device.")
399                   String deviceID) throws Exception {
400         BluetoothDevice device = BluetoothFacade.getDevice(
401                 sHidProfile.getConnectedDevices(), deviceID);
402         return sHidProfile.getProtocolMode(device);
403     }
404 
405     /**
406      * Sends the Set_Idle_Time command to the given connected HID input device.
407      * @param deviceID name or MAC address or the HID input device
408      * @param idleTime idle time
409      * @return True if successfully sent the command; otherwise false
410      * @throws Exception error from Bluetooth HidService
411      */
412     @Rpc(description = "Send Set_Idle_Time command to the connected HID input device.")
bluetoothHidSetIdleTime( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID, @RpcParameter(name = "idleTime") Integer idleTime)413     public Boolean bluetoothHidSetIdleTime(
414             @RpcParameter(name = "deviceID",
415                 description = "Name or MAC address of a bluetooth device.")
416                     String deviceID,
417             @RpcParameter(name = "idleTime")
418                 Integer idleTime) throws Exception {
419         BluetoothDevice device = BluetoothFacade.getDevice(
420                 sHidProfile.getConnectedDevices(), deviceID);
421         return sHidProfile.setIdleTime(
422                 device, (byte) (int) idleTime);
423     }
424 
425     /**
426      * Sends the Get_Idle_Time command to the given connected HID input device.
427      * @param deviceID name or MAC address or the HID input device
428      * @return True if successfully sent the command; otherwise false
429      * @throws Exception error from Bluetooth HidService
430      */
431     @Rpc(description = "Send Get_Idle_Time command to the connected HID input device.")
bluetoothHidGetIdleTime( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)432     public Boolean bluetoothHidGetIdleTime(
433             @RpcParameter(name = "deviceID",
434                   description = "Name or MAC address of a bluetooth device.")
435                   String deviceID) throws Exception {
436         BluetoothDevice device = BluetoothFacade.getDevice(
437                 sHidProfile.getConnectedDevices(), deviceID);
438         return sHidProfile.getIdleTime(device);
439     }
440 
441     /**
442      * Start to monitor HID device input count
443      */
444     @Rpc(description = "Start keyboard/mouse input counter")
bluetoothHidStartInputCounter()445     public void bluetoothHidStartInputCounter() throws InterruptedException {
446         mInputCounterTask = new BluetoothHidInputCounterTask();
447         mTaskQueue.execute(mInputCounterTask);
448         mInputCounterTask.getShowLatch().await();
449     }
450 
451     /**
452      * Stop to monitor HID device input count
453      */
454     @Rpc(description = "Stop keyboard/mouse input rate checker")
bluetoothHidStopInputCounter()455     public void bluetoothHidStopInputCounter() throws InterruptedException {
456         if (mInputCounterTask != null) {
457             mInputCounterTask.finish();
458             mInputCounterTask = null;
459         }
460     }
461 
462     /**
463      * Get HID device input rate
464      * @return The value of HID device input count during the first and the last input.
465      */
466     @Rpc(description = "Get HID keyboard/mouse input count")
bluetoothHidGetCount()467     public double bluetoothHidGetCount() {
468         return mInputCounterTask.getCount();
469     }
470 
471     /**
472      * Test byte transfer.
473      */
474     @Rpc(description = "Test byte transfer.")
testByte()475     public byte[] testByte() {
476         byte[] bts = {0b01, 0b10, 0b11, 0b100};
477         return bts;
478     }
479 
480     @Override
shutdown()481     public void shutdown() {
482         mService.unregisterReceiver(mHidServiceBroadcastReceiver);
483     }
484 }
485