diff options
Diffstat (limited to 'src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java')
-rw-r--r-- | src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java | 611 |
1 files changed, 0 insertions, 611 deletions
diff --git a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java deleted file mode 100644 index cdd16686..00000000 --- a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java +++ /dev/null @@ -1,611 +0,0 @@ -/**************************************************************************** - ** - ** Copyright (C) 2016 The Qt Company Ltd. - ** Contact: https://www.qt.io/licensing/ - ** - ** This file is part of the QtBluetooth module of the Qt Toolkit. - ** - ** $QT_BEGIN_LICENSE:LGPL$ - ** Commercial License Usage - ** Licensees holding valid commercial Qt licenses may use this file in - ** accordance with the commercial license agreement provided with the - ** Software or, alternatively, in accordance with the terms contained in - ** a written agreement between you and The Qt Company. For licensing terms - ** and conditions see https://www.qt.io/terms-conditions. For further - ** information use the contact form at https://www.qt.io/contact-us. - ** - ** GNU Lesser General Public License Usage - ** Alternatively, this file may be used under the terms of the GNU Lesser - ** General Public License version 3 as published by the Free Software - ** Foundation and appearing in the file LICENSE.LGPL3 included in the - ** packaging of this file. Please review the following information to - ** ensure the GNU Lesser General Public License version 3 requirements - ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. - ** - ** GNU General Public License Usage - ** Alternatively, this file may be used under the terms of the GNU - ** General Public License version 2.0 or (at your option) the GNU General - ** Public license version 3 or any later version approved by the KDE Free - ** Qt Foundation. The licenses are as published by the Free Software - ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 - ** included in the packaging of this file. Please review the following - ** information to ensure the GNU General Public License requirements will - ** be met: https://www.gnu.org/licenses/gpl-2.0.html and - ** https://www.gnu.org/licenses/gpl-3.0.html. - ** - ** $QT_END_LICENSE$ - ** - ****************************************************************************/ - -package org.qtproject.qt5.android.bluetooth; - -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; -import android.bluetooth.BluetoothGattService; -import android.content.Context; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattServer; -import android.bluetooth.BluetoothGattServerCallback; -import android.bluetooth.BluetoothManager; -import android.bluetooth.BluetoothProfile; -import android.bluetooth.le.AdvertiseCallback; -import android.bluetooth.le.AdvertiseData; -import android.bluetooth.le.AdvertiseData.Builder; -import android.bluetooth.le.AdvertiseSettings; -import android.bluetooth.le.BluetoothLeAdvertiser; -import android.os.ParcelUuid; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.HashMap; -import java.util.UUID; - -public class QtBluetoothLEServer { - private static final String TAG = "QtBluetoothGattServer"; - - /* Pointer to the Qt object that "owns" the Java object */ - @SuppressWarnings({"CanBeFinal", "WeakerAccess"}) - long qtObject = 0; - @SuppressWarnings("WeakerAccess") - - private Context qtContext = null; - - // Bluetooth members - private final BluetoothAdapter mBluetoothAdapter; - private BluetoothGattServer mGattServer = null; - private BluetoothLeAdvertiser mLeAdvertiser = null; - - private String mRemoteName = ""; - public String remoteName() { return mRemoteName; } - - private String mRemoteAddress = ""; - public String remoteAddress() { return mRemoteAddress; } - - /* - As per Bluetooth specification each connected device can have individual and persistent - Client characteristic configurations (see Bluetooth Spec 5.0 Vol 3 Part G 3.3.3.3) - This class manages the existing configurrations. - */ - private class ClientCharacteristicManager { - private final HashMap<BluetoothGattCharacteristic, List<Entry>> notificationStore = new HashMap<BluetoothGattCharacteristic, List<Entry>>(); - - private class Entry { - BluetoothDevice device = null; - byte[] value = null; - boolean isConnected = false; - } - - public void insertOrUpdate(BluetoothGattCharacteristic characteristic, - BluetoothDevice device, byte[] newValue) - { - if (notificationStore.containsKey(characteristic)) { - - List<Entry> entries = notificationStore.get(characteristic); - for (int i = 0; i < entries.size(); i++) { - if (entries.get(i).device.equals(device)) { - Entry e = entries.get(i); - e.value = newValue; - entries.set(i, e); - return; - } - } - - // not match so far -> add device to list - Entry e = new Entry(); - e.device = device; - e.value = newValue; - e.isConnected = true; - entries.add(e); - return; - } - - // new characteristic - Entry e = new Entry(); - e.device = device; - e.value = newValue; - e.isConnected = true; - List<Entry> list = new LinkedList<Entry>(); - list.add(e); - notificationStore.put(characteristic, list); - } - - /* - Marks client characteristic configuration entries as (in)active based the associated - devices general connectivity state. - This function avoids that existing configurations are not acted - upon when the associated device is not connected. - */ - public void markDeviceConnectivity(BluetoothDevice device, boolean isConnected) - { - final Iterator<BluetoothGattCharacteristic> keys = notificationStore.keySet().iterator(); - while (keys.hasNext()) { - final BluetoothGattCharacteristic characteristic = keys.next(); - final List<Entry> entries = notificationStore.get(characteristic); - if (entries == null) - continue; - - ListIterator<Entry> charConfig = entries.listIterator(); - while (charConfig.hasNext()) { - Entry e = charConfig.next(); - if (e.device.equals(device)) - e.isConnected = isConnected; - } - } - } - - // Returns list of all BluetoothDevices which require notification or indication. - // No match returns an empty list. - List<BluetoothDevice> getToBeUpdatedDevices(BluetoothGattCharacteristic characteristic) - { - ArrayList<BluetoothDevice> result = new ArrayList<BluetoothDevice>(); - if (!notificationStore.containsKey(characteristic)) - return result; - - final ListIterator<Entry> iter = notificationStore.get(characteristic).listIterator(); - while (iter.hasNext()) - result.add(iter.next().device); - - return result; - } - - // Returns null if no match; otherwise the configured actual client characteristic - // configuration value - byte[] valueFor(BluetoothGattCharacteristic characteristic, BluetoothDevice device) - { - if (!notificationStore.containsKey(characteristic)) - return null; - - List<Entry> entries = notificationStore.get(characteristic); - for (int i = 0; i < entries.size(); i++) { - final Entry entry = entries.get(i); - if (entry.device.equals(device) && entry.isConnected == true) - return entries.get(i).value; - } - - return null; - } - } - - private static final UUID CLIENT_CHARACTERISTIC_CONFIGURATION_UUID = UUID - .fromString("00002902-0000-1000-8000-00805f9b34fb"); - ClientCharacteristicManager clientCharacteristicManager = new ClientCharacteristicManager(); - - public QtBluetoothLEServer(Context context) - { - qtContext = context; - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - - if (mBluetoothAdapter == null || qtContext == null) { - Log.w(TAG, "Missing Bluetooth adapter or Qt context. Peripheral role disabled."); - return; - } - - BluetoothManager manager = (BluetoothManager) qtContext.getSystemService(Context.BLUETOOTH_SERVICE); - if (manager == null) { - Log.w(TAG, "Bluetooth service not available."); - return; - } - - mLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); - - if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) - Log.w(TAG, "Device does not support Bluetooth Low Energy advertisement."); - else - Log.w(TAG, "Let's do BTLE Peripheral."); - } - - /* - * Call back handler for the Gatt Server. - */ - private BluetoothGattServerCallback mGattServerListener = new BluetoothGattServerCallback() - { - @Override - public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { - Log.w(TAG, "Our gatt server connection state changed, new state: " + newState + " " + status); - super.onConnectionStateChange(device, status, newState); - - int qtControllerState = 0; - switch (newState) { - case BluetoothProfile.STATE_DISCONNECTED: - qtControllerState = 0; // QLowEnergyController::UnconnectedState - clientCharacteristicManager.markDeviceConnectivity(device, false); - mGattServer.close(); - break; - case BluetoothProfile.STATE_CONNECTED: - clientCharacteristicManager.markDeviceConnectivity(device, true); - qtControllerState = 2; // QLowEnergyController::ConnectedState - break; - } - - mRemoteName = device.getName(); - mRemoteAddress = device.getAddress(); - - int qtErrorCode; - switch (status) { - case BluetoothGatt.GATT_SUCCESS: - qtErrorCode = 0; break; - default: - Log.w(TAG, "Unhandled error code on peripheral connectionStateChanged: " + status + " " + newState); - qtErrorCode = status; - break; - } - - leServerConnectionStateChange(qtObject, qtErrorCode, qtControllerState); - } - - @Override - public void onServiceAdded(int status, BluetoothGattService service) { - super.onServiceAdded(status, service); - } - - @Override - public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) - { - byte[] dataArray; - try { - dataArray = Arrays.copyOfRange(characteristic.getValue(), offset, characteristic.getValue().length); - mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, dataArray); - } catch (Exception ex) { - Log.w(TAG, "onCharacteristicReadRequest: " + requestId + " " + offset + " " + characteristic.getValue().length); - ex.printStackTrace(); - mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, offset, null); - } - - super.onCharacteristicReadRequest(device, requestId, offset, characteristic); - } - - @Override - public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) - { - Log.w(TAG, "onCharacteristicWriteRequest"); - int resultStatus = BluetoothGatt.GATT_SUCCESS; - boolean sendNotificationOrIndication = false; - if (!preparedWrite) { // regular write - if (offset == 0) { - characteristic.setValue(value); - leServerCharacteristicChanged(qtObject, characteristic, value); - sendNotificationOrIndication = true; - } else { - // This should not really happen as per Bluetooth spec - Log.w(TAG, "onCharacteristicWriteRequest: !preparedWrite, offset " + offset + ", Not supported"); - resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED; - } - - - } else { - Log.w(TAG, "onCharacteristicWriteRequest: preparedWrite, offset " + offset + ", Not supported"); - resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED; - - // TODO we need to record all requests and execute them in one go once onExecuteWrite() is received - // we use a queue to remember the pending requests - // TODO we are ignoring the device identificator for now -> Bluetooth spec requires a queue per device - } - - - if (responseNeeded) - mGattServer.sendResponse(device, requestId, resultStatus, offset, value); - if (sendNotificationOrIndication) - sendNotificationsOrIndications(characteristic); - - super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value); - } - - @Override - public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) - { - byte[] dataArray = descriptor.getValue(); - try { - if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) { - dataArray = clientCharacteristicManager.valueFor(descriptor.getCharacteristic(), device); - if (dataArray == null) - dataArray = descriptor.getValue(); - } - - dataArray = Arrays.copyOfRange(dataArray, offset, dataArray.length); - mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, dataArray); - } catch (Exception ex) { - Log.w(TAG, "onDescriptorReadRequest: " + requestId + " " + offset + " " + dataArray.length); - ex.printStackTrace(); - mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, offset, null); - } - - super.onDescriptorReadRequest(device, requestId, offset, descriptor); - } - - @Override - public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, - boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) - { - int resultStatus = BluetoothGatt.GATT_SUCCESS; - if (!preparedWrite) { // regular write - if (offset == 0) { - descriptor.setValue(value); - - if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) { - clientCharacteristicManager.insertOrUpdate(descriptor.getCharacteristic(), - device, value); - } - - leServerDescriptorWritten(qtObject, descriptor, value); - } else { - // This should not really happen as per Bluetooth spec - Log.w(TAG, "onDescriptorWriteRequest: !preparedWrite, offset " + offset + ", Not supported"); - resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED; - } - - - } else { - Log.w(TAG, "onDescriptorWriteRequest: preparedWrite, offset " + offset + ", Not supported"); - resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED; - // TODO we need to record all requests and execute them in one go once onExecuteWrite() is received - // we use a queue to remember the pending requests - // TODO we are ignoring the device identificator for now -> Bluetooth spec requires a queue per device - } - - - if (responseNeeded) - mGattServer.sendResponse(device, requestId, resultStatus, offset, value); - - super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value); - } - - @Override - public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) - { - // TODO not yet implemented -> return proper GATT error for it - mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED, 0, null); - - super.onExecuteWrite(device, requestId, execute); - } - - @Override - public void onNotificationSent(BluetoothDevice device, int status) { - super.onNotificationSent(device, status); - Log.w(TAG, "onNotificationSent" + device + " " + status); - } - - // MTU change disabled since it requires API level 22. Right now we only enforce lvl 21 -// @Override -// public void onMtuChanged(BluetoothDevice device, int mtu) { -// super.onMtuChanged(device, mtu); -// } - }; - - public boolean connectServer() - { - if (mGattServer != null) - return true; - - BluetoothManager manager = (BluetoothManager) qtContext.getSystemService(Context.BLUETOOTH_SERVICE); - if (manager == null) { - Log.w(TAG, "Bluetooth service not available."); - return false; - } - - mGattServer = manager.openGattServer(qtContext, mGattServerListener); - - return (mGattServer != null); - } - - public void disconnectServer() - { - if (mGattServer == null) - return; - - mGattServer.close(); - mGattServer = null; - - mRemoteName = mRemoteAddress = ""; - leServerConnectionStateChange(qtObject, 0 /*NoError*/, 0 /*QLowEnergyController::UnconnectedState*/); - } - - public boolean startAdvertising(AdvertiseData advertiseData, - AdvertiseData scanResponse, - AdvertiseSettings settings) - { - if (mLeAdvertiser == null) - return false; - - if (!connectServer()) { - Log.w(TAG, "Server::startAdvertising: Cannot open GATT server"); - return false; - } - - Log.w(TAG, "Starting to advertise."); - mLeAdvertiser.startAdvertising(settings, advertiseData, scanResponse, mAdvertiseListener); - - return true; - } - - public void stopAdvertising() - { - if (mLeAdvertiser == null) - return; - - mLeAdvertiser.stopAdvertising(mAdvertiseListener); - Log.w(TAG, "Advertisement stopped."); - } - - public void addService(BluetoothGattService service) - { - if (!connectServer()) { - Log.w(TAG, "Server::addService: Cannot open GATT server"); - return; - } - - mGattServer.addService(service); - } - - /* - Check the client characteristics configuration for the given characteristic - and sends notifications or indications as per required. - */ - private void sendNotificationsOrIndications(BluetoothGattCharacteristic characteristic) - { - final ListIterator<BluetoothDevice> iter = - clientCharacteristicManager.getToBeUpdatedDevices(characteristic).listIterator(); - - // TODO This quick loop over multiple devices should be synced with onNotificationSent(). - // The next notifyCharacteristicChanged() call must wait until onNotificationSent() - // was received. At this becomes an issue when the server accepts multiple remote - // devices at the same time. - while (iter.hasNext()) { - final BluetoothDevice device = iter.next(); - final byte[] clientCharacteristicConfig = clientCharacteristicManager.valueFor(characteristic, device); - if (clientCharacteristicConfig != null) { - if (Arrays.equals(clientCharacteristicConfig, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) { - mGattServer.notifyCharacteristicChanged(device, characteristic, false); - } else if (Arrays.equals(clientCharacteristicConfig, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) { - mGattServer.notifyCharacteristicChanged(device, characteristic, true); - } - } - } - } - - /* - Updates the local database value for the given characteristic with \a charUuid and - \a newValue. If notifications for this task are enabled an approproiate notification will - be send to the remote client. - - This function is called from the Qt thread. - */ - public boolean writeCharacteristic(BluetoothGattService service, UUID charUuid, byte[] newValue) - { - BluetoothGattCharacteristic foundChar = null; - List<BluetoothGattCharacteristic> charList = service.getCharacteristics(); - for (BluetoothGattCharacteristic iter: charList) { - if (iter.getUuid().equals(charUuid) && foundChar == null) { - foundChar = iter; - // don't break here since we want to check next condition below on next iteration - } else if (iter.getUuid().equals(charUuid)) { - Log.w(TAG, "Found second char with same UUID. Wrong char may have been selected."); - break; - } - } - - if (foundChar == null) { - Log.w(TAG, "writeCharacteristic: update for unknown characteristic failed"); - return false; - } - - foundChar.setValue(newValue); - sendNotificationsOrIndications(foundChar); - - return true; - } - - /* - Updates the local database value for the given \a descUuid to \a newValue. - - This function is called from the Qt thread. - */ - public boolean writeDescriptor(BluetoothGattService service, UUID charUuid, UUID descUuid, - byte[] newValue) - { - BluetoothGattDescriptor foundDesc = null; - BluetoothGattCharacteristic foundChar = null; - final List<BluetoothGattCharacteristic> charList = service.getCharacteristics(); - for (BluetoothGattCharacteristic iter: charList) { - if (!iter.getUuid().equals(charUuid)) - continue; - - if (foundChar == null) { - foundChar = iter; - } else { - Log.w(TAG, "Found second char with same UUID. Wrong char may have been selected."); - break; - } - } - - if (foundChar != null) - foundDesc = foundChar.getDescriptor(descUuid); - - if (foundChar == null || foundDesc == null) { - Log.w(TAG, "writeDescriptor: update for unknown char or desc failed (" + foundChar + ")"); - return false; - } - - // we even write CLIENT_CHARACTERISTIC_CONFIGURATION_UUID this way as we choose - // to interpret the server's call as a change of the default value. - foundDesc.setValue(newValue); - - return true; - } - - /* - * Call back handler for Advertisement requests. - */ - private AdvertiseCallback mAdvertiseListener = new AdvertiseCallback() - { - @Override - public void onStartSuccess(AdvertiseSettings settingsInEffect) { - super.onStartSuccess(settingsInEffect); - } - - @Override - public void onStartFailure(int errorCode) { - Log.e(TAG, "Advertising failure: " + errorCode); - super.onStartFailure(errorCode); - - // changing errorCode here implies changes to errorCode handling on Qt side - int qtErrorCode = 0; - switch (errorCode) { - case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED: - return; // ignore -> noop - case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE: - qtErrorCode = 1; - break; - case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED: - qtErrorCode = 2; - break; - default: // default maps to internal error - case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR: - qtErrorCode = 3; - break; - case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS: - qtErrorCode = 4; - break; - } - - if (qtErrorCode > 0) - leServerAdvertisementError(qtObject, qtErrorCode); - } - }; - - public native void leServerConnectionStateChange(long qtObject, int errorCode, int newState); - public native void leServerAdvertisementError(long qtObject, int status); - public native void leServerCharacteristicChanged(long qtObject, - BluetoothGattCharacteristic characteristic, - byte[] newValue); - public native void leServerDescriptorWritten(long qtObject, - BluetoothGattDescriptor descriptor, - byte[] newValue); -} |