summaryrefslogtreecommitdiffstats
path: root/src/android
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@digia.com>2014-11-06 15:46:03 +0100
committerAlex Blasche <alexander.blasche@theqtcompany.com>2014-11-10 08:38:10 +0100
commit9277a04640e488916b79a0dcbf818c7f1aa1510d (patch)
treeb534a9f55da305c50bdda8cb9e29bb6831d324a1 /src/android
parent311eef287d658a376c80047aef942dfbf43eb889 (diff)
Majority of service detail discovery code on Android
Primarily the change adds the required data structures and interfaces on the Java side. What is missing is the reporting of the discovery details back to Qt. Change-Id: I37f2e17bb0f87b4c526f1b43a933b9b09b22be72 Reviewed-by: Timur Pocheptsov <Timur.Pocheptsov@digia.com> Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src/android')
-rw-r--r--src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java254
1 files changed, 252 insertions, 2 deletions
diff --git a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java
index 0fd73613..50e2f293 100644
--- a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java
+++ b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java
@@ -38,12 +38,17 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.util.Log;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Hashtable;
import java.util.List;
+import java.util.UUID;
public class QtBluetoothLE {
private static final String TAG = "QtBluetoothGatt";
@@ -69,6 +74,11 @@ public class QtBluetoothLE {
mRemoteGattAddress = remoteAddress;
}
+
+ /*************************************************************/
+ /* Device scan */
+ /*************************************************************/
+
/*
Returns true, if request was successfully completed
*/
@@ -101,6 +111,10 @@ public class QtBluetoothLE {
public native void leScanResult(long qtObject, BluetoothDevice device, int rssi);
+ /*************************************************************/
+ /* Service Discovery */
+ /*************************************************************/
+
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
@@ -146,10 +160,59 @@ public class QtBluetoothLE {
}
leServicesDiscovered(qtObject, errorCode, builder.toString());
}
+
+ public void onCharacteristicRead(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattCharacteristic characteristic,
+ int status)
+ {
+ GattEntry entry = entries.get(runningHandle);
+ entry.valueKnown = true;
+ entries.set(runningHandle, entry);
+ performServiceDiscoveryForHandle(runningHandle+1, false);
+ }
+
+ public void onCharacteristicWrite(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattCharacteristic characteristic,
+ int status)
+ {
+ System.out.println("onCharacteristicWrite");
+ }
+
+ public void onCharacteristicChanged(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattCharacteristic characteristic)
+ {
+ System.out.println("onCharacteristicChanged");
+ }
+
+ public void onDescriptorRead(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattDescriptor descriptor,
+ int status)
+ {
+ GattEntry entry = entries.get(runningHandle);
+ entry.valueKnown = true;
+ entries.set(runningHandle, entry);
+ performServiceDiscoveryForHandle(runningHandle+1, false);
+ }
+
+ public void onDescriptorWrite(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattDescriptor descriptor,
+ int status)
+ {
+ System.out.println("onDescriptorWrite");
+ }
+ //TODO Requires Android API 21 which is not available on CI yet.
+// public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt gatt,
+// int status) {
+// System.out.println("onReliableWriteCompleted");
+// }
+//
+// public void onReadRemoteRssi(android.bluetooth.BluetoothGatt gatt,
+// int rssi, int status) {
+// System.out.println("onReadRemoteRssi");
+// }
+
};
- public native void leConnectionStateChange(long qtObject, int wasErrorTransition, int newState);
- public native void leServicesDiscovered(long qtObject, int errorCode, String uuidList);
public boolean connect() {
if (mBluetoothGatt != null)
@@ -181,5 +244,192 @@ public class QtBluetoothLE {
return mBluetoothGatt.discoverServices();
}
+ private enum GattEntryType
+ {
+ Service, Characteristic, CharacteristicValue, Descriptor
+ }
+ private class GattEntry
+ {
+ public GattEntryType type;
+ public boolean valueKnown = false;
+ public BluetoothGattService service = null;
+ public BluetoothGattCharacteristic characteristic = null;
+ public BluetoothGattDescriptor descriptor = null;
+ }
+ Hashtable<UUID, List<Integer>> uuidToEntry = new Hashtable<UUID, List<Integer>>(100);
+ ArrayList<GattEntry> entries = new ArrayList<GattEntry>(100);
+
+ private void populateHandles()
+ {
+ // We introduce the notion of artificial handles. While GATT handles
+ // are not exposed on Android they help to quickly identify GATT attributes
+ // on the C++ side. The Qt Api will not expose the handles
+ GattEntry entry = null;
+ List<BluetoothGattService> services = mBluetoothGatt.getServices();
+ for (BluetoothGattService service: services) {
+ entry = new GattEntry();
+ entry.type = GattEntryType.Service;
+ entry.service = service;
+ entries.add(entry);
+
+ //some devices may have more than one service with the same uuid
+ List<Integer> old = uuidToEntry.get(service.getUuid());
+ if (old == null)
+ old = new ArrayList<Integer>();
+ old.add(entries.size()-1);
+ uuidToEntry.put(service.getUuid(), old);
+
+ List<BluetoothGattCharacteristic> charList = service.getCharacteristics();
+ for (BluetoothGattCharacteristic characteristic: charList) {
+ entry = new GattEntry();
+ entry.type = GattEntryType.Characteristic;
+ entry.characteristic = characteristic;
+ entries.add(entry);
+ //uuidToEntry.put(characteristic.getUuid(), entries.size()-1);
+
+ // this emulates GATT value attributes
+ entry = new GattEntry();
+ entry.type = GattEntryType.CharacteristicValue;
+ entries.add(entry);
+ //uuidToEntry.put(characteristic.getUuid(), entry);
+
+ List<BluetoothGattDescriptor> descList = characteristic.getDescriptors();
+ for (BluetoothGattDescriptor desc: descList) {
+ entry = new GattEntry();
+ entry.type = GattEntryType.Descriptor;
+ entry.descriptor = desc;
+ entries.add(entry);
+ //uuidToEntry.put(desc.getUuid(), entries.size()-1);
+ }
+ }
+ }
+
+ entries.trimToSize();
+ }
+
+ private int currentServiceInDiscovery = -1;
+ private int runningHandle = -1;
+ public synchronized boolean discoverServiceDetails(String serviceUuid)
+ {
+ try {
+ if (mBluetoothGatt == null)
+ return false;
+
+ if (entries.isEmpty())
+ populateHandles();
+
+ GattEntry entry;
+ int serviceHandle;
+ try {
+ UUID service = UUID.fromString(serviceUuid);
+ List<Integer> handles = uuidToEntry.get(service);
+ if (handles == null || handles.isEmpty()) {
+ Log.w(TAG, "Unknown service uuid for current device: " + service.toString());
+ return false;
+ }
+
+ //TODO for now we assume we always want the first service in case of uuid collision
+ serviceHandle = handles.get(0);
+ entry = entries.get(serviceHandle);
+ if (entry == null) {
+ Log.w(TAG, "Service with UUID " + service.toString() + " not found");
+ return false;
+ }
+ } catch (IllegalArgumentException ex) {
+ //invalid UUID string passed
+ Log.w(TAG, "Cannot parse given UUID");
+ return false;
+ }
+
+ if (entry.type != GattEntryType.Service) {
+ Log.w(TAG, "Given UUID is not a service UUID: " + serviceUuid);
+ return false;
+ }
+
+ // current service already under investigation
+ if (currentServiceInDiscovery == serviceHandle)
+ return true;
+
+ if (currentServiceInDiscovery != -1) {
+ Log.w(TAG, "Service discovery already running on another service");
+ return false;
+ }
+
+ if (!entry.valueKnown) {
+ performServiceDiscoveryForHandle(serviceHandle, true);
+ } else {
+ Log.w(TAG, "Service already discovered");
+ }
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+
+ return true;
+ }
+
+ private void finishCurrentServiceDiscovery()
+ {
+ GattEntry discoveredService = entries.get(currentServiceInDiscovery);
+ discoveredService.valueKnown = true;
+ entries.set(currentServiceInDiscovery, discoveredService);
+ runningHandle = -1;
+ currentServiceInDiscovery = -1;
+ leServiceDetailDiscoveryFinished(qtObject, discoveredService.service.getUuid().toString());
+ }
+
+ private synchronized void performServiceDiscoveryForHandle(int nextHandle, boolean searchStarted)
+ {
+ try {
+ if (searchStarted) {
+ currentServiceInDiscovery = nextHandle;
+ runningHandle = ++nextHandle;
+ } else {
+ runningHandle = nextHandle;
+ }
+
+ GattEntry entry = null;
+ try {
+ entry = entries.get(nextHandle);
+ } catch (IndexOutOfBoundsException ex) {
+ ex.printStackTrace();
+ Log.w(TAG, "Last entry of last service read");
+ finishCurrentServiceDiscovery();
+ return;
+ }
+
+ boolean result = false;
+ switch (entry.type) {
+ case Characteristic:
+ result = mBluetoothGatt.readCharacteristic(entry.characteristic);
+ if (!result)
+ performServiceDiscoveryForHandle(runningHandle+1, false);
+ break;
+ case CharacteristicValue:
+ // ignore -> nothing to do for this artificial type
+ performServiceDiscoveryForHandle(runningHandle+1, false);
+ break;
+ case Descriptor:
+ result = mBluetoothGatt.readDescriptor(entry.descriptor);
+ if (!result)
+ performServiceDiscoveryForHandle(runningHandle+1, false);
+ break;
+ case Service:
+ finishCurrentServiceDiscovery();
+ break;
+ default:
+ Log.w(TAG, "Invalid GATT attribute type");
+ break;
+ }
+
+ } catch(Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ public native void leConnectionStateChange(long qtObject, int wasErrorTransition, int newState);
+ public native void leServicesDiscovered(long qtObject, int errorCode, String uuidList);
+ public native void leServiceDetailDiscoveryFinished(long qtObject, final String serviceUuid);
}