blob: 71000a53dabc44c8bd00b1c5c2236670b6da54c7 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.bluetooth.gatt;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothGattCallback;
import android.bluetooth.IBluetoothGattServerCallback;
import android.content.Intent;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;
import com.android.bluetooth.btservice.ProfileService;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Provides Bluetooth Gatt profile, as a service in
* the Bluetooth application.
* @hide
*/
public class GattService extends ProfileService {
private static final boolean DBG = GattServiceConfig.DBG;
private static final String TAG = GattServiceConfig.TAG_PREFIX + "GattService";
private static final int DEFAULT_SCAN_INTERVAL_MILLIS = 200;
/**
* Max packet size for ble advertising, defined in Bluetooth Specification Version 4.0 [Vol 3].
*/
private static final int ADVERTISING_PACKET_MAX_BYTES = 31;
/**
* Size overhead for advertising flag.
*/
private static final int ADVERTISING_FLAGS_BYTES = 3;
/**
* Size overhead per field. Usually it's just one byte of field length and one byte of
* field type.
*/
private static final int FIELD_OVERHEAD_BYTES = 2;
/**
* Byte size of 16 bit service uuid.
*/
private static final int SHORT_UUID_BYTES = 2;
/**
* Byte size of 128 bit service uuid.
*/
private static final int FULL_UUID_BYTES = 16;
/**
* Search queue to serialize remote onbject inspection.
*/
SearchQueue mSearchQueue = new SearchQueue();
/**
* List of our registered clients.
*/
class ClientMap extends ContextMap<IBluetoothGattCallback> {}
ClientMap mClientMap = new ClientMap();
/**
* List of our registered server apps.
*/
class ServerMap extends ContextMap<IBluetoothGattServerCallback> {}
ServerMap mServerMap = new ServerMap();
/**
* Server handle map.
*/
HandleMap mHandleMap = new HandleMap();
private List<UUID> mAdvertisingServiceUuids = new ArrayList<UUID>();
private int mAdvertisingClientIf = 0;
private byte[] mServiceData = new byte[0];
private int mManufacturerCode = -1;
private byte[] mManufacturerData = new byte[0];
private Integer mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STOPPED;
private final Object mLock = new Object();
/**
* Pending service declaration queue
*/
private List<ServiceDeclaration> mServiceDeclarations = new ArrayList<ServiceDeclaration>();
private ServiceDeclaration addDeclaration() {
synchronized (mServiceDeclarations) {
mServiceDeclarations.add(new ServiceDeclaration());
}
return getActiveDeclaration();
}
private ServiceDeclaration getActiveDeclaration() {
synchronized (mServiceDeclarations) {
if (mServiceDeclarations.size() > 0)
return mServiceDeclarations.get(mServiceDeclarations.size() - 1);
}
return null;
}
private ServiceDeclaration getPendingDeclaration() {
synchronized (mServiceDeclarations) {
if (mServiceDeclarations.size() > 0)
return mServiceDeclarations.get(0);
}
return null;
}
private void removePendingDeclaration() {
synchronized (mServiceDeclarations) {
if (mServiceDeclarations.size() > 0)
mServiceDeclarations.remove(0);
}
}
/**
* List of clients interested in scan results.
*/
private List<ScanClient> mScanQueue = new ArrayList<ScanClient>();
private ScanClient getScanClient(int appIf, boolean isServer) {
for(ScanClient client : mScanQueue) {
if (client.appIf == appIf && client.isServer == isServer) {
return client;
}
}
return null;
}
private void removeScanClient(int appIf, boolean isServer) {
for(ScanClient client : mScanQueue) {
if (client.appIf == appIf && client.isServer == isServer) {
mScanQueue.remove(client);
break;
}
}
}
/**
* Reliable write queue
*/
private Set<String> mReliableQueue = new HashSet<String>();
static {
classInitNative();
}
protected String getName() {
return TAG;
}
protected IProfileServiceBinder initBinder() {
return new BluetoothGattBinder(this);
}
protected boolean start() {
if (DBG) Log.d(TAG, "start()");
initializeNative();
return true;
}
protected boolean stop() {
if (DBG) Log.d(TAG, "stop()");
mClientMap.clear();
mServerMap.clear();
mSearchQueue.clear();
mScanQueue.clear();
mHandleMap.clear();
mServiceDeclarations.clear();
mReliableQueue.clear();
return true;
}
protected boolean cleanup() {
if (DBG) Log.d(TAG, "cleanup()");
cleanupNative();
return true;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (GattDebugUtils.handleDebugAction(this, intent)) {
return Service.START_NOT_STICKY;
}
return super.onStartCommand(intent, flags, startId);
}
/**
* DeathReceipient handlers used to unregister applications that
* disconnect ungracefully (ie. crash or forced close).
*/
class ClientDeathRecipient implements IBinder.DeathRecipient {
int mAppIf;
public ClientDeathRecipient(int appIf) {
mAppIf = appIf;
}
public void binderDied() {
if (DBG) Log.d(TAG, "Binder is dead - unregistering client (" + mAppIf + ")!");
if (mAdvertisingClientIf == mAppIf) {
stopAdvertising(true); // force stop advertising.
} else {
stopScan(mAppIf, false);
}
unregisterClient(mAppIf);
}
}
class ServerDeathRecipient implements IBinder.DeathRecipient {
int mAppIf;
public ServerDeathRecipient(int appIf) {
mAppIf = appIf;
}
public void binderDied() {
if (DBG) Log.d(TAG, "Binder is dead - unregistering server (" + mAppIf + ")!");
unregisterServer(mAppIf);
}
}
/**
* Handlers for incoming service calls
*/
private static class BluetoothGattBinder extends IBluetoothGatt.Stub implements IProfileServiceBinder {
private GattService mService;
public BluetoothGattBinder(GattService svc) {
mService = svc;
}
public boolean cleanup() {
mService = null;
return true;
}
private GattService getService() {
if (mService != null && mService.isAvailable()) return mService;
Log.e(TAG, "getService() - Service requested, but not available!");
return null;
}
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
GattService service = getService();
if (service == null) return new ArrayList<BluetoothDevice>();
return service.getDevicesMatchingConnectionStates(states);
}
public void registerClient(ParcelUuid uuid, IBluetoothGattCallback callback) {
GattService service = getService();
if (service == null) return;
service.registerClient(uuid.getUuid(), callback);
}
public void unregisterClient(int clientIf) {
GattService service = getService();
if (service == null) return;
service.unregisterClient(clientIf);
}
public void startScan(int appIf, boolean isServer) {
GattService service = getService();
if (service == null) return;
service.startScan(appIf, isServer);
}
public void startScanWithUuids(int appIf, boolean isServer, ParcelUuid[] ids) {
GattService service = getService();
if (service == null) return;
UUID[] uuids = new UUID[ids.length];
for(int i = 0; i != ids.length; ++i) {
uuids[i] = ids[i].getUuid();
}
service.startScanWithUuids(appIf, isServer, uuids);
}
public void stopScan(int appIf, boolean isServer) {
GattService service = getService();
if (service == null) return;
service.stopScan(appIf, isServer);
}
public void clientConnect(int clientIf, String address, boolean isDirect) {
GattService service = getService();
if (service == null) return;
service.clientConnect(clientIf, address, isDirect);
}
public void clientDisconnect(int clientIf, String address) {
GattService service = getService();
if (service == null) return;
service.clientDisconnect(clientIf, address);
}
public void refreshDevice(int clientIf, String address) {
GattService service = getService();
if (service == null) return;
service.refreshDevice(clientIf, address);
}
public void discoverServices(int clientIf, String address) {
GattService service = getService();
if (service == null) return;
service.discoverServices(clientIf, address);
}
public void readCharacteristic(int clientIf, String address, int srvcType,
int srvcInstanceId, ParcelUuid srvcId,
int charInstanceId, ParcelUuid charId,
int authReq) {
GattService service = getService();
if (service == null) return;
service.readCharacteristic(clientIf, address, srvcType, srvcInstanceId,
srvcId.getUuid(), charInstanceId,
charId.getUuid(), authReq);
}
public void writeCharacteristic(int clientIf, String address, int srvcType,
int srvcInstanceId, ParcelUuid srvcId,
int charInstanceId, ParcelUuid charId,
int writeType, int authReq, byte[] value) {
GattService service = getService();
if (service == null) return;
service.writeCharacteristic(clientIf, address, srvcType, srvcInstanceId,
srvcId.getUuid(), charInstanceId,
charId.getUuid(), writeType, authReq,
value);
}
public void readDescriptor(int clientIf, String address, int srvcType,
int srvcInstanceId, ParcelUuid srvcId,
int charInstanceId, ParcelUuid charId,
int descrInstanceId, ParcelUuid descrId,
int authReq) {
GattService service = getService();
if (service == null) return;
service.readDescriptor(clientIf, address, srvcType,
srvcInstanceId, srvcId.getUuid(),
charInstanceId, charId.getUuid(),
descrInstanceId, descrId.getUuid(),
authReq);
}
public void writeDescriptor(int clientIf, String address, int srvcType,
int srvcInstanceId, ParcelUuid srvcId,
int charInstanceId, ParcelUuid charId,
int descrInstanceId, ParcelUuid descrId,
int writeType, int authReq, byte[] value) {
GattService service = getService();
if (service == null) return;
service.writeDescriptor(clientIf, address, srvcType,
srvcInstanceId, srvcId.getUuid(),
charInstanceId, charId.getUuid(),
descrInstanceId, descrId.getUuid(),
writeType, authReq, value);
}
public void beginReliableWrite(int clientIf, String address) {
GattService service = getService();
if (service == null) return;
service.beginReliableWrite(clientIf, address);
}
public void endReliableWrite(int clientIf, String address, boolean execute) {
GattService service = getService();
if (service == null) return;
service.endReliableWrite(clientIf, address, execute);
}
public void registerForNotification(int clientIf, String address, int srvcType,
int srvcInstanceId, ParcelUuid srvcId,
int charInstanceId, ParcelUuid charId,
boolean enable) {
GattService service = getService();
if (service == null) return;
service.registerForNotification(clientIf, address, srvcType, srvcInstanceId,
srvcId.getUuid(), charInstanceId,
charId.getUuid(), enable);
}
public void readRemoteRssi(int clientIf, String address) {
GattService service = getService();
if (service == null) return;
service.readRemoteRssi(clientIf, address);
}
public void registerServer(ParcelUuid uuid, IBluetoothGattServerCallback callback) {
GattService service = getService();
if (service == null) return;
service.registerServer(uuid.getUuid(), callback);
}
public void unregisterServer(int serverIf) {
GattService service = getService();
if (service == null) return;
service.unregisterServer(serverIf);
}
public void serverConnect(int serverIf, String address, boolean isDirect) {
GattService service = getService();
if (service == null) return;
service.serverConnect(serverIf, address, isDirect);
}
public void serverDisconnect(int serverIf, String address) {
GattService service = getService();
if (service == null) return;
service.serverDisconnect(serverIf, address);
}
public void beginServiceDeclaration(int serverIf, int srvcType,
int srvcInstanceId, int minHandles,
ParcelUuid srvcId, boolean advertisePreferred) {
GattService service = getService();
if (service == null) return;
service.beginServiceDeclaration(serverIf, srvcType, srvcInstanceId,
minHandles, srvcId.getUuid(), advertisePreferred);
}
public void addIncludedService(int serverIf, int srvcType,
int srvcInstanceId, ParcelUuid srvcId) {
GattService service = getService();
if (service == null) return;
service.addIncludedService(serverIf, srvcType, srvcInstanceId,
srvcId.getUuid());
}
public void addCharacteristic(int serverIf, ParcelUuid charId,
int properties, int permissions) {
GattService service = getService();
if (service == null) return;
service.addCharacteristic(serverIf, charId.getUuid(), properties,
permissions);
}
public void addDescriptor(int serverIf, ParcelUuid descId,
int permissions) {
GattService service = getService();
if (service == null) return;
service.addDescriptor(serverIf, descId.getUuid(), permissions);
}
public void endServiceDeclaration(int serverIf) {
GattService service = getService();
if (service == null) return;
service.endServiceDeclaration(serverIf);
}
public void removeService(int serverIf, int srvcType,
int srvcInstanceId, ParcelUuid srvcId) {
GattService service = getService();
if (service == null) return;
service.removeService(serverIf, srvcType, srvcInstanceId,
srvcId.getUuid());
}
public void clearServices(int serverIf) {
GattService service = getService();
if (service == null) return;
service.clearServices(serverIf);
}
public void sendResponse(int serverIf, String address, int requestId,
int status, int offset, byte[] value) {
GattService service = getService();
if (service == null) return;
service.sendResponse(serverIf, address, requestId, status, offset, value);
}
public void sendNotification(int serverIf, String address, int srvcType,
int srvcInstanceId, ParcelUuid srvcId,
int charInstanceId, ParcelUuid charId,
boolean confirm, byte[] value) {
GattService service = getService();
if (service == null) return;
service.sendNotification(serverIf, address, srvcType, srvcInstanceId,
srvcId.getUuid(), charInstanceId, charId.getUuid(), confirm, value);
}
@Override
public void startAdvertising(int appIf) throws RemoteException {
GattService service = getService();
if (service == null) return;
service.startAdvertising(appIf);
}
@Override
public boolean isAdvertising() {
GattService service = getService();
if (service == null) return false;
return service.isAdvertising();
}
@Override
public void stopAdvertising() throws RemoteException {
GattService service = getService();
if (service == null) return;
service.stopAdvertising();
}
@Override
public boolean setAdvServiceData(byte[] serviceData) throws RemoteException {
GattService service = getService();
if (service == null) return false;
return service.setAdvServiceData(serviceData);
}
@Override
public byte[] getAdvServiceData() throws RemoteException {
GattService service = getService();
if (service == null) return null;
return service.getAdvServiceData();
}
@Override
public boolean setAdvManufacturerCodeAndData(int manufactureCode, byte[] manufacturerData)
throws RemoteException {
GattService service = getService();
if (service == null) return false;
return service.setAdvManufacturerCodeAndData(manufactureCode, manufacturerData);
}
@Override
public byte[] getAdvManufacturerData() throws RemoteException {
GattService service = getService();
if (service == null) return null;
return service.getAdvManufacturerData();
}
@Override
public List<ParcelUuid> getAdvServiceUuids() throws RemoteException {
GattService service = getService();
if (service == null) return null;
return service.getAdvServiceUuids();
}
@Override
public void removeAdvManufacturerCodeAndData(int manufacturerCode) throws RemoteException {
GattService service = getService();
if (service == null) return;
service.removeAdvManufacturerCodeAndData(manufacturerCode);
}
};
/**************************************************************************
* Callback functions - CLIENT
*************************************************************************/
void onScanResult(String address, int rssi, byte[] adv_data) {
if (DBG) Log.d(TAG, "onScanResult() - address=" + address
+ ", rssi=" + rssi);
List<UUID> remoteUuids = parseUuids(adv_data);
for (ScanClient client : mScanQueue) {
if (client.uuids.length > 0) {
int matches = 0;
for (UUID search : client.uuids) {
for (UUID remote: remoteUuids) {
if (remote.equals(search)) {
++matches;
break; // Only count 1st match in case of duplicates
}
}
}
if (matches < client.uuids.length) continue;
}
if (!client.isServer) {
ClientMap.App app = mClientMap.getById(client.appIf);
if (app != null) {
try {
app.callback.onScanResult(address, rssi, adv_data);
} catch (RemoteException e) {
Log.e(TAG, "Exception: " + e);
mClientMap.remove(client.appIf);
mScanQueue.remove(client);
}
}
} else {
ServerMap.App app = mServerMap.getById(client.appIf);
if (app != null) {
try {
app.callback.onScanResult(address, rssi, adv_data);
} catch (RemoteException e) {
Log.e(TAG, "Exception: " + e);
mServerMap.remove(client.appIf);
mScanQueue.remove(client);
}
}
}
}
}
void onClientRegistered(int status, int clientIf, long uuidLsb, long uuidMsb)
throws RemoteException {
UUID uuid = new UUID(uuidMsb, uuidLsb);
if (DBG) Log.d(TAG, "onClientRegistered() - UUID=" + uuid + ", clientIf=" + clientIf);
ClientMap.App app = mClientMap.getByUuid(uuid);
if (app != null) {
app.id = clientIf;
app.linkToDeath(new ClientDeathRecipient(clientIf));
app.callback.onClientRegistered(status, clientIf);
}
}
void onConnected(int clientIf, int connId, int status, String address)
throws RemoteException {
if (DBG) Log.d(TAG, "onConnected() - clientIf=" + clientIf
+ ", connId=" + connId + ", address=" + address);
if (status == 0) mClientMap.addConnection(clientIf, connId, address);
ClientMap.App app = mClientMap.getById(clientIf);
if (app != null) {
app.callback.onClientConnectionState(status, clientIf, true, address);
}
}
void onDisconnected(int clientIf, int connId, int status, String address)
throws RemoteException {
if (DBG) Log.d(TAG, "onDisconnected() - clientIf=" + clientIf
+ ", connId=" + connId + ", address=" + address);
mClientMap.removeConnection(clientIf, connId);
mSearchQueue.removeConnId(connId);
ClientMap.App app = mClientMap.getById(clientIf);
if (app != null) {
app.callback.onClientConnectionState(status, clientIf, false, address);
}
}
void onSearchCompleted(int connId, int status) throws RemoteException {
if (DBG) Log.d(TAG, "onSearchCompleted() - connId=" + connId+ ", status=" + status);
// We got all services, now let's explore characteristics...
continueSearch(connId, status);
}
void onSearchResult(int connId, int srvcType,
int srvcInstId, long srvcUuidLsb, long srvcUuidMsb)
throws RemoteException {
UUID uuid = new UUID(srvcUuidMsb, srvcUuidLsb);
String address = mClientMap.addressByConnId(connId);
if (DBG) Log.d(TAG, "onSearchResult() - address=" + address + ", uuid=" + uuid);
mSearchQueue.add(connId, srvcType, srvcInstId, srvcUuidLsb, srvcUuidMsb);
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onGetService(address, srvcType, srvcInstId,
new ParcelUuid(uuid));
}
}
void onGetCharacteristic(int connId, int status, int srvcType,
int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
int charInstId, long charUuidLsb, long charUuidMsb,
int charProp) throws RemoteException {
UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
String address = mClientMap.addressByConnId(connId);
if (DBG) Log.d(TAG, "onGetCharacteristic() - address=" + address
+ ", status=" + status + ", charUuid=" + charUuid + ", prop=" + charProp);
if (status == 0) {
mSearchQueue.add(connId, srvcType,
srvcInstId, srvcUuidLsb, srvcUuidMsb,
charInstId, charUuidLsb, charUuidMsb);
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onGetCharacteristic(address, srvcType,
srvcInstId, new ParcelUuid(srvcUuid),
charInstId, new ParcelUuid(charUuid), charProp);
}
// Get next characteristic in the current service
gattClientGetCharacteristicNative(connId, srvcType,
srvcInstId, srvcUuidLsb, srvcUuidMsb,
charInstId, charUuidLsb, charUuidMsb);
} else {
// Check for included services next
gattClientGetIncludedServiceNative(connId,
srvcType, srvcInstId, srvcUuidLsb, srvcUuidMsb,
0,0,0,0);
}
}
void onGetDescriptor(int connId, int status, int srvcType,
int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
int charInstId, long charUuidLsb, long charUuidMsb,
int descrInstId, long descrUuidLsb, long descrUuidMsb) throws RemoteException {
UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
UUID descUuid = new UUID(descrUuidMsb, descrUuidLsb);
String address = mClientMap.addressByConnId(connId);
if (DBG) Log.d(TAG, "onGetDescriptor() - address=" + address
+ ", status=" + status + ", descUuid=" + descUuid);
if (status == 0) {
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onGetDescriptor(address, srvcType,
srvcInstId, new ParcelUuid(srvcUuid),
charInstId, new ParcelUuid(charUuid),
descrInstId, new ParcelUuid(descUuid));
}
// Get next descriptor for the current characteristic
gattClientGetDescriptorNative(connId, srvcType,
srvcInstId, srvcUuidLsb, srvcUuidMsb,
charInstId, charUuidLsb, charUuidMsb,
descrInstId, descrUuidLsb, descrUuidMsb);
} else {
// Explore the next service
continueSearch(connId, 0);
}
}
void onGetIncludedService(int connId, int status, int srvcType,
int srvcInstId, long srvcUuidLsb, long srvcUuidMsb, int inclSrvcType,
int inclSrvcInstId, long inclSrvcUuidLsb, long inclSrvcUuidMsb)
throws RemoteException {
UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
UUID inclSrvcUuid = new UUID(inclSrvcUuidMsb, inclSrvcUuidLsb);
String address = mClientMap.addressByConnId(connId);
if (DBG) Log.d(TAG, "onGetIncludedService() - address=" + address
+ ", status=" + status + ", uuid=" + srvcUuid
+ ", inclUuid=" + inclSrvcUuid);
if (status == 0) {
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onGetIncludedService(address,
srvcType, srvcInstId, new ParcelUuid(srvcUuid),
inclSrvcType, inclSrvcInstId, new ParcelUuid(inclSrvcUuid));
}
// Find additional included services
gattClientGetIncludedServiceNative(connId,
srvcType, srvcInstId, srvcUuidLsb, srvcUuidMsb,
inclSrvcType, inclSrvcInstId, inclSrvcUuidLsb, inclSrvcUuidMsb);
} else {
// Discover descriptors now
continueSearch(connId, 0);
}
}
void onRegisterForNotifications(int connId, int status, int registered, int srvcType,
int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
int charInstId, long charUuidLsb, long charUuidMsb) {
UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
String address = mClientMap.addressByConnId(connId);
if (DBG) Log.d(TAG, "onRegisterForNotifications() - address=" + address
+ ", status=" + status + ", registered=" + registered
+ ", charUuid=" + charUuid);
}
void onNotify(int connId, String address, int srvcType,
int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
int charInstId, long charUuidLsb, long charUuidMsb,
boolean isNotify, byte[] data) throws RemoteException {
UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
if (DBG) Log.d(TAG, "onNotify() - address=" + address
+ ", charUuid=" + charUuid + ", length=" + data.length);
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onNotify(address, srvcType,
srvcInstId, new ParcelUuid(srvcUuid),
charInstId, new ParcelUuid(charUuid),
data);
}
}
void onReadCharacteristic(int connId, int status, int srvcType,
int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
int charInstId, long charUuidLsb, long charUuidMsb,
int charType, byte[] data) throws RemoteException {
UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
String address = mClientMap.addressByConnId(connId);
if (DBG) Log.d(TAG, "onReadCharacteristic() - address=" + address
+ ", status=" + status + ", length=" + data.length);
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onCharacteristicRead(address, status, srvcType,
srvcInstId, new ParcelUuid(srvcUuid),
charInstId, new ParcelUuid(charUuid), data);
}
}
void onWriteCharacteristic(int connId, int status, int srvcType,
int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
int charInstId, long charUuidLsb, long charUuidMsb)
throws RemoteException {
UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
String address = mClientMap.addressByConnId(connId);
if (DBG) Log.d(TAG, "onWriteCharacteristic() - address=" + address
+ ", status=" + status);
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onCharacteristicWrite(address, status, srvcType,
srvcInstId, new ParcelUuid(srvcUuid),
charInstId, new ParcelUuid(charUuid));
}
}
void onExecuteCompleted(int connId, int status) throws RemoteException {
String address = mClientMap.addressByConnId(connId);
if (DBG) Log.d(TAG, "onExecuteCompleted() - address=" + address
+ ", status=" + status);
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onExecuteWrite(address, status);
}
}
void onReadDescriptor(int connId, int status, int srvcType,
int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
int charInstId, long charUuidLsb, long charUuidMsb,
int descrInstId, long descrUuidLsb, long descrUuidMsb,
int charType, byte[] data) throws RemoteException {
UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
UUID descrUuid = new UUID(descrUuidMsb, descrUuidLsb);
String address = mClientMap.addressByConnId(connId);
if (DBG) Log.d(TAG, "onReadDescriptor() - address=" + address
+ ", status=" + status + ", length=" + data.length);
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onDescriptorRead(address, status, srvcType,
srvcInstId, new ParcelUuid(srvcUuid),
charInstId, new ParcelUuid(charUuid),
descrInstId, new ParcelUuid(descrUuid), data);
}
}
void onWriteDescriptor(int connId, int status, int srvcType,
int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
int charInstId, long charUuidLsb, long charUuidMsb,
int descrInstId, long descrUuidLsb, long descrUuidMsb) throws RemoteException {
UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
UUID descrUuid = new UUID(descrUuidMsb, descrUuidLsb);
String address = mClientMap.addressByConnId(connId);
if (DBG) Log.d(TAG, "onWriteDescriptor() - address=" + address
+ ", status=" + status);
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onDescriptorWrite(address, status, srvcType,
srvcInstId, new ParcelUuid(srvcUuid),
charInstId, new ParcelUuid(charUuid),
descrInstId, new ParcelUuid(descrUuid));
}
}
void onReadRemoteRssi(int clientIf, String address,
int rssi, int status) throws RemoteException{
if (DBG) Log.d(TAG, "onReadRemoteRssi() - clientIf=" + clientIf + " address=" +
address + ", rssi=" + rssi + ", status=" + status);
ClientMap.App app = mClientMap.getById(clientIf);
if (app != null) {
app.callback.onReadRemoteRssi(address, rssi, status);
}
}
void onAdvertiseCallback(int status, int clientIf) throws RemoteException {
if (DBG) Log.d(TAG, "onClientListen() status=" + status);
synchronized (mLock) {
if (DBG) Log.d(TAG, "state" + mAdvertisingState);
// Invalid advertising state
if (mAdvertisingState == BluetoothAdapter.STATE_ADVERTISE_STARTED ||
mAdvertisingState == BluetoothAdapter.STATE_ADVERTISE_STOPPED) {
Log.e(TAG, "invalid callback state " + mAdvertisingState);
return;
}
// Force stop advertising, no callback.
if (mAdvertisingState == BluetoothAdapter.STATE_ADVERTISE_FORCE_STOPPING) {
mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STOPPED;
mAdvertisingClientIf = 0;
sendBroadcast(new Intent(
BluetoothAdapter.ACTION_BLUETOOTH_ADVERTISING_STOPPED));
return;
}
if (mAdvertisingState == BluetoothAdapter.STATE_ADVERTISE_STARTING) {
if (status == 0) {
mAdvertisingClientIf = clientIf;
mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STARTED;
sendBroadcast(new Intent(
BluetoothAdapter.ACTION_BLUETOOTH_ADVERTISING_STARTED));
} else {
mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STOPPED;
}
} else if (mAdvertisingState == BluetoothAdapter.STATE_ADVERTISE_STOPPING) {
if (status == 0) {
mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STOPPED;
sendBroadcast(new Intent(
BluetoothAdapter.ACTION_BLUETOOTH_ADVERTISING_STOPPED));
mAdvertisingClientIf = 0;
} else {
mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STARTED;
}
}
}
ClientMap.App app = mClientMap.getById(clientIf);
if (app == null || app.callback == null) {
Log.e(TAG, "app or callback is null");
return;
}
app.callback.onAdvertiseStateChange(mAdvertisingState, status);
}
/**************************************************************************
* GATT Service functions - Shared CLIENT/SERVER
*************************************************************************/
List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
final int DEVICE_TYPE_BREDR = 0x1;
Map<BluetoothDevice, Integer> deviceStates = new HashMap<BluetoothDevice,
Integer>();
// Add paired LE devices
Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
for (BluetoothDevice device : bondedDevices) {
if (getDeviceType(device) != DEVICE_TYPE_BREDR) {
deviceStates.put(device, BluetoothProfile.STATE_DISCONNECTED);
}
}
// Add connected deviceStates
Set<String> connectedDevices = new HashSet<String>();
connectedDevices.addAll(mClientMap.getConnectedDevices());
connectedDevices.addAll(mServerMap.getConnectedDevices());
for (String address : connectedDevices ) {
BluetoothDevice device = mAdapter.getRemoteDevice(address);
if (device != null) {
deviceStates.put(device, BluetoothProfile.STATE_CONNECTED);
}
}
// Create matching device sub-set
List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
for (Map.Entry<BluetoothDevice, Integer> entry : deviceStates.entrySet()) {
for(int state : states) {
if (entry.getValue() == state) {
deviceList.add(entry.getKey());
}
}
}
return deviceList;
}
void startScan(int appIf, boolean isServer) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
if (DBG) Log.d(TAG, "startScan() - queue=" + mScanQueue.size());
if (getScanClient(appIf, isServer) == null) {
if (DBG) Log.d(TAG, "startScan() - adding client=" + appIf);
mScanQueue.add(new ScanClient(appIf, isServer));
}
gattClientScanNative(appIf, true);
}
void startScanWithUuids(int appIf, boolean isServer, UUID[] uuids) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
if (DBG) Log.d(TAG, "startScanWithUuids() - queue=" + mScanQueue.size());
if (getScanClient(appIf, isServer) == null) {
if (DBG) Log.d(TAG, "startScanWithUuids() - adding client=" + appIf);
mScanQueue.add(new ScanClient(appIf, isServer, uuids));
}
gattClientScanNative(appIf, true);
}
void stopScan(int appIf, boolean isServer) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH_ADMIN permission");
if (DBG) Log.d(TAG, "stopScan() - queue=" + mScanQueue.size());
removeScanClient(appIf, isServer);
if (mScanQueue.isEmpty()) {
if (DBG) Log.d(TAG, "stopScan() - queue empty; stopping scan");
gattClientScanNative(appIf, false);
}
}
/**************************************************************************
* GATT Service functions - CLIENT
*************************************************************************/
void registerClient(UUID uuid, IBluetoothGattCallback callback) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "registerClient() - UUID=" + uuid);
mClientMap.add(uuid, callback);
gattClientRegisterAppNative(uuid.getLeastSignificantBits(),
uuid.getMostSignificantBits());
}
void unregisterClient(int clientIf) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "unregisterClient() - clientIf=" + clientIf);
mClientMap.remove(clientIf);
gattClientUnregisterAppNative(clientIf);
}
void clientConnect(int clientIf, String address, boolean isDirect) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "clientConnect() - address=" + address + ", isDirect=" + isDirect);
gattClientConnectNative(clientIf, address, isDirect);
}
void clientDisconnect(int clientIf, String address) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Integer connId = mClientMap.connIdByAddress(clientIf, address);
if (DBG) Log.d(TAG, "clientDisconnect() - address=" + address + ", connId=" + connId);
gattClientDisconnectNative(clientIf, address, connId != null ? connId : 0);
}
synchronized boolean setAdvServiceData(byte[] serviceData) {
enforcePrivilegedPermission();
if (serviceData == null) return false;
// Calculate how many more bytes are needed for advertising service data field.
int extraBytes = (mServiceData == null) ?
FIELD_OVERHEAD_BYTES + serviceData.length :
serviceData.length - mServiceData.length;
if (getAvailableSize() < extraBytes) {
Log.e(TAG, "cannot set service data, available size " + getAvailableSize());
return false;
}
mServiceData = serviceData;
return true;
}
byte[] getAdvServiceData() {
enforcePrivilegedPermission();
return mServiceData;
}
synchronized boolean setAdvManufacturerCodeAndData(
int manufacturerCode, byte[] manufacturerData) {
enforcePrivilegedPermission();
if (manufacturerCode <= 0 || manufacturerData == null) {
return false;
}
if (mManufacturerCode > 0 && mManufacturerData != null) {
Log.e(TAG, "manufacture data is already set");
return false;
}
if (getAvailableSize() <
FIELD_OVERHEAD_BYTES + manufacturerData.length) {
Log.e(TAG, "cannot set manu data, available size " + getAvailableSize());
return false;
}
this.mManufacturerCode = manufacturerCode;
this.mManufacturerData = manufacturerData;
return true;
}
void removeAdvManufacturerCodeAndData(int manufacturerCode) {
enforcePrivilegedPermission();
if (mManufacturerCode != manufacturerCode) {
return;
}
mManufacturerCode = -1;
mManufacturerData = new byte[0];
}
byte[] getAdvManufacturerData() {
enforcePrivilegedPermission();
return mManufacturerData;
}
synchronized List<ParcelUuid> getAdvServiceUuids() {
enforcePrivilegedPermission();;
boolean fullUuidFound = false;
List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
for (HandleMap.Entry entry : mHandleMap.mEntries) {
if (entry.advertisePreferred) {
ParcelUuid parcelUuid = new ParcelUuid(entry.uuid);
if (BluetoothUuid.isShortUuid(parcelUuid)) {
serviceUuids.add(parcelUuid);
} else {
// Allow at most one 128 bit service uuid to be advertised.
if (!fullUuidFound) {
fullUuidFound = true;
serviceUuids.add(parcelUuid);
}
}
}
}
return serviceUuids;
}
boolean isAdvertising() {
enforcePrivilegedPermission();
return mAdvertisingState != BluetoothAdapter.STATE_ADVERTISE_STOPPED;
}
void startAdvertising(int clientIf) {
enforcePrivilegedPermission();
if (DBG) Log.d(TAG, "start advertising for app - " + clientIf);
List<ParcelUuid> serviceUuids = getAdvServiceUuids();
int advertisingServiceUuidLength = serviceUuids == null ? 0 : serviceUuids.size();
// Note according to Bluetooth Spec Version 4.0, for advertising and scan response data
// "all numerical multi-byte entities and values shall use little-endian byte order".
ByteBuffer advertisingUuidBytes = ByteBuffer.allocate(advertisingServiceUuidLength * 16)
.order(ByteOrder.LITTLE_ENDIAN);
for (ParcelUuid parcelUuid : serviceUuids) {
UUID uuid = parcelUuid.getUuid();
// Least signifcant bits first as the advertising uuid should be in little-endian.
advertisingUuidBytes.putLong(uuid.getLeastSignificantBits())
.putLong(uuid.getMostSignificantBits());
}
// Set advertising data.
gattSetAdvDataNative(clientIf,
false, // not scan response data
false, // no device name
false, // no tx power included
DEFAULT_SCAN_INTERVAL_MILLIS,
DEFAULT_SCAN_INTERVAL_MILLIS,
0, // no appearance limit
mManufacturerData,
mServiceData,
advertisingUuidBytes.array());
// Start advertising if advertising is not already started.
if (!isAdvertising()) {
gattAdvertiseNative(clientIf, true);
mAdvertisingClientIf = clientIf;
mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STARTING;
}
}
void stopAdvertising() {
stopAdvertising(false);
}
void stopAdvertising(boolean forceStop) {
enforcePrivilegedPermission();
gattAdvertiseNative(mAdvertisingClientIf, false);
synchronized (mLock) {
if (forceStop) {
mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_FORCE_STOPPING;
} else {
mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STOPPING;
}
}
}
List<String> getConnectedDevices() {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Set<String> connectedDevAddress = new HashSet<String>();
connectedDevAddress.addAll(mClientMap.getConnectedDevices());
connectedDevAddress.addAll(mServerMap.getConnectedDevices());
List<String> connectedDeviceList = new ArrayList<String>(connectedDevAddress);
return connectedDeviceList;
}
void refreshDevice(int clientIf, String address) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "refreshDevice() - address=" + address);
gattClientRefreshNative(clientIf, address);
}
void discoverServices(int clientIf, String address) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Integer connId = mClientMap.connIdByAddress(clientIf, address);
if (DBG) Log.d(TAG, "discoverServices() - address=" + address + ", connId=" + connId);
if (connId != null)
gattClientSearchServiceNative(connId, true, 0, 0);
else
Log.e(TAG, "discoverServices() - No connection for " + address + "...");
}
void readCharacteristic(int clientIf, String address, int srvcType,
int srvcInstanceId, UUID srvcUuid,
int charInstanceId, UUID charUuid, int authReq) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "readCharacteristic() - address=" + address);
Integer connId = mClientMap.connIdByAddress(clientIf, address);
if (connId != null)
gattClientReadCharacteristicNative(connId, srvcType,
srvcInstanceId, srvcUuid.getLeastSignificantBits(),
srvcUuid.getMostSignificantBits(), charInstanceId,
charUuid.getLeastSignificantBits(), charUuid.getMostSignificantBits(),
authReq);
else
Log.e(TAG, "readCharacteristic() - No connection for " + address + "...");
}
void writeCharacteristic(int clientIf, String address, int srvcType,
int srvcInstanceId, UUID srvcUuid,
int charInstanceId, UUID charUuid, int writeType,
int authReq, byte[] value) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "writeCharacteristic() - address=" + address);
if (mReliableQueue.contains(address)) writeType = 3; // Prepared write
Integer connId = mClientMap.connIdByAddress(clientIf, address);
if (connId != null)
gattClientWriteCharacteristicNative(connId, srvcType,
srvcInstanceId, srvcUuid.getLeastSignificantBits(),
srvcUuid.getMostSignificantBits(), charInstanceId,
charUuid.getLeastSignificantBits(), charUuid.getMostSignificantBits(),
writeType, authReq, value);
else
Log.e(TAG, "writeCharacteristic() - No connection for " + address + "...");
}
void readDescriptor(int clientIf, String address, int srvcType,
int srvcInstanceId, UUID srvcUuid,
int charInstanceId, UUID charUuid,
int descrInstanceId, UUID descrUuid,
int authReq) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "readDescriptor() - address=" + address);
Integer connId = mClientMap.connIdByAddress(clientIf, address);
if (connId != null)
gattClientReadDescriptorNative(connId, srvcType,
srvcInstanceId,
srvcUuid.getLeastSignificantBits(), srvcUuid.getMostSignificantBits(),
charInstanceId,
charUuid.getLeastSignificantBits(), charUuid.getMostSignificantBits(),
descrInstanceId,
descrUuid.getLeastSignificantBits(), descrUuid.getMostSignificantBits(),
authReq);
else
Log.e(TAG, "readDescriptor() - No connection for " + address + "...");
};
void writeDescriptor(int clientIf, String address, int srvcType,
int srvcInstanceId, UUID srvcUuid,
int charInstanceId, UUID charUuid,
int descrInstanceId, UUID descrUuid,
int writeType, int authReq, byte[] value) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "writeDescriptor() - address=" + address);
Integer connId = mClientMap.connIdByAddress(clientIf, address);
if (connId != null)
gattClientWriteDescriptorNative(connId, srvcType,
srvcInstanceId,
srvcUuid.getLeastSignificantBits(), srvcUuid.getMostSignificantBits(),
charInstanceId,
charUuid.getLeastSignificantBits(), charUuid.getMostSignificantBits(),
descrInstanceId,
descrUuid.getLeastSignificantBits(), descrUuid.getMostSignificantBits(),
writeType, authReq, value);
else
Log.e(TAG, "writeDescriptor() - No connection for " + address + "...");
}
void beginReliableWrite(int clientIf, String address) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "beginReliableWrite() - address=" + address);
mReliableQueue.add(address);
}
void endReliableWrite(int clientIf, String address, boolean execute) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "endReliableWrite() - address=" + address
+ " execute: " + execute);
mReliableQueue.remove(address);
Integer connId = mClientMap.connIdByAddress(clientIf, address);
if (connId != null) gattClientExecuteWriteNative(connId, execute);
}
void registerForNotification(int clientIf, String address, int srvcType,
int srvcInstanceId, UUID srvcUuid,
int charInstanceId, UUID charUuid,
boolean enable) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "registerForNotification() - address=" + address + " enable: " + enable);
Integer connId = mClientMap.connIdByAddress(clientIf, address);
if (connId != null) {
gattClientRegisterForNotificationsNative(clientIf, address,
srvcType, srvcInstanceId, srvcUuid.getLeastSignificantBits(),
srvcUuid.getMostSignificantBits(), charInstanceId,
charUuid.getLeastSignificantBits(), charUuid.getMostSignificantBits(),
enable);
} else {
Log.e(TAG, "registerForNotification() - No connection for " + address + "...");
}
}
void readRemoteRssi(int clientIf, String address) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "readRemoteRssi() - address=" + address);
gattClientReadRemoteRssiNative(clientIf, address);
}
/**************************************************************************
* Callback functions - SERVER
*************************************************************************/
void onServerRegistered(int status, int serverIf, long uuidLsb, long uuidMsb)
throws RemoteException {
UUID uuid = new UUID(uuidMsb, uuidLsb);
if (DBG) Log.d(TAG, "onServerRegistered() - UUID=" + uuid + ", serverIf=" + serverIf);
ServerMap.App app = mServerMap.getByUuid(uuid);
if (app != null) {
app.id = serverIf;
app.linkToDeath(new ServerDeathRecipient(serverIf));
app.callback.onServerRegistered(status, serverIf);
}
}
void onServiceAdded(int status, int serverIf, int srvcType, int srvcInstId,
long srvcUuidLsb, long srvcUuidMsb, int srvcHandle)
throws RemoteException {
UUID uuid = new UUID(srvcUuidMsb, srvcUuidLsb);
if (DBG) Log.d(TAG, "onServiceAdded() UUID=" + uuid + ", status=" + status
+ ", handle=" + srvcHandle);
if (status == 0) {
mHandleMap.addService(serverIf, srvcHandle, uuid, srvcType, srvcInstId,
mAdvertisingServiceUuids.remove(uuid));
}
continueServiceDeclaration(serverIf, status, srvcHandle);
}
void onIncludedServiceAdded(int status, int serverIf, int srvcHandle,
int includedSrvcHandle) throws RemoteException {
if (DBG) Log.d(TAG, "onIncludedServiceAdded() status=" + status
+ ", service=" + srvcHandle + ", included=" + includedSrvcHandle);
continueServiceDeclaration(serverIf, status, srvcHandle);
}
void onCharacteristicAdded(int status, int serverIf,
long charUuidLsb, long charUuidMsb,
int srvcHandle, int charHandle)
throws RemoteException {
UUID uuid = new UUID(charUuidMsb, charUuidLsb);
if (DBG) Log.d(TAG, "onCharacteristicAdded() UUID=" + uuid + ", status=" + status
+ ", srvcHandle=" + srvcHandle + ", charHandle=" + charHandle);
if (status == 0)
mHandleMap.addCharacteristic(serverIf, charHandle, uuid, srvcHandle);
continueServiceDeclaration(serverIf, status, srvcHandle);
}
void onDescriptorAdded(int status, int serverIf,
long descrUuidLsb, long descrUuidMsb,
int srvcHandle, int descrHandle)
throws RemoteException {
UUID uuid = new UUID(descrUuidMsb, descrUuidLsb);
if (DBG) Log.d(TAG, "onDescriptorAdded() UUID=" + uuid + ", status=" + status
+ ", srvcHandle=" + srvcHandle + ", descrHandle=" + descrHandle);
if (status == 0)
mHandleMap.addDescriptor(serverIf, descrHandle, uuid, srvcHandle);
continueServiceDeclaration(serverIf, status, srvcHandle);
}
void onServiceStarted(int status, int serverIf, int srvcHandle)
throws RemoteException {
if (DBG) Log.d(TAG, "onServiceStarted() srvcHandle=" + srvcHandle
+ ", status=" + status);
if (status == 0)
mHandleMap.setStarted(serverIf, srvcHandle, true);
}
void onServiceStopped(int status, int serverIf, int srvcHandle)
throws RemoteException {
if (DBG) Log.d(TAG, "onServiceStopped() srvcHandle=" + srvcHandle
+ ", status=" + status);
if (status == 0)
mHandleMap.setStarted(serverIf, srvcHandle, false);
stopNextService(serverIf, status);
}
void onServiceDeleted(int status, int serverIf, int srvcHandle) {
if (DBG) Log.d(TAG, "onServiceDeleted() srvcHandle=" + srvcHandle
+ ", status=" + status);
mHandleMap.deleteService(serverIf, srvcHandle);
}
void onClientConnected(String address, boolean connected, int connId, int serverIf)
throws RemoteException {
if (DBG) Log.d(TAG, "onConnected() connId=" + connId
+ ", address=" + address + ", connected=" + connected);
ServerMap.App app = mServerMap.getById(serverIf);
if (app == null) return;
if (connected) {
mServerMap.addConnection(serverIf, connId, address);
} else {
mServerMap.removeConnection(serverIf, connId);
}
app.callback.onServerConnectionState((byte)0, serverIf, connected, address);
}
void onAttributeRead(String address, int connId, int transId,
int attrHandle, int offset, boolean isLong)
throws RemoteException {
if (DBG) Log.d(TAG, "onAttributeRead() connId=" + connId
+ ", address=" + address + ", handle=" + attrHandle
+ ", requestId=" + transId + ", offset=" + offset);
HandleMap.Entry entry = mHandleMap.getByHandle(attrHandle);
if (entry == null) return;
if (DBG) Log.d(TAG, "onAttributeRead() UUID=" + entry.uuid
+ ", serverIf=" + entry.serverIf + ", type=" + entry.type);
mHandleMap.addRequest(transId, attrHandle);
ServerMap.App app = mServerMap.getById(entry.serverIf);
if (app == null) return;
switch(entry.type) {
case HandleMap.TYPE_CHARACTERISTIC:
{
HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle);
app.callback.onCharacteristicReadRequest(address, transId, offset, isLong,
serviceEntry.serviceType, serviceEntry.instance,
new ParcelUuid(serviceEntry.uuid), entry.instance,
new ParcelUuid(entry.uuid));
break;
}
case HandleMap.TYPE_DESCRIPTOR:
{
HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle);
HandleMap.Entry charEntry = mHandleMap.getByHandle(entry.charHandle);
app.callback.onDescriptorReadRequest(address, transId, offset, isLong,
serviceEntry.serviceType, serviceEntry.instance,
new ParcelUuid(serviceEntry.uuid), charEntry.instance,
new ParcelUuid(charEntry.uuid),
new ParcelUuid(entry.uuid));
break;
}
default:
Log.e(TAG, "onAttributeRead() - Requested unknown attribute type.");
break;
}
}
void onAttributeWrite(String address, int connId, int transId,
int attrHandle, int offset, int length,
boolean needRsp, boolean isPrep,
byte[] data)
throws RemoteException {
if (DBG) Log.d(TAG, "onAttributeWrite() connId=" + connId
+ ", address=" + address + ", handle=" + attrHandle
+ ", requestId=" + transId + ", isPrep=" + isPrep
+ ", offset=" + offset);
HandleMap.Entry entry = mHandleMap.getByHandle(attrHandle);
if (entry == null) return;
if (DBG) Log.d(TAG, "onAttributeWrite() UUID=" + entry.uuid
+ ", serverIf=" + entry.serverIf + ", type=" + entry.type);
mHandleMap.addRequest(transId, attrHandle);
ServerMap.App app = mServerMap.getById(entry.serverIf);
if (app == null) return;
switch(entry.type) {
case HandleMap.TYPE_CHARACTERISTIC:
{
HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle);
app.callback.onCharacteristicWriteRequest(address, transId,
offset, length, isPrep, needRsp,
serviceEntry.serviceType, serviceEntry.instance,
new ParcelUuid(serviceEntry.uuid), entry.instance,
new ParcelUuid(entry.uuid), data);
break;
}
case HandleMap.TYPE_DESCRIPTOR:
{
HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle);
HandleMap.Entry charEntry = mHandleMap.getByHandle(entry.charHandle);
app.callback.onDescriptorWriteRequest(address, transId,
offset, length, isPrep, needRsp,
serviceEntry.serviceType, serviceEntry.instance,
new ParcelUuid(serviceEntry.uuid), charEntry.instance,
new ParcelUuid(charEntry.uuid),
new ParcelUuid(entry.uuid), data);
break;
}
default:
Log.e(TAG, "onAttributeWrite() - Requested unknown attribute type.");
break;
}
}
void onExecuteWrite(String address, int connId, int transId, int execWrite)
throws RemoteException {
if (DBG) Log.d(TAG, "onExecuteWrite() connId=" + connId
+ ", address=" + address + ", transId=" + transId);
ServerMap.App app = mServerMap.getByConnId(connId);
if (app == null) return;
app.callback.onExecuteWrite(address, transId, execWrite == 1);
}
void onResponseSendCompleted(int status, int attrHandle) {
if (DBG) Log.d(TAG, "onResponseSendCompleted() handle=" + attrHandle);
}
/**************************************************************************
* GATT Service functions - SERVER
*************************************************************************/
void registerServer(UUID uuid, IBluetoothGattServerCallback callback) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "registerServer() - UUID=" + uuid);
mServerMap.add(uuid, callback);
gattServerRegisterAppNative(uuid.getLeastSignificantBits(),
uuid.getMostSignificantBits());
}
void unregisterServer(int serverIf) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "unregisterServer() - serverIf=" + serverIf);
deleteServices(serverIf);
mServerMap.remove(serverIf);
gattServerUnregisterAppNative(serverIf);
}
void serverConnect(int serverIf, String address, boolean isDirect) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "serverConnect() - address=" + address);
gattServerConnectNative(serverIf, address, isDirect);
}
void serverDisconnect(int serverIf, String address) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Integer connId = mServerMap.connIdByAddress(serverIf, address);
if (DBG) Log.d(TAG, "serverDisconnect() - address=" + address + ", connId=" + connId);
gattServerDisconnectNative(serverIf, address, connId != null ? connId : 0);
}
void beginServiceDeclaration(int serverIf, int srvcType, int srvcInstanceId,
int minHandles, UUID srvcUuid, boolean advertisePreferred) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "beginServiceDeclaration() - uuid=" + srvcUuid);
ServiceDeclaration serviceDeclaration = addDeclaration();
serviceDeclaration.addService(srvcUuid, srvcType, srvcInstanceId, minHandles,
advertisePreferred);
}
void addIncludedService(int serverIf, int srvcType, int srvcInstanceId,
UUID srvcUuid) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "addIncludedService() - uuid=" + srvcUuid);
getActiveDeclaration().addIncludedService(srvcUuid, srvcType, srvcInstanceId);
}
void addCharacteristic(int serverIf, UUID charUuid, int properties,
int permissions) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "addCharacteristic() - uuid=" + charUuid);
getActiveDeclaration().addCharacteristic(charUuid, properties, permissions);
}
void addDescriptor(int serverIf, UUID descUuid, int permissions) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "addDescriptor() - uuid=" + descUuid);
getActiveDeclaration().addDescriptor(descUuid, permissions);
}
void endServiceDeclaration(int serverIf) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "endServiceDeclaration()");
if (getActiveDeclaration() == getPendingDeclaration()) {
try {
continueServiceDeclaration(serverIf, (byte)0, 0);
} catch (RemoteException e) {
Log.e(TAG,""+e);
}
}
}
void removeService(int serverIf, int srvcType,
int srvcInstanceId, UUID srvcUuid) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "removeService() - uuid=" + srvcUuid);
int srvcHandle = mHandleMap.getServiceHandle(srvcUuid, srvcType, srvcInstanceId);
if (srvcHandle == 0) return;
gattServerDeleteServiceNative(serverIf, srvcHandle);
}
void clearServices(int serverIf) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "clearServices()");
deleteServices(serverIf);
}
void sendResponse(int serverIf, String address, int requestId,
int status, int offset, byte[] value) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "sendResponse() - address=" + address);
int handle = 0;
HandleMap.Entry entry = mHandleMap.getByRequestId(requestId);
if (entry != null) handle = entry.handle;
int connId = mServerMap.connIdByAddress(serverIf, address);
gattServerSendResponseNative(serverIf, connId, requestId, (byte)status,
handle, offset, value, (byte)0);
mHandleMap.deleteRequest(requestId);
}
void sendNotification(int serverIf, String address, int srvcType,
int srvcInstanceId, UUID srvcUuid,
int charInstanceId, UUID charUuid,
boolean confirm, byte[] value) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "sendNotification() - address=" + address);
int srvcHandle = mHandleMap.getServiceHandle(srvcUuid, srvcType, srvcInstanceId);
if (srvcHandle == 0) return;
int charHandle = mHandleMap.getCharacteristicHandle(srvcHandle, charUuid, charInstanceId);
if (charHandle == 0) return;
int connId = mServerMap.connIdByAddress(serverIf, address);
if (connId == 0) return;
if (confirm) {
gattServerSendIndicationNative(serverIf, charHandle, connId, value);
} else {
gattServerSendNotificationNative(serverIf, charHandle, connId, value);
}
}
/**************************************************************************
* Private functions
*************************************************************************/
private int getDeviceType(BluetoothDevice device) {
int type = gattClientGetDeviceTypeNative(device.getAddress());
if (DBG) Log.d(TAG, "getDeviceType() - device=" + device
+ ", type=" + type);
return type;
}
private synchronized int getAvailableSize() {
enforcePrivilegedPermission();
int availableSize = ADVERTISING_PACKET_MAX_BYTES - ADVERTISING_FLAGS_BYTES;
for (ParcelUuid parcelUuid : getAdvServiceUuids()) {
if (BluetoothUuid.isShortUuid(parcelUuid)) {
availableSize -= FIELD_OVERHEAD_BYTES + SHORT_UUID_BYTES;
} else {
availableSize -= FIELD_OVERHEAD_BYTES + FULL_UUID_BYTES;
}
}
if (mManufacturerCode > 0 && mManufacturerData != null) {
availableSize -= (FIELD_OVERHEAD_BYTES + mManufacturerData.length);
}
if (mServiceData != null) {
availableSize -= (FIELD_OVERHEAD_BYTES + mServiceData.length);
}
return availableSize;
}
// Enforce caller has BLUETOOTH_PRIVILEGED permission. A {@link SecurityException} will be
// thrown if the caller app does not have BLUETOOTH_PRIVILEGED permission.
private void enforcePrivilegedPermission() {
enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
"Need BLUETOOTH_PRIVILEGED permission");
}
private void continueSearch(int connId, int status) throws RemoteException {
if (status == 0 && !mSearchQueue.isEmpty()) {
SearchQueue.Entry svc = mSearchQueue.pop();
if (svc.charUuidLsb == 0) {
// Characteristic is up next
gattClientGetCharacteristicNative(svc.connId, svc.srvcType,
svc.srvcInstId, svc.srvcUuidLsb, svc.srvcUuidMsb, 0, 0, 0);
} else {
// Descriptor is up next
gattClientGetDescriptorNative(svc.connId, svc.srvcType,
svc.srvcInstId, svc.srvcUuidLsb, svc.srvcUuidMsb,
svc.charInstId, svc.charUuidLsb, svc.charUuidMsb, 0, 0, 0);
}
} else {
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onSearchComplete(mClientMap.addressByConnId(connId), status);
}
}
}
private void continueServiceDeclaration(int serverIf, int status, int srvcHandle) throws RemoteException {
if (mServiceDeclarations.size() == 0) return;
if (DBG) Log.d(TAG, "continueServiceDeclaration() - srvcHandle=" + srvcHandle);
boolean finished = false;
ServiceDeclaration.Entry entry = null;
if (status == 0)
entry = getPendingDeclaration().getNext();
if (entry != null) {
if (DBG) Log.d(TAG, "continueServiceDeclaration() - next entry type="
+ entry.type);
switch(entry.type) {
case ServiceDeclaration.TYPE_SERVICE:
if (entry.advertisePreferred) {
mAdvertisingServiceUuids.add(entry.uuid);
}
gattServerAddServiceNative(serverIf, entry.serviceType,
entry.instance,
entry.uuid.getLeastSignificantBits(),
entry.uuid.getMostSignificantBits(),
getPendingDeclaration().getNumHandles());
break;
case ServiceDeclaration.TYPE_CHARACTERISTIC:
gattServerAddCharacteristicNative(serverIf, srvcHandle,
entry.uuid.getLeastSignificantBits(),
entry.uuid.getMostSignificantBits(),
entry.properties, entry.permissions);
break;
case ServiceDeclaration.TYPE_DESCRIPTOR:
gattServerAddDescriptorNative(serverIf, srvcHandle,
entry.uuid.getLeastSignificantBits(),
entry.uuid.getMostSignificantBits(),
entry.permissions);
break;
case ServiceDeclaration.TYPE_INCLUDED_SERVICE:
{
int inclSrvc = mHandleMap.getServiceHandle(entry.uuid,
entry.serviceType, entry.instance);
if (inclSrvc != 0) {
gattServerAddIncludedServiceNative(serverIf, srvcHandle,
inclSrvc);
} else {
finished = true;
}
break;
}
}
} else {
gattServerStartServiceNative(serverIf, srvcHandle, (byte)2 /*BREDR/LE*/);
finished = true;
}
if (finished) {
if (DBG) Log.d(TAG, "continueServiceDeclaration() - completed.");
ServerMap.App app = mServerMap.getById(serverIf);
if (app != null) {
HandleMap.Entry serviceEntry = mHandleMap.getByHandle(srvcHandle);
if (serviceEntry != null) {
app.callback.onServiceAdded(status, serviceEntry.serviceType,
serviceEntry.instance, new ParcelUuid(serviceEntry.uuid));
} else {
app.callback.onServiceAdded(status, 0, 0, null);
}
}
removePendingDeclaration();
if (getPendingDeclaration() != null) {
continueServiceDeclaration(serverIf, (byte)0, 0);
}
}
}
private void stopNextService(int serverIf, int status) throws RemoteException {
if (DBG) Log.d(TAG, "stopNextService() - serverIf=" + serverIf
+ ", status=" + status);
if (status == 0) {
List<HandleMap.Entry> entries = mHandleMap.getEntries();
for(HandleMap.Entry entry : entries) {
if (entry.type != HandleMap.TYPE_SERVICE ||
entry.serverIf != serverIf ||
entry.started == false)
continue;
gattServerStopServiceNative(serverIf, entry.handle);
return;
}
}
}
private void deleteServices(int serverIf) {
if (DBG) Log.d(TAG, "deleteServices() - serverIf=" + serverIf);
/*
* Figure out which handles to delete.
* The handles are copied into a new list to avoid race conditions.
*/
List<Integer> handleList = new ArrayList<Integer>();
List<HandleMap.Entry> entries = mHandleMap.getEntries();
for(HandleMap.Entry entry : entries) {
if (entry.type != HandleMap.TYPE_SERVICE ||
entry.serverIf != serverIf)
continue;
handleList.add(entry.handle);
}
/* Now actually delete the services.... */
for(Integer handle : handleList) {
gattServerDeleteServiceNative(serverIf, handle);
}
}
private List<UUID> parseUuids(byte[] adv_data) {
List<UUID> uuids = new ArrayList<UUID>();
int offset = 0;
while(offset < (adv_data.length-2)) {
int len = adv_data[offset++];
if (len == 0) break;
int type = adv_data[offset++];
switch (type) {
case 0x02: // Partial list of 16-bit UUIDs
case 0x03: // Complete list of 16-bit UUIDs
while (len > 1) {
int uuid16 = adv_data[offset++];
uuid16 += (adv_data[offset++] << 8);
len -= 2;
uuids.add(UUID.fromString(String.format(
"%08x-0000-1000-8000-00805f9b34fb", uuid16)));
}
break;
default:
offset += (len - 1);
break;
}
}
return uuids;
}
/**************************************************************************
* GATT Test functions
*************************************************************************/
void gattTestCommand(int command, UUID uuid1, String bda1,
int p1, int p2, int p3, int p4, int p5) {
if (bda1 == null) bda1 = "00:00:00:00:00:00";
if (uuid1 != null)
gattTestNative(command, uuid1.getLeastSignificantBits(),
uuid1.getMostSignificantBits(), bda1, p1, p2, p3, p4, p5);
else
gattTestNative(command, 0,0, bda1, p1, p2, p3, p4, p5);
}
private native void gattTestNative(int command,
long uuid1_lsb, long uuid1_msb, String bda1,
int p1, int p2, int p3, int p4, int p5);
/**************************************************************************
* Native functions prototypes
*************************************************************************/
private native static void classInitNative();
private native void initializeNative();
private native void cleanupNative();
private native int gattClientGetDeviceTypeNative(String address);
private native void gattClientRegisterAppNative(long app_uuid_lsb,
long app_uuid_msb);
private native void gattClientUnregisterAppNative(int clientIf);
private native void gattClientScanNative(int clientIf, boolean start);
private native void gattClientConnectNative(int clientIf, String address,
boolean isDirect);
private native void gattClientDisconnectNative(int clientIf, String address,
int conn_id);
private native void gattClientRefreshNative(int clientIf, String address);
private native void gattClientSearchServiceNative(int conn_id,
boolean search_all, long service_uuid_lsb, long service_uuid_msb);
private native void gattClientGetCharacteristicNative(int conn_id,
int service_type, int service_id_inst_id, long service_id_uuid_lsb,
long service_id_uuid_msb, int char_id_inst_id, long char_id_uuid_lsb,
long char_id_uuid_msb);
private native void gattClientGetDescriptorNative(int conn_id, int service_type,
int service_id_inst_id, long service_id_uuid_lsb, long service_id_uuid_msb,
int char_id_inst_id, long char_id_uuid_lsb, long char_id_uuid_msb,
int descr_id_inst_id, long descr_id_uuid_lsb, long descr_id_uuid_msb);
private native void gattClientGetIncludedServiceNative(int conn_id,
int service_type, int service_id_inst_id,
long service_id_uuid_lsb, long service_id_uuid_msb,
int incl_service_id_inst_id, int incl_service_type,
long incl_service_id_uuid_lsb, long incl_service_id_uuid_msb);
private native void gattClientReadCharacteristicNative(int conn_id,
int service_type, int service_id_inst_id, long service_id_uuid_lsb,
long service_id_uuid_msb, int char_id_inst_id, long char_id_uuid_lsb,
long char_id_uuid_msb, int authReq);
private native void gattClientReadDescriptorNative(int conn_id, int service_type,
int service_id_inst_id, long service_id_uuid_lsb, long service_id_uuid_msb,
int char_id_inst_id, long char_id_uuid_lsb, long char_id_uuid_msb,
int descr_id_inst_id, long descr_id_uuid_lsb, long descr_id_uuid_msb,
int authReq);
private native void gattClientWriteCharacteristicNative(int conn_id,
int service_type, int service_id_inst_id, long service_id_uuid_lsb,
long service_id_uuid_msb, int char_id_inst_id, long char_id_uuid_lsb,
long char_id_uuid_msb, int write_type, int auth_req, byte[] value);
private native void gattClientWriteDescriptorNative(int conn_id, int service_type,
int service_id_inst_id, long service_id_uuid_lsb, long service_id_uuid_msb,
int char_id_inst_id, long char_id_uuid_lsb, long char_id_uuid_msb,
int descr_id_inst_id, long descr_id_uuid_lsb, long descr_id_uuid_msb,
int write_type, int auth_req, byte[] value);
private native void gattClientExecuteWriteNative(int conn_id, boolean execute);
private native void gattClientRegisterForNotificationsNative(int clientIf,
String address, int service_type, int service_id_inst_id,
long service_id_uuid_lsb, long service_id_uuid_msb,
int char_id_inst_id, long char_id_uuid_lsb, long char_id_uuid_msb,
boolean enable);
private native void gattClientReadRemoteRssiNative(int clientIf,
String address);
private native void gattAdvertiseNative(int client_if, boolean start);
private native void gattSetAdvDataNative(int serverIf, boolean setScanRsp, boolean inclName,
boolean inclTxPower, int minInterval, int maxInterval,
int appearance, byte[] manufacturerData, byte[] serviceData, byte[] serviceUuid);
private native void gattServerRegisterAppNative(long app_uuid_lsb,
long app_uuid_msb);
private native void gattServerUnregisterAppNative(int serverIf);
private native void gattServerConnectNative(int server_if, String address,
boolean is_direct);
private native void gattServerDisconnectNative(int serverIf, String address,
int conn_id);
private native void gattServerAddServiceNative (int server_if,
int service_type, int service_id_inst_id,
long service_id_uuid_lsb, long service_id_uuid_msb,
int num_handles);
private native void gattServerAddIncludedServiceNative (int server_if,
int svc_handle, int included_svc_handle);
private native void gattServerAddCharacteristicNative (int server_if,
int svc_handle, long char_uuid_lsb, long char_uuid_msb,
int properties, int permissions);
private native void gattServerAddDescriptorNative (int server_if,
int svc_handle, long desc_uuid_lsb, long desc_uuid_msb,
int permissions);
private native void gattServerStartServiceNative (int server_if,
int svc_handle, int transport );
private native void gattServerStopServiceNative (int server_if,
int svc_handle);
private native void gattServerDeleteServiceNative (int server_if,
int svc_handle);
private native void gattServerSendIndicationNative (int server_if,
int attr_handle, int conn_id, byte[] val);
private native void gattServerSendNotificationNative (int server_if,
int attr_handle, int conn_id, byte[] val);
private native void gattServerSendResponseNative (int server_if,
int conn_id, int trans_id, int status, int handle, int offset,
byte[] val, int auth_req);
}