blob: 71951027c8a9dc4ba0a373020b67556f98ed18f8 [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package io.v.android.impl.google.discovery.plugins.ble;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* A server that serves Vanadium Gatt services to remote Gatt readers.
*/
class GattServer extends BluetoothGattServerCallback {
// L2CAP implementations shall support a minimum MTU size of 23 octets.
// See Bluetooth specification version 4.2 section 5.1.
private static final int DEFAULT_MTU = 23;
private final BluetoothGattServer mBluetoothGattServer;
private final Map<BluetoothDevice, Integer> mMtus;
GattServer(Context context) {
BluetoothManager manager =
((BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE));
mBluetoothGattServer = manager.openGattServer(context, this);
mMtus = new HashMap<BluetoothDevice, Integer>();
}
/**
* Add a service to the Gatt server.
*/
boolean addService(BluetoothGattService service) {
return mBluetoothGattServer.addService(service);
}
/**
* Removes a service from the Gatt server.
*/
boolean removeService(BluetoothGattService service) {
return mBluetoothGattServer.removeService(service);
}
/**
* Closes the Gatt server removing all services.
*/
void close() {
mBluetoothGattServer.clearServices();
mBluetoothGattServer.close();
}
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
if (status != BluetoothGatt.GATT_SUCCESS || newState != BluetoothProfile.STATE_CONNECTED) {
mMtus.remove(device);
}
}
@Override
public void onMtuChanged(BluetoothDevice device, int mtu) {
super.onMtuChanged(device, mtu);
mMtus.put(device, mtu);
}
@Override
public void onCharacteristicReadRequest(
BluetoothDevice device,
int requestId,
int offset,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
byte[] data = characteristic.getValue();
if (offset >= data.length) {
data = null;
} else {
// MTU exchange is not allowed on a BR/EDR physical link. Send all requested data in that link.
int deviceType = device.getType();
if (deviceType == BluetoothDevice.DEVICE_TYPE_CLASSIC
|| deviceType == BluetoothDevice.DEVICE_TYPE_DUAL) {
if (offset > 0) {
data = Arrays.copyOfRange(data, offset, data.length);
}
} else {
int mtu = DEFAULT_MTU;
if (mMtus.containsKey(device)) {
mtu = mMtus.get(device);
}
// We can send data up to MTU - 1 bytes.
int to = offset + mtu - 1;
if (to > data.length) {
to = data.length;
}
if (offset > 0 || to < data.length) {
data = Arrays.copyOfRange(data, offset, to);
}
}
}
mBluetoothGattServer.sendResponse(
device, requestId, BluetoothGatt.GATT_SUCCESS, offset, data);
}
}