1 /*
2  * Copyright (C) 2016 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.googlecode.android_scripting.facade.bluetooth;
18 
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.UUID;
23 import java.util.concurrent.Callable;
24 
25 import android.app.Service;
26 import android.bluetooth.BluetoothAdapter;
27 import android.bluetooth.BluetoothDevice;
28 import android.bluetooth.BluetoothGatt;
29 import android.bluetooth.BluetoothGattCallback;
30 import android.bluetooth.BluetoothManager;
31 import android.bluetooth.BluetoothGattCharacteristic;
32 import android.bluetooth.BluetoothGattDescriptor;
33 import android.bluetooth.BluetoothGattService;
34 import android.bluetooth.BluetoothProfile;
35 import android.content.Context;
36 import android.os.Bundle;
37 
38 import com.googlecode.android_scripting.ConvertUtils;
39 import com.googlecode.android_scripting.Log;
40 import com.googlecode.android_scripting.MainThread;
41 import com.googlecode.android_scripting.facade.EventFacade;
42 import com.googlecode.android_scripting.facade.FacadeManager;
43 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
44 import com.googlecode.android_scripting.rpc.Rpc;
45 import com.googlecode.android_scripting.rpc.RpcParameter;
46 import com.googlecode.android_scripting.rpc.RpcStopEvent;
47 
48 public class GattClientFacade extends RpcReceiver {
49     private final EventFacade mEventFacade;
50     private BluetoothAdapter mBluetoothAdapter;
51     private BluetoothManager mBluetoothManager;
52     private final Service mService;
53     private final Context mContext;
54     private final HashMap<Integer, myBluetoothGattCallback> mGattCallbackList;
55     private final HashMap<Integer, BluetoothGatt> mBluetoothGattList;
56     private final HashMap<Integer, BluetoothGattCharacteristic> mCharacteristicList;
57     private final HashMap<Integer, BluetoothGattDescriptor> mDescriptorList;
58     private final HashMap<Integer, BluetoothGattService> mGattServiceList;
59     private final HashMap<Integer, List<BluetoothGattService>> mBluetoothGattDiscoveredServicesList;
60     private final HashMap<Integer, List<BluetoothDevice>> mGattServerDiscoveredDevicesList;
61     private static int GattCallbackCount;
62     private static int BluetoothGattDiscoveredServicesCount;
63     private static int BluetoothGattCount;
64     private static int CharacteristicCount;
65     private static int DescriptorCount;
66     private static int GattServerCallbackCount;
67     private static int GattServerCount;
68     private static int GattServiceCount;
69 
GattClientFacade(FacadeManager manager)70     public GattClientFacade(FacadeManager manager) {
71         super(manager);
72         mService = manager.getService();
73         mContext = mService.getApplicationContext();
74         mBluetoothAdapter = MainThread.run(mService,
75                 new Callable<BluetoothAdapter>() {
76                     @Override
77                     public BluetoothAdapter call() throws Exception {
78                         return BluetoothAdapter.getDefaultAdapter();
79                     }
80                 });
81         mBluetoothManager = (BluetoothManager) mContext.getSystemService(Service.BLUETOOTH_SERVICE);
82         mEventFacade = manager.getReceiver(EventFacade.class);
83         mGattCallbackList = new HashMap<Integer, myBluetoothGattCallback>();
84         mCharacteristicList = new HashMap<Integer, BluetoothGattCharacteristic>();
85         mBluetoothGattList = new HashMap<Integer, BluetoothGatt>();
86         mDescriptorList = new HashMap<Integer, BluetoothGattDescriptor>();
87         mGattServiceList = new HashMap<Integer, BluetoothGattService>();
88         mBluetoothGattDiscoveredServicesList = new HashMap<Integer, List<BluetoothGattService>>();
89         mGattServerDiscoveredDevicesList = new HashMap<Integer, List<BluetoothDevice>>();
90     }
91 
92     /**
93      * Create a BluetoothGatt connection
94      *
95      * @param index of the callback to start a connection on
96      * @param macAddress the mac address of the ble device
97      * @param autoConnect Whether to directly connect to the remote device (false) or to
98      *            automatically connect as soon as the remote device becomes available (true)
99      * @return the index of the BluetoothGatt object
100      * @throws Exception
101      */
102     @Rpc(description = "Create a gatt connection")
gattClientConnectGatt( @pcParametername = "index") Integer index, @RpcParameter(name = "macAddress") String macAddress, @RpcParameter(name = "autoConnect") Boolean autoConnect )103     public int gattClientConnectGatt(
104             @RpcParameter(name = "index")
105             Integer index,
106             @RpcParameter(name = "macAddress")
107             String macAddress,
108             @RpcParameter(name = "autoConnect")
109             Boolean autoConnect
110             ) throws Exception {
111         if (mGattCallbackList.get(index) != null) {
112             BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(macAddress);
113             BluetoothGatt mBluetoothGatt = device.connectGatt(mService.getApplicationContext(),
114                     autoConnect,
115                     mGattCallbackList.get(index));
116             BluetoothGattCount += 1;
117             mBluetoothGattList.put(BluetoothGattCount, mBluetoothGatt);
118             return BluetoothGattCount;
119         } else {
120             throw new Exception("Invalid index input:" + Integer.toString(index));
121         }
122     }
123 
124     /**
125      * Trigger discovering of services on the BluetoothGatt object
126      *
127      * @param index The BluetoothGatt object index
128      * @return true, if the remote service discovery has been started
129      * @throws Exception
130      */
131     @Rpc(description = "Trigger discovering of services on the BluetoothGatt object")
gattClientDiscoverServices( @pcParametername = "index") Integer index )132     public boolean gattClientDiscoverServices(
133             @RpcParameter(name = "index")
134             Integer index
135             ) throws Exception {
136         if (mBluetoothGattList.get(index) != null) {
137             return mBluetoothGattList.get(index).discoverServices();
138         } else {
139             throw new Exception("Invalid index input:" + Integer.toString(index));
140         }
141     }
142 
143     /**
144      * Get the services from the BluetoothGatt object
145      *
146      * @param index The BluetoothGatt object index
147      * @return a list of BluetoothGattServices
148      * @throws Exception
149      */
150     @Rpc(description = "Get the services from the BluetoothGatt object")
gattClientGetServices( @pcParametername = "index") Integer index )151     public List<BluetoothGattService> gattClientGetServices(
152             @RpcParameter(name = "index")
153             Integer index
154             ) throws Exception {
155         if (mBluetoothGattList.get(index) != null) {
156             return mBluetoothGattList.get(index).getServices();
157         } else {
158             throw new Exception("Invalid index input:" + Integer.toString(index));
159         }
160     }
161 
162     /**
163      * Abort reliable write of a bluetooth gatt
164      *
165      * @param index the bluetooth gatt index
166      * @throws Exception
167      */
168     @Rpc(description = "Abort reliable write of a bluetooth gatt")
gattClientAbortReliableWrite( @pcParametername = "index") Integer index )169     public void gattClientAbortReliableWrite(
170             @RpcParameter(name = "index")
171             Integer index
172             ) throws Exception {
173         if (mBluetoothGattList.get(index) != null) {
174             mBluetoothGattList.get(index).abortReliableWrite();
175         } else {
176             throw new Exception("Invalid index input:" + index);
177         }
178     }
179 
180     /**
181      * Begin reliable write of a bluetooth gatt
182      *
183      * @param index the bluetooth gatt index
184      * @return
185      * @throws Exception
186      */
187     @Rpc(description = "Begin reliable write of a bluetooth gatt")
gattClientBeginReliableWrite( @pcParametername = "index") Integer index )188     public boolean gattClientBeginReliableWrite(
189             @RpcParameter(name = "index")
190             Integer index
191             ) throws Exception {
192         if (mBluetoothGattList.get(index) != null) {
193             return mBluetoothGattList.get(index).beginReliableWrite();
194         } else {
195             throw new Exception("Invalid index input:" + index);
196         }
197     }
198 
199     /**
200      * Configure a bluetooth gatt's MTU
201      *
202      * @param index the bluetooth gatt index
203      * @param mtu the MTU to set
204      * @return
205      * @throws Exception
206      */
207     @Rpc(description = "true, if the new MTU value has been requested successfully")
gattClientRequestMtu( @pcParametername = "index") Integer index, @RpcParameter(name = "mtu") Integer mtu )208     public boolean gattClientRequestMtu(
209             @RpcParameter(name = "index")
210             Integer index,
211             @RpcParameter(name = "mtu")
212             Integer mtu
213             ) throws Exception {
214         if (mBluetoothGattList.get(index) != null) {
215             return mBluetoothGattList.get(index).requestMtu(mtu);
216         } else {
217             throw new Exception("Invalid index input:" + index);
218         }
219     }
220 
221     /**
222      * Disconnect a bluetooth gatt
223      *
224      * @param index the bluetooth gatt index
225      * @throws Exception
226      */
227     @Rpc(description = "Disconnect a bluetooth gatt")
228     @RpcStopEvent("GattConnect")
gattClientDisconnect( @pcParametername = "index") Integer index )229     public void gattClientDisconnect(
230             @RpcParameter(name = "index")
231             Integer index
232             ) throws Exception {
233         if (mBluetoothGattList.get(index) != null) {
234             mBluetoothGattList.get(index).disconnect();
235         } else {
236             throw new Exception("Invalid index input: " + index);
237         }
238     }
239 
240     /**
241      * Close a bluetooth gatt object
242      *
243      * @param index the bluetooth gatt index
244      * @throws Exception
245      */
246     @Rpc(description = "Close a Bluetooth GATT object")
gattClientClose( @pcParametername = "index") Integer index )247     public void gattClientClose(
248             @RpcParameter(name = "index")
249             Integer index
250             ) throws Exception {
251         if (mBluetoothGattList.get(index) != null) {
252             mBluetoothGattList.get(index).close();
253         } else {
254             throw new Exception("Invalid index input: " + index);
255         }
256     }
257 
258     /**
259      * Execute reliable write on a bluetooth gatt
260      *
261      * @param index the bluetooth gatt index
262      * @return true, if the request to execute the transaction has been sent
263      * @throws Exception
264      */
265     @Rpc(description = "Execute reliable write on a bluetooth gatt")
gattExecuteReliableWrite( @pcParametername = "index") Integer index )266     public boolean gattExecuteReliableWrite(
267             @RpcParameter(name = "index")
268             Integer index
269             ) throws Exception {
270         if (mBluetoothGattList.get(index) != null) {
271             return mBluetoothGattList.get(index).executeReliableWrite();
272         } else {
273             throw new Exception("Invalid index input:" + index);
274         }
275     }
276 
277     /**
278      * Get a list of Bluetooth Devices connnected to the bluetooth gatt
279      *
280      * @param index the bluetooth gatt index
281      * @return List of BluetoothDevice Objects
282      * @throws Exception
283      */
284     @Rpc(description = "Get a list of Bluetooth Devices connnected to the bluetooth gatt")
gattClientGetConnectedDevices( @pcParametername = "index") Integer index )285     public List<BluetoothDevice> gattClientGetConnectedDevices(
286             @RpcParameter(name = "index")
287             Integer index
288             ) throws Exception {
289         if (mBluetoothGattList.get(index) != null) {
290             return mBluetoothGattList.get(index).getConnectedDevices();
291         } else {
292             throw new Exception("Invalid index input:" + index);
293         }
294     }
295 
296     /**
297      * Get the remote bluetooth device this GATT client targets to
298      *
299      * @param index the bluetooth gatt index
300      * @return the remote bluetooth device this gatt client targets to
301      * @throws Exception
302      */
303     @Rpc(description = "Get the remote bluetooth device this GATT client targets to")
gattGetDevice( @pcParametername = "index") Integer index )304     public BluetoothDevice gattGetDevice(
305             @RpcParameter(name = "index")
306             Integer index
307             ) throws Exception {
308         if (mBluetoothGattList.get(index) != null) {
309             return mBluetoothGattList.get(index).getDevice();
310         } else {
311             throw new Exception("Invalid index input:" + index);
312         }
313     }
314 
315     /**
316      * Get the bluetooth devices matching input connection states
317      *
318      * @param index the bluetooth gatt index
319      * @param states the list of states to match
320      * @return The list of BluetoothDevice objects that match the states
321      * @throws Exception
322      */
323     @Rpc(description = "Get the bluetooth devices matching input connection states")
gattClientGetDevicesMatchingConnectionStates( @pcParametername = "index") Integer index, @RpcParameter(name = "states") int[] states )324     public List<BluetoothDevice> gattClientGetDevicesMatchingConnectionStates(
325             @RpcParameter(name = "index")
326             Integer index,
327             @RpcParameter(name = "states")
328             int[] states
329             ) throws Exception {
330         if (mBluetoothGattList.get(index) != null) {
331             return mBluetoothGattList.get(index).getDevicesMatchingConnectionStates(states);
332         } else {
333             throw new Exception("Invalid index input:" + index);
334         }
335     }
336 
337     /**
338      * Get the service from an input UUID
339      *
340      * @param index the bluetooth gatt index
341      * @return BluetoothGattService related to the bluetooth gatt
342      * @throws Exception
343      */
344     @Rpc(description = "Get the service from an input UUID")
gattClientGetServiceUuidList( @pcParametername = "index") Integer index )345     public ArrayList<String> gattClientGetServiceUuidList(
346             @RpcParameter(name = "index")
347             Integer index
348             ) throws Exception {
349         if (mBluetoothGattList.get(index) != null) {
350             ArrayList<String> serviceUuidList = new ArrayList<String>();
351             for (BluetoothGattService service : mBluetoothGattList.get(index).getServices()) {
352                 serviceUuidList.add(service.getUuid().toString());
353             }
354             return serviceUuidList;
355         } else {
356             throw new Exception("Invalid index input:" + index);
357         }
358     }
359 
360     /**
361      * Reads the requested characteristic from the associated remote device.
362      * @param gattIndex the BluetoothGatt server accociated with the device
363      * @param discoveredServiceListIndex the index returned from the discovered
364      * services callback
365      * @param serviceIndex the service index of the discovered services
366      * @param characteristicUuid the characteristic uuid to read
367      * @return true, if the read operation was initiated successfully
368      * @throws Exception
369      */
370     @Rpc(description = "Reads the requested characteristic from the associated remote device.")
gattClientReadCharacteristic( @pcParametername = "gattIndex") Integer gattIndex, @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex, @RpcParameter(name = "serviceIndex") Integer serviceIndex, @RpcParameter(name = "characteristicUuid") String characteristicUuid)371     public boolean gattClientReadCharacteristic(
372         @RpcParameter(name = "gattIndex") Integer gattIndex,
373         @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex,
374         @RpcParameter(name = "serviceIndex") Integer serviceIndex,
375         @RpcParameter(name = "characteristicUuid") String characteristicUuid) throws Exception {
376       BluetoothGatt bluetoothGatt = mBluetoothGattList.get(gattIndex);
377       if (bluetoothGatt == null) {
378         throw new Exception("Invalid gattIndex " + gattIndex);
379       }
380       List<BluetoothGattService> discoveredServiceList =
381           mBluetoothGattDiscoveredServicesList.get(discoveredServiceListIndex);
382       if (discoveredServiceList == null) {
383         throw new Exception("Invalid discoveredServiceListIndex " + discoveredServiceListIndex);
384       }
385       BluetoothGattService gattService = discoveredServiceList.get(serviceIndex);
386       if (gattService == null) {
387         throw new Exception("Invalid serviceIndex " + serviceIndex);
388       }
389       UUID cUuid = UUID.fromString(characteristicUuid);
390       BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(cUuid);
391       if (gattCharacteristic == null) {
392         throw new Exception("Invalid characteristic uuid: " + characteristicUuid);
393       }
394       return bluetoothGatt.readCharacteristic(gattCharacteristic);
395     }
396 
397     /**
398      * Reads the value for a given descriptor from the associated remote device
399      * @param gattIndex - the gatt index to use
400      * @param discoveredServiceListIndex - the discvered serivice list index
401      * @param serviceIndex - the servce index of the discoveredServiceListIndex
402      * @param characteristicUuid - the characteristic uuid in which the descriptor is
403      * @param descriptorUuid - the descriptor uuid to read
404      * @return
405      * @throws Exception
406      */
407     @Rpc(description = "Reads the value for a given descriptor from the associated remote device")
gattClientReadDescriptor(@pcParametername = "gattIndex") Integer gattIndex, @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex, @RpcParameter(name = "serviceIndex") Integer serviceIndex, @RpcParameter(name = "characteristicUuid") String characteristicUuid, @RpcParameter(name = "descriptorUuid") String descriptorUuid)408     public boolean gattClientReadDescriptor(@RpcParameter(name = "gattIndex") Integer gattIndex,
409         @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex,
410         @RpcParameter(name = "serviceIndex") Integer serviceIndex,
411         @RpcParameter(name = "characteristicUuid") String characteristicUuid,
412         @RpcParameter(name = "descriptorUuid") String descriptorUuid) throws Exception {
413       BluetoothGatt bluetoothGatt = mBluetoothGattList.get(gattIndex);
414       if (bluetoothGatt == null) {
415         throw new Exception("Invalid gattIndex " + gattIndex);
416       }
417       List<BluetoothGattService> gattServiceList = mBluetoothGattDiscoveredServicesList.get(
418           discoveredServiceListIndex);
419       if (gattServiceList == null) {
420         throw new Exception("Invalid discoveredServiceListIndex " + discoveredServiceListIndex);
421       }
422       BluetoothGattService gattService = gattServiceList.get(serviceIndex);
423       if (gattService == null) {
424         throw new Exception("Invalid serviceIndex " + serviceIndex);
425       }
426       UUID cUuid = UUID.fromString(characteristicUuid);
427       BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(cUuid);
428       if (gattCharacteristic == null) {
429         throw new Exception("Invalid characteristic uuid: " + characteristicUuid);
430       }
431       UUID dUuid = UUID.fromString(descriptorUuid);
432       BluetoothGattDescriptor gattDescriptor = gattCharacteristic.getDescriptor(dUuid);
433       if (gattDescriptor == null) {
434         throw new Exception("Invalid descriptor uuid: " + descriptorUuid);
435       }
436       return bluetoothGatt.readDescriptor(gattDescriptor);
437     }
438 
439     /**
440      * Write the value of a given descriptor to the associated remote device
441      *
442      * @param index the bluetooth gatt index
443      * @param serviceIndex the service index to write to
444      * @param characteristicUuid the uuid where the descriptor lives
445      * @param descriptorIndex the descriptor index
446      * @return true, if the write operation was initiated successfully
447      * @throws Exception
448      */
449     @Rpc(description = "Write the value of a given descriptor to the associated remote device")
gattClientWriteDescriptor(@pcParametername = "gattIndex") Integer gattIndex, @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex, @RpcParameter(name = "serviceIndex") Integer serviceIndex, @RpcParameter(name = "characteristicUuid") String characteristicUuid, @RpcParameter(name = "descriptorUuid") String descriptorUuid)450     public boolean gattClientWriteDescriptor(@RpcParameter(name = "gattIndex") Integer gattIndex,
451         @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex,
452         @RpcParameter(name = "serviceIndex") Integer serviceIndex,
453         @RpcParameter(name = "characteristicUuid") String characteristicUuid,
454         @RpcParameter(name = "descriptorUuid") String descriptorUuid) throws Exception {
455       BluetoothGatt bluetoothGatt = mBluetoothGattList.get(gattIndex);
456       if (bluetoothGatt == null) {
457         throw new Exception("Invalid gattIndex " + gattIndex);
458       }
459       List<BluetoothGattService> discoveredServiceList =
460           mBluetoothGattDiscoveredServicesList.get(discoveredServiceListIndex);
461       if (discoveredServiceList == null) {
462         throw new Exception("Invalid discoveredServiceListIndex " + discoveredServiceListIndex);
463       }
464       BluetoothGattService gattService = discoveredServiceList.get(serviceIndex);
465       if (gattService == null) {
466         throw new Exception("Invalid serviceIndex " + serviceIndex);
467       }
468       UUID cUuid = UUID.fromString(characteristicUuid);
469       BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(cUuid);
470       if (gattCharacteristic == null) {
471         throw new Exception("Invalid characteristic uuid: " + characteristicUuid);
472       }
473       UUID dUuid = UUID.fromString(descriptorUuid);
474       BluetoothGattDescriptor gattDescriptor = gattCharacteristic.getDescriptor(dUuid);
475       if (gattDescriptor == null) {
476         throw new Exception("Invalid descriptor uuid: " + descriptorUuid);
477       }
478       return bluetoothGatt.writeDescriptor(gattDescriptor);
479     }
480 
481     /**
482      * Write the value to a discovered descriptor.
483      * @param gattIndex - the gatt index to use
484      * @param discoveredServiceListIndex - the discovered service list index
485      * @param serviceIndex - the service index of the discoveredServiceListIndex
486      * @param characteristicUuid - the characteristic uuid in which the descriptor is
487      * @param descriptorUuid - the descriptor uuid to read
488      * @param value - the value to set the descriptor to
489      * @return true is the value was set to the descriptor
490      * @throws Exception
491      */
492     @Rpc(description = "Write the value of a given descriptor to the associated remote device")
gattClientDescriptorSetValue(@pcParametername = "gattIndex") Integer gattIndex, @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex, @RpcParameter(name = "serviceIndex") Integer serviceIndex, @RpcParameter(name = "characteristicUuid") String characteristicUuid, @RpcParameter(name = "descriptorUuid") String descriptorUuid, @RpcParameter(name = "value") byte[] value)493     public boolean gattClientDescriptorSetValue(@RpcParameter(name = "gattIndex") Integer gattIndex,
494         @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex,
495         @RpcParameter(name = "serviceIndex") Integer serviceIndex,
496         @RpcParameter(name = "characteristicUuid") String characteristicUuid,
497         @RpcParameter(name = "descriptorUuid") String descriptorUuid,
498         @RpcParameter(name = "value") byte[] value) throws Exception {
499       if (mBluetoothGattList.get(gattIndex) == null) {
500         throw new Exception("Invalid gattIndex " + gattIndex);
501       }
502       List<BluetoothGattService> discoveredServiceList =
503           mBluetoothGattDiscoveredServicesList.get(discoveredServiceListIndex);
504       if (discoveredServiceList == null) {
505         throw new Exception("Invalid discoveredServiceListIndex " + discoveredServiceListIndex);
506       }
507       BluetoothGattService gattService = discoveredServiceList.get(serviceIndex);
508       if (gattService == null) {
509         throw new Exception("Invalid serviceIndex " + serviceIndex);
510       }
511       UUID cUuid = UUID.fromString(characteristicUuid);
512       BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(cUuid);
513       if (gattCharacteristic == null) {
514         throw new Exception("Invalid characteristic uuid: " + characteristicUuid);
515       }
516       UUID dUuid = UUID.fromString(descriptorUuid);
517       BluetoothGattDescriptor gattDescriptor = gattCharacteristic.getDescriptor(dUuid);
518       if (gattDescriptor == null) {
519         throw new Exception("Invalid descriptor uuid: " + descriptorUuid);
520       }
521       return gattDescriptor.setValue(value);
522     }
523 
524     /**
525      * Write the value of a given characteristic to the associated remote device
526      *
527      * @param index the bluetooth gatt index
528      * @param serviceIndex the service where the characteristic lives
529      * @param characteristicUuid the characteristic uuid to write to
530      * @return true, if the write operation was successful
531      * @throws Exception
532      */
533     @Rpc(description = "Write the value of a given characteristic to the associated remote device")
gattClientWriteCharacteristic(@pcParametername = "gattIndex") Integer gattIndex, @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex, @RpcParameter(name = "serviceIndex") Integer serviceIndex, @RpcParameter(name = "characteristicUuid") String characteristicUuid)534     public boolean gattClientWriteCharacteristic(@RpcParameter(name = "gattIndex") Integer gattIndex,
535         @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex,
536         @RpcParameter(name = "serviceIndex") Integer serviceIndex,
537         @RpcParameter(name = "characteristicUuid") String characteristicUuid) throws Exception {
538       BluetoothGatt bluetoothGatt = mBluetoothGattList.get(gattIndex);
539       if (bluetoothGatt == null) {
540         throw new Exception("Invalid gattIndex " + gattIndex);
541       }
542       List<BluetoothGattService> discoveredServiceList =
543           mBluetoothGattDiscoveredServicesList.get(discoveredServiceListIndex);
544       if (discoveredServiceList == null) {
545         throw new Exception("Invalid discoveredServiceListIndex " + discoveredServiceListIndex);
546       }
547       BluetoothGattService gattService = discoveredServiceList.get(serviceIndex);
548       if (gattService == null) {
549         throw new Exception("Invalid serviceIndex " + serviceIndex);
550       }
551       UUID cUuid = UUID.fromString(characteristicUuid);
552       BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(cUuid);
553       if (gattCharacteristic == null) {
554         throw new Exception("Invalid characteristic uuid: " + characteristicUuid);
555       }
556       return bluetoothGatt.writeCharacteristic(gattCharacteristic);
557     }
558 
559     /**
560      * Write the value to a discovered characteristic.
561      * @param gattIndex - the gatt index to use
562      * @param discoveredServiceListIndex - the discovered service list index
563      * @param serviceIndex - the service index of the discoveredServiceListIndex
564      * @param characteristicUuid - the characteristic uuid in which the descriptor is
565      * @param value - the value to set the characteristic to
566      * @return true, if the value was set to the characteristic
567      * @throws Exception
568      */
569     @Rpc(description = "Write the value of a given characteristic to the associated remote device")
gattClientCharacteristicSetValue(@pcParametername = "gattIndex") Integer gattIndex, @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex, @RpcParameter(name = "serviceIndex") Integer serviceIndex, @RpcParameter(name = "characteristicUuid") String characteristicUuid, @RpcParameter(name = "value") byte[] value)570     public boolean gattClientCharacteristicSetValue(@RpcParameter(name = "gattIndex") Integer gattIndex,
571         @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex,
572         @RpcParameter(name = "serviceIndex") Integer serviceIndex,
573         @RpcParameter(name = "characteristicUuid") String characteristicUuid,
574         @RpcParameter(name = "value") byte[] value) throws Exception {
575       if (mBluetoothGattList.get(gattIndex) == null) {
576         throw new Exception("Invalid gattIndex " + gattIndex);
577       }
578       List<BluetoothGattService> discoveredServiceList =
579           mBluetoothGattDiscoveredServicesList.get(discoveredServiceListIndex);
580       if (discoveredServiceList == null) {
581         throw new Exception("Invalid discoveredServiceListIndex " + discoveredServiceListIndex);
582       }
583       BluetoothGattService gattService = discoveredServiceList.get(serviceIndex);
584       if (gattService == null) {
585         throw new Exception("Invalid serviceIndex " + serviceIndex);
586       }
587       UUID cUuid = UUID.fromString(characteristicUuid);
588       BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(cUuid);
589       if (gattCharacteristic == null) {
590         throw new Exception("Invalid characteristic uuid: " + characteristicUuid);
591       }
592       return gattCharacteristic.setValue(value);
593     }
594 
595     /**
596      * Set write type to a discovered characteristic.
597      * @param gattIndex - the gatt index to use
598      * @param discoveredServiceListIndex - the discovered service list index
599      * @param serviceIndex - the service index of the discoveredServiceListIndex
600      * @param characteristicUuid - the characteristic uuid in which the descriptor is
601      * @param writeType - the write type for characteristic
602      * @return true, if the value was set to the characteristic
603      * @throws Exception
604      */
605     @Rpc(description = "Set write type of a given characteristic to the associated remote device")
gattClientCharacteristicSetWriteType( @pcParametername = "gattIndex") Integer gattIndex, @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex, @RpcParameter(name = "serviceIndex") Integer serviceIndex, @RpcParameter(name = "characteristicUuid") String characteristicUuid, @RpcParameter(name = "writeType") Integer writeType)606     public boolean gattClientCharacteristicSetWriteType(
607         @RpcParameter(name = "gattIndex") Integer gattIndex,
608         @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex,
609         @RpcParameter(name = "serviceIndex") Integer serviceIndex,
610         @RpcParameter(name = "characteristicUuid") String characteristicUuid,
611         @RpcParameter(name = "writeType") Integer writeType) throws Exception {
612       if (mBluetoothGattList.get(gattIndex) == null) {
613         throw new Exception("Invalid gattIndex " + gattIndex);
614       }
615       List<BluetoothGattService> discoveredServiceList =
616           mBluetoothGattDiscoveredServicesList.get(discoveredServiceListIndex);
617       if (discoveredServiceList == null) {
618         throw new Exception("Invalid discoveredServiceListIndex " + discoveredServiceListIndex);
619       }
620       BluetoothGattService gattService = discoveredServiceList.get(serviceIndex);
621       if (gattService == null) {
622         throw new Exception("Invalid serviceIndex " + serviceIndex);
623       }
624       UUID cUuid = UUID.fromString(characteristicUuid);
625       BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(cUuid);
626       if (gattCharacteristic == null) {
627         throw new Exception("Invalid characteristic uuid: " + characteristicUuid);
628       }
629       gattCharacteristic.setWriteType(writeType);
630       return true;
631     }
632 
633     /**
634      * Read the RSSI for a connected remote device
635      *
636      * @param index the bluetooth gatt index
637      * @return true, if the RSSI value has been requested successfully
638      * @throws Exception
639      */
640     @Rpc(description = "Read the RSSI for a connected remote device")
gattClientReadRSSI( @pcParametername = "index") Integer index )641     public boolean gattClientReadRSSI(
642             @RpcParameter(name = "index")
643             Integer index
644             ) throws Exception {
645         if (mBluetoothGattList.get(index) != null) {
646             return mBluetoothGattList.get(index).readRemoteRssi();
647         } else {
648             throw new Exception("Invalid index input:" + index);
649         }
650     }
651 
652     /**
653      * Clears the internal cache and forces a refresh of the services from the remote device
654      *
655      * @param index the bluetooth gatt index
656      * @return Clears the internal cache and forces a refresh of the services from the remote
657      *         device.
658      * @throws Exception
659      */
660     @Rpc(description = "Clears the internal cache and forces a refresh of the services from the remote device")
gattClientRefresh( @pcParametername = "index") Integer index )661     public boolean gattClientRefresh(
662             @RpcParameter(name = "index")
663             Integer index
664             ) throws Exception {
665         if (mBluetoothGattList.get(index) != null) {
666             return mBluetoothGattList.get(index).refresh();
667         } else {
668             throw new Exception("Invalid index input:" + index);
669         }
670     }
671 
672     /**
673      * Request a connection parameter update.
674      * @param index the bluetooth gatt index
675      * @param connectionPriority connection priority
676      * @return boolean True if successful False otherwise.
677      * @throws Exception
678      */
679     @Rpc(description = "Request a connection parameter update. from the Bluetooth Gatt")
gattClientRequestConnectionPriority( @pcParametername = "index") Integer index, @RpcParameter(name = "connectionPriority") Integer connectionPriority )680     public boolean gattClientRequestConnectionPriority(
681             @RpcParameter(name = "index")
682             Integer index,
683             @RpcParameter(name = "connectionPriority")
684             Integer connectionPriority
685             ) throws Exception {
686         boolean result = false;
687         if (mBluetoothGattList.get(index) != null) {
688             result = mBluetoothGattList.get(index).requestConnectionPriority(
689                     connectionPriority);
690         } else {
691             throw new Exception("Invalid index input:" + index);
692         }
693         return result;
694     }
695 
696     /**
697      * Sets the characteristic notification of a bluetooth gatt
698      *
699      * @param index the bluetooth gatt index
700      * @param characteristicIndex the characteristic index
701      * @param enable Enable or disable notifications/indications for a given characteristic
702      * @return true, if the requested notification status was set successfully
703      * @throws Exception
704      */
705     @Rpc(description = "Sets the characteristic notification of a bluetooth gatt")
gattClientSetCharacteristicNotification( @pcParametername = "gattIndex") Integer gattIndex, @RpcParameter(name = "discoveredServiceListIndex") Integer discoveredServiceListIndex, @RpcParameter(name = "serviceIndex") Integer serviceIndex, @RpcParameter(name = "characteristicUuid") String characteristicUuid, @RpcParameter(name = "enable") Boolean enable )706     public boolean gattClientSetCharacteristicNotification(
707             @RpcParameter(name = "gattIndex")
708             Integer gattIndex,
709             @RpcParameter(name = "discoveredServiceListIndex")
710             Integer discoveredServiceListIndex,
711             @RpcParameter(name = "serviceIndex")
712             Integer serviceIndex,
713             @RpcParameter(name = "characteristicUuid")
714             String characteristicUuid,
715             @RpcParameter(name = "enable")
716             Boolean enable
717             ) throws Exception {
718         if (mBluetoothGattList.get(gattIndex) != null) {
719             if(mBluetoothGattDiscoveredServicesList.get(discoveredServiceListIndex) != null) {
720                 List<BluetoothGattService> discoveredServiceList =
721                     mBluetoothGattDiscoveredServicesList.get(discoveredServiceListIndex);
722                 if (discoveredServiceList.get(serviceIndex) != null) {
723                     UUID cUuid = UUID.fromString(characteristicUuid);
724                     if (discoveredServiceList.get(serviceIndex).getCharacteristic(cUuid) != null) {
725                         return mBluetoothGattList.get(gattIndex).setCharacteristicNotification(
726                                 discoveredServiceList.get(serviceIndex).getCharacteristic(cUuid), enable);
727                     } else {
728                         throw new Exception ("Invalid characteristic uuid: " + characteristicUuid);
729                     }
730                 } else {
731                     throw new Exception ("Invalid serviceIndex " + serviceIndex);
732                 }
733             } else {
734                 throw new Exception("Invalid discoveredServiceListIndex: " + discoveredServiceListIndex);
735             }
736         } else {
737             throw new Exception("Invalid gattIndex input: " + gattIndex);
738         }
739     }
740 
741     /**
742      * Create a new GattCallback object
743      *
744      * @return the index of the callback object
745      */
746     @Rpc(description = "Create a new GattCallback object")
gattCreateGattCallback()747     public Integer gattCreateGattCallback() {
748         GattCallbackCount += 1;
749         int index = GattCallbackCount;
750         mGattCallbackList.put(index, new myBluetoothGattCallback(index));
751         return index;
752     }
753 
754     /**
755      * Returns the list of discovered Bluetooth Gatt Services.
756      * @throws Exception
757      */
758     @Rpc(description = "Get Bluetooth Gatt Services")
gattClientGetDiscoveredServicesCount( @pcParametername = "index") Integer index )759     public int gattClientGetDiscoveredServicesCount (
760             @RpcParameter(name = "index")
761             Integer index
762             ) throws Exception {
763         if (mBluetoothGattDiscoveredServicesList.get(index) != null) {
764             return mBluetoothGattDiscoveredServicesList.get(index).size();
765         } else {
766             throw new Exception("Invalid index input:" + index);
767         }
768     }
769 
770     /**
771      * Returns the discovered Bluetooth Gatt Service Uuid.
772      * @throws Exception
773      */
774     @Rpc(description = "Get Bluetooth Gatt Service Uuid")
gattClientGetDiscoveredServiceUuid( @pcParametername = "index") Integer index, @RpcParameter(name = "serviceIndex") Integer serviceIndex )775     public String gattClientGetDiscoveredServiceUuid (
776             @RpcParameter(name = "index")
777             Integer index,
778             @RpcParameter(name = "serviceIndex")
779             Integer serviceIndex
780             ) throws Exception {
781         List<BluetoothGattService> mBluetoothServiceList =
782             mBluetoothGattDiscoveredServicesList.get(index);
783         if (mBluetoothServiceList != null) {
784             return mBluetoothServiceList.get(serviceIndex).getUuid().toString();
785         } else {
786             throw new Exception("Invalid index input:" + index);
787         }
788     }
789 
790     /**
791      * Get discovered characteristic uuids from the pheripheral device.
792      * @param index the index of the bluetooth gatt discovered services list
793      * @param serviceIndex the service to get
794      * @return the list of characteristic uuids
795      * @throws Exception
796      */
797     @Rpc(description = "Get Bluetooth Gatt Services")
gattClientGetDiscoveredCharacteristicUuids( @pcParametername = "index") Integer index, @RpcParameter(name = "serviceIndex") Integer serviceIndex )798     public ArrayList<String> gattClientGetDiscoveredCharacteristicUuids (
799             @RpcParameter(name = "index")
800             Integer index,
801             @RpcParameter(name = "serviceIndex")
802             Integer serviceIndex
803             ) throws Exception {
804         if (mBluetoothGattDiscoveredServicesList.get(index) != null) {
805             if (mBluetoothGattDiscoveredServicesList.get(index).get(serviceIndex) != null) {
806                 ArrayList<String> uuidList = new ArrayList<String>();
807                 List<BluetoothGattCharacteristic> charList = mBluetoothGattDiscoveredServicesList.get(index).get(serviceIndex).getCharacteristics();
808                 for (BluetoothGattCharacteristic mChar : charList) {
809                     uuidList.add(mChar.getUuid().toString());
810                 }
811                 return uuidList;
812             } else {
813                 throw new Exception("Invalid serviceIndex input:" + index);
814             }
815         } else {
816             throw new Exception("Invalid index input:" + index);
817         }
818     }
819 
820     /**
821      * Get discovered descriptor uuids from the pheripheral device.
822      * @param index the discovered services list index
823      * @param serviceIndex the service index of the discovered services list
824      * @param characteristicUuid the characteristicUuid to select from the
825      * discovered service which contains the list of descriptors.
826      * @return the list of descriptor uuids
827      * @throws Exception
828      */
829     @Rpc(description = "Get Bluetooth Gatt Services")
gattClientGetDiscoveredDescriptorUuids( @pcParametername = "index") Integer index, @RpcParameter(name = "serviceIndex") Integer serviceIndex, @RpcParameter(name = "characteristicUuid") String characteristicUuid )830     public ArrayList<String> gattClientGetDiscoveredDescriptorUuids (
831             @RpcParameter(name = "index")
832             Integer index,
833             @RpcParameter(name = "serviceIndex")
834             Integer serviceIndex,
835             @RpcParameter(name = "characteristicUuid")
836             String characteristicUuid
837             ) throws Exception {
838         if (mBluetoothGattDiscoveredServicesList.get(index) != null) {
839             if (mBluetoothGattDiscoveredServicesList.get(index).get(serviceIndex) != null) {
840                 BluetoothGattService service = mBluetoothGattDiscoveredServicesList.get(index).get(serviceIndex);
841                 UUID cUuid = UUID.fromString(characteristicUuid);
842                 if (service.getCharacteristic(cUuid) != null) {
843                     ArrayList<String> uuidList = new ArrayList<String>();
844                     for (BluetoothGattDescriptor mDesc : service.getCharacteristic(cUuid).getDescriptors()) {
845                         uuidList.add(mDesc.getUuid().toString());
846                     }
847                     return uuidList;
848                 } else {
849                     throw new Exception("Invalid characeristicUuid : "
850                             + characteristicUuid);
851                 }
852             } else {
853                 throw new Exception("Invalid serviceIndex input:"
854                         + index);
855             }
856         } else {
857             throw new Exception("Invalid index input:"
858                     + index);
859         }
860     }
861 
862     private class myBluetoothGattCallback extends BluetoothGattCallback {
863         private final Bundle mResults;
864         private final int index;
865         private final String mEventType;
866 
myBluetoothGattCallback(int idx)867         public myBluetoothGattCallback(int idx) {
868             mResults = new Bundle();
869             mEventType = "GattConnect";
870             index = idx;
871         }
872 
873         @Override
onConnectionStateChange(BluetoothGatt gatt, int status, int newState)874         public void onConnectionStateChange(BluetoothGatt gatt, int status,
875                 int newState) {
876             Log.d("gatt_connect change onConnectionStateChange " + mEventType + " " + index);
877             if (newState == BluetoothProfile.STATE_CONNECTED) {
878                 Log.d("State Connected to mac address "
879                         + gatt.getDevice().getAddress() + " status " + status);
880             } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
881                 Log.d("State Disconnected from mac address "
882                         + gatt.getDevice().getAddress() + " status " + status);
883             } else if (newState == BluetoothProfile.STATE_CONNECTING) {
884                 Log.d("State Connecting to mac address "
885                         + gatt.getDevice().getAddress() + " status " + status);
886             } else if (newState == BluetoothProfile.STATE_DISCONNECTING) {
887                 Log.d("State Disconnecting from mac address "
888                         + gatt.getDevice().getAddress() + " status " + status);
889             }
890             mResults.putInt("Status", status);
891             mResults.putInt("State", newState);
892             mEventFacade
893                     .postEvent(mEventType + index + "onConnectionStateChange", mResults.clone());
894             mResults.clear();
895         }
896 
897         @Override
onServicesDiscovered(BluetoothGatt gatt, int status)898         public void onServicesDiscovered(BluetoothGatt gatt, int status) {
899             Log.d("gatt_connect change onServicesDiscovered " + mEventType + " " + index);
900             int idx = BluetoothGattDiscoveredServicesCount++;
901             mBluetoothGattDiscoveredServicesList.put(idx, gatt.getServices());
902             mResults.putInt("ServicesIndex", idx);
903             mResults.putInt("Status", status);
904             for (BluetoothGattService se: gatt.getServices()) {
905                 System.out.println("SWAG: " + se.getUuid().toString());
906             }
907             mEventFacade
908                     .postEvent(mEventType + index + "onServicesDiscovered", mResults.clone());
909             mResults.clear();
910         }
911 
912         @Override
onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)913         public void onCharacteristicRead(BluetoothGatt gatt,
914                 BluetoothGattCharacteristic characteristic,
915                 int status) {
916             Log.d("gatt_connect change onCharacteristicRead " + mEventType + " " + index);
917             mResults.putInt("Status", status);
918             mResults.putString("CharacteristicUuid", characteristic.getUuid().toString());
919             mResults.putByteArray("CharacteristicValue", characteristic.getValue());
920             mEventFacade
921                     .postEvent(mEventType + index + "onCharacteristicRead", mResults.clone());
922             mResults.clear();
923         }
924 
925         @Override
onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)926         public void onCharacteristicWrite(BluetoothGatt gatt,
927                 BluetoothGattCharacteristic characteristic, int status) {
928             Log.d("gatt_connect change onCharacteristicWrite " + mEventType + " " + index);
929             mResults.putInt("Status", status);
930             mResults.putString("CharacteristicUuid", characteristic.getUuid().toString());
931             mResults.putByteArray("CharacteristicValue", characteristic.getValue());
932             mEventFacade
933                     .postEvent(mEventType + index + "onCharacteristicWrite", mResults.clone());
934             mResults.clear();
935         }
936 
937         @Override
onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)938         public void onCharacteristicChanged(BluetoothGatt gatt,
939                 BluetoothGattCharacteristic characteristic) {
940             Log.d("gatt_connect change onCharacteristicChanged " + mEventType + " " + index);
941             mResults.putInt("ID", index);
942             mResults.putString("CharacteristicUuid", characteristic.getUuid().toString());
943             mResults.putByteArray("CharacteristicValue", characteristic.getValue());
944             mEventFacade
945                     .postEvent(mEventType + index + "onCharacteristicChanged", mResults.clone());
946             mResults.clear();
947         }
948 
949         @Override
onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)950         public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
951                 int status) {
952             Log.d("gatt_connect change onServicesDiscovered " + mEventType + " " + index);
953             mResults.putInt("Status", status);
954             mResults.putString("DescriptorUuid", descriptor.getUuid().toString());
955             mEventFacade
956                     .postEvent(mEventType + index + "onDescriptorRead", mResults.clone());
957             mResults.clear();
958         }
959 
960         @Override
onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)961         public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
962                 int status) {
963             Log.d("gatt_connect change onDescriptorWrite " + mEventType + " " + index);
964             mResults.putInt("ID", index);
965             mResults.putInt("Status", status);
966             mResults.putString("DescriptorUuid", descriptor.getUuid().toString());
967             mEventFacade
968                     .postEvent(mEventType + index + "onDescriptorWrite", mResults.clone());
969             mResults.clear();
970         }
971 
972         @Override
onReliableWriteCompleted(BluetoothGatt gatt, int status)973         public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
974             Log.d("gatt_connect change onReliableWriteCompleted " + mEventType + " "
975                     + index);
976             mResults.putInt("Status", status);
977             mEventFacade
978                     .postEvent(mEventType + index + "onReliableWriteCompleted", mResults.clone());
979             mResults.clear();
980         }
981 
982         @Override
onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)983         public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
984             Log.d("gatt_connect change onReadRemoteRssi " + mEventType + " " + index);
985             mResults.putInt("Status", status);
986             mResults.putInt("Rssi", rssi);
987             mEventFacade
988                     .postEvent(mEventType + index + "onReadRemoteRssi", mResults.clone());
989             mResults.clear();
990         }
991 
992         @Override
onMtuChanged(BluetoothGatt gatt, int mtu, int status)993         public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
994             Log.d("gatt_connect change onMtuChanged " + mEventType + " " + index);
995             mResults.putInt("Status", status);
996             mResults.putInt("MTU", mtu);
997             mEventFacade
998                     .postEvent(mEventType + index + "onMtuChanged", mResults.clone());
999             mResults.clear();
1000         }
1001     }
1002 
1003     @Override
shutdown()1004     public void shutdown() {
1005         if (!mBluetoothGattList.isEmpty()) {
1006             if (mBluetoothGattList.values() != null) {
1007                 for (BluetoothGatt mBluetoothGatt : mBluetoothGattList.values()) {
1008                     mBluetoothGatt.close();
1009                 }
1010             }
1011         }
1012         mGattCallbackList.clear();
1013     }
1014 }
1015