diff options
16 files changed, 4054 insertions, 2 deletions
@@ -102,6 +102,9 @@ LOCAL_SRC_FILES += \ core/java/android/bluetooth/IBluetoothManagerCallback.aidl \ core/java/android/bluetooth/IBluetoothPbap.aidl \ core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \ + core/java/android/bluetooth/IBluetoothGatt.aidl \ + core/java/android/bluetooth/IBluetoothGattCallback.aidl \ + core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \ core/java/android/content/IClipboard.aidl \ core/java/android/content/IContentService.aidl \ core/java/android/content/IIntentReceiver.aidl \ diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 6367e16..1bbfb5d 100755..100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1136,8 +1136,9 @@ public final class BluetoothAdapter { /** * Get the profile proxy object associated with the profile. * - * <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or - * {@link BluetoothProfile#A2DP}. Clients must implements + * <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, + * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, + * or {@link BluetoothProfile#GATT_SERVER}. Clients must implements * {@link BluetoothProfile.ServiceListener} to get notified of * the connection status and to get the proxy object. * @@ -1166,6 +1167,12 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.HEALTH) { BluetoothHealth health = new BluetoothHealth(context, listener); return true; + } else if (profile == BluetoothProfile.GATT) { + BluetoothGatt gatt = new BluetoothGatt(context, listener); + return true; + } else if (profile == BluetoothProfile.GATT_SERVER) { + BluetoothGattServer gattServer = new BluetoothGattServer(context, listener); + return true; } else { return false; } @@ -1206,6 +1213,14 @@ public final class BluetoothAdapter { BluetoothHealth health = (BluetoothHealth)proxy; health.close(); break; + case BluetoothProfile.GATT: + BluetoothGatt gatt = (BluetoothGatt)proxy; + gatt.close(); + break; + case BluetoothProfile.GATT_SERVER: + BluetoothGattServer gattServer = (BluetoothGattServer)proxy; + gattServer.close(); + break; } } diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java new file mode 100644 index 0000000..1e12025 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -0,0 +1,1309 @@ +/* + * 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 android.bluetooth; + + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.bluetooth.IBluetoothManager; +import android.bluetooth.IBluetoothStateChangeCallback; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Public API for the Bluetooth Gatt Profile. + * + * <p>This class provides Bluetooth Gatt functionality to enable communication + * with Bluetooth Smart or Smart Ready devices. + * + * <p>BluetoothGatt is a proxy object for controlling the Bluetooth Service + * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the + * BluetoothGatt proxy object. + * + * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback} + * and call {@link #registerApp} to register your application. Gatt capable + * devices can be discovered using the {@link #startScan} function or the + * regular Bluetooth device discovery process. + * @hide + */ +public final class BluetoothGatt implements BluetoothProfile { + private static final String TAG = "BluetoothGatt"; + private static final boolean DBG = true; + + private Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetoothGatt mService; + private BluetoothGattCallback mCallback; + private int mClientIf; + private boolean mAuthRetry = false; + + private List<BluetoothGattService> mServices; + + /** A Gatt operation completed successfully */ + public static final int GATT_SUCCESS = 0; + + /** Gatt read operation is not permitted */ + public static final int GATT_READ_NOT_PERMITTED = 0x2; + + /** Gatt write operation is not permitted */ + public static final int GATT_WRITE_NOT_PERMITTED = 0x3; + + /** Insufficient authentication for a given operation */ + public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5; + + /** The given request is not supported */ + public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6; + + /** Insufficient encryption for a given operation */ + public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf; + + /** A read or write operation was requested with an invalid offset */ + public static final int GATT_INVALID_OFFSET = 0x7; + + /** A write operation exceeds the maximum length of the attribute */ + public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; + + /** + * No authentication required. + * @hide + */ + /*package*/ static final int AUTHENTICATION_NONE = 0; + + /** + * Authentication requested; no man-in-the-middle protection required. + * @hide + */ + /*package*/ static final int AUTHENTICATION_NO_MITM = 1; + + /** + * Authentication with man-in-the-middle protection requested. + * @hide + */ + /*package*/ static final int AUTHENTICATION_MITM = 2; + + /** + * Bluetooth state change handlers + */ + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (DBG) Log.d(TAG,"Unbinding service..."); + synchronized (mConnection) { + mService = null; + mContext.unbindService(mConnection); + } + } else { + synchronized (mConnection) { + if (mService == null) { + if (DBG) Log.d(TAG,"Binding service..."); + if (!mContext.bindService(new Intent(IBluetoothGatt.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth GATT Service"); + } + } + } + } + } + }; + + /** + * Service binder handling + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothGatt.Stub.asInterface(service); + ServiceListener serviceListener = mServiceListener; + if (serviceListener != null) { + serviceListener.onServiceConnected(BluetoothProfile.GATT, BluetoothGatt.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + ServiceListener serviceListener = mServiceListener; + if (serviceListener != null) { + serviceListener.onServiceDisconnected(BluetoothProfile.GATT); + } + } + }; + + /** + * Bluetooth GATT interface callbacks + */ + private final IBluetoothGattCallback mBluetoothGattCallback = + new IBluetoothGattCallback.Stub() { + /** + * Application interface registered - app is ready to go + * @hide + */ + public void onClientRegistered(int status, int clientIf) { + if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status + + " clientIf=" + clientIf); + mClientIf = clientIf; + try { + mCallback.onAppRegistered(status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Client connection state changed + * @hide + */ + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status + + " clientIf=" + clientIf + " device=" + address); + try { + mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, + connected ? BluetoothProfile.STATE_CONNECTED + : BluetoothProfile.STATE_DISCONNECTED); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Callback reporting an LE scan result. + * @hide + */ + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + + try { + mCallback.onScanResult(mAdapter.getRemoteDevice(address), rssi, advData); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * A new GATT service has been discovered. + * The service is added to the internal list and the search + * continues. + * @hide + */ + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + mServices.add(new BluetoothGattService(device, srvcUuid.getUuid(), + srvcInstId, srvcType)); + } + + /** + * An included service has been found durig GATT discovery. + * The included service is added to the respective parent. + * @hide + */ + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address + + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, + srvcUuid.getUuid(), srvcInstId, srvcType); + BluetoothGattService includedService = getService(device, + inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType); + + if (service != null && includedService != null) { + service.addIncludedService(includedService); + } + } + + /** + * A new GATT characteristic has been discovered. + * Add the new characteristic to the relevant service and continue + * the remote device inspection. + * @hide + */ + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" + + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service != null) { + service.addCharacteristic(new BluetoothGattCharacteristic( + service, charUuid.getUuid(), charInstId, charProps, 0)); + } + } + + /** + * A new GATT descriptor has been discovered. + * Finally, add the descriptor to the related characteristic. + * This should conclude the remote device update. + * @hide + */ + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descUuid) { + if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid()); + if (characteristic == null) return; + + characteristic.addDescriptor(new BluetoothGattDescriptor( + characteristic, descUuid.getUuid(), 0)); + } + + /** + * Remote search has been completed. + * The internal object structure should now reflect the state + * of the remote device database. Let the application know that + * we are done at this point. + * @hide + */ + public void onSearchComplete(String address, int status) { + if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + try { + mCallback.onServicesDiscovered(device, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote characteristic has been read. + * Updates the internal value. + * @hide + */ + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address + + " UUID=" + charUuid + " Status=" + status); + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && mAuthRetry == false) { + try { + mAuthRetry = true; + mService.readCharacteristic(mClientIf, address, + srvcType, srvcInstId, srvcUuid, + charInstId, charUuid, AUTHENTICATION_MITM); + return; + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + mAuthRetry = false; + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid(), charInstId); + if (characteristic == null) return; + + if (status == 0) characteristic.setValue(value); + + try { + mCallback.onCharacteristicRead(characteristic, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Characteristic has been written to the remote device. + * Let the app know how we did... + * @hide + */ + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address + + " UUID=" + charUuid + " Status=" + status); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid(), charInstId); + if (characteristic == null) return; + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && mAuthRetry == false) { + try { + mAuthRetry = true; + mService.writeCharacteristic(mClientIf, address, + srvcType, srvcInstId, srvcUuid, charInstId, charUuid, + characteristic.getWriteType(), AUTHENTICATION_MITM, + characteristic.getValue()); + return; + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + mAuthRetry = false; + + try { + mCallback.onCharacteristicWrite(characteristic, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote characteristic has been updated. + * Updates the internal value. + * @hide + */ + public void onNotify(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + byte[] value) { + if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid(), charInstId); + if (characteristic == null) return; + + characteristic.setValue(value); + + try { + mCallback.onCharacteristicChanged(characteristic); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Descriptor has been read. + * @hide + */ + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid, byte[] value) { + if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid(), charInstId); + if (characteristic == null) return; + + BluetoothGattDescriptor descriptor = characteristic.getDescriptor( + descrUuid.getUuid()); + if (descriptor == null) return; + + if (status == 0) descriptor.setValue(value); + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && mAuthRetry == false) { + try { + mAuthRetry = true; + mService.readDescriptor(mClientIf, address, + srvcType, srvcInstId, srvcUuid, charInstId, charUuid, + descrUuid, AUTHENTICATION_MITM); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + mAuthRetry = true; + + try { + mCallback.onDescriptorRead(descriptor, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Descriptor write operation complete. + * @hide + */ + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid) { + if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid(), charInstId); + if (characteristic == null) return; + + BluetoothGattDescriptor descriptor = characteristic.getDescriptor( + descrUuid.getUuid()); + if (descriptor == null) return; + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && mAuthRetry == false) { + try { + mAuthRetry = true; + mService.writeDescriptor(mClientIf, address, + srvcType, srvcInstId, srvcUuid, charInstId, charUuid, + descrUuid, characteristic.getWriteType(), + AUTHENTICATION_MITM, descriptor.getValue()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + mAuthRetry = false; + + try { + mCallback.onDescriptorWrite(descriptor, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Prepared write transaction completed (or aborted) + * @hide + */ + public void onExecuteWrite(String address, int status) { + if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address + + " status=" + status); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + try { + mCallback.onReliableWriteCompleted(device, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote device RSSI has been read + * @hide + */ + public void onReadRemoteRssi(String address, int rssi, int status) { + if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address + + " rssi=" + rssi + " status=" + status); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + try { + mCallback.onReadRemoteRssi(device, rssi, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + }; + + /** + * Create a BluetoothGatt proxy object. + */ + /*package*/ BluetoothGatt(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mServices = new ArrayList<BluetoothGattService>(); + + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); + if (b != null) { + IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re); + } + } else { + Log.e(TAG, "Unable to get BluetoothManager interface."); + throw new RuntimeException("BluetoothManager inactive"); + } + + //Bind to the service only if the Bluetooth is ON + if(mAdapter.isEnabled()){ + if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Gatt Service"); + } + } + } + + /** + * Close the connection to the gatt service. + */ + /*package*/ void close() { + if (DBG) Log.d(TAG, "close()"); + + unregisterApp(); + mServiceListener = null; + + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); + if (b != null) { + IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re); + } + } + + synchronized (mConnection) { + if (mService != null) { + mService = null; + mContext.unbindService(mConnection); + } + } + } + + /** + * Returns a service by UUID, instance and type. + * @hide + */ + /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, + int instanceId, int type) { + for(BluetoothGattService svc : mServices) { + if (svc.getDevice().equals(device) && + svc.getType() == type && + svc.getInstanceId() == instanceId && + svc.getUuid().equals(uuid)) { + return svc; + } + } + return null; + } + + + /** + * Register an application callback to start using Gatt. + * + * <p>This is an asynchronous call. The callback is used to notify + * success or failure if the function returns true. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback Gatt callback handler that will receive asynchronous + * callbacks. + * @return true, if application was successfully registered. + */ + public boolean registerApp(BluetoothGattCallback callback) { + if (DBG) Log.d(TAG, "registerApp()"); + if (mService == null) return false; + + mCallback = callback; + UUID uuid = UUID.randomUUID(); + if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); + + try { + mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Unregister the current application and callbacks. + */ + public void unregisterApp() { + if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); + if (mService == null || mClientIf == 0) return; + + try { + mCallback = null; + mService.unregisterClient(mClientIf); + mClientIf = 0; + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Starts a scan for Bluetooth LE devices. + * + * <p>Results of the scan are reported using the + * {@link BluetoothGattCallback#onScanResult} callback. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the scan was started successfully + */ + public boolean startScan() { + if (DBG) Log.d(TAG, "startScan()"); + if (mService == null || mClientIf == 0) return false; + + try { + mService.startScan(mClientIf, false); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Starts a scan for Bluetooth LE devices, looking for devices that + * advertise given services. + * + * <p>Devices which advertise all specified services are reported using the + * {@link BluetoothGattCallback#onScanResult} callback. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param serviceUuids Array of services to look for + * @return true, if the scan was started successfully + */ + public boolean startScan(UUID[] serviceUuids) { + if (DBG) Log.d(TAG, "startScan() - with UUIDs"); + if (mService == null || mClientIf == 0) return false; + + try { + ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(serviceUuids[i]); + } + mService.startScanWithUuids(mClientIf, false, uuids); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Stops an ongoing Bluetooth LE device scan. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + */ + public void stopScan() { + if (DBG) Log.d(TAG, "stopScan()"); + if (mService == null || mClientIf == 0) return; + + try { + mService.stopScan(mClientIf, false); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Initiate a connection to a Bluetooth Gatt capable device. + * + * <p>The connection may not be established right away, but will be + * completed when the remote device is available. A + * {@link BluetoothGattCallback#onConnectionStateChange} callback will be + * invoked when the connection state changes as a result of this function. + * + * <p>The autoConnect paramter determines whether to actively connect to + * the remote device, or rather passively scan and finalize the connection + * when the remote device is in range/available. Generally, the first ever + * connection to a device should be direct (autoConnect set to false) and + * subsequent connections to known devices should be invoked with the + * autoConnect parameter set to false. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device to connect to + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @return true, if the connection attempt was initiated successfully + */ + public boolean connect(BluetoothDevice device, boolean autoConnect) { + if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); + if (mService == null || mClientIf == 0) return false; + + try { + mService.clientConnect(mClientIf, device.getAddress(), + autoConnect ? false : true); // autoConnect is inverse of "isDirect" + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Disconnects an established connection, or cancels a connection attempt + * currently in progress. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + */ + public void cancelConnection(BluetoothDevice device) { + if (DBG) Log.d(TAG, "cancelOpen() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return; + + try { + mService.clientDisconnect(mClientIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Discovers services offered by a remote device as well as their + * characteristics and descriptors. + * + * <p>This is an asynchronous operation. Once service discovery is completed, + * the {@link BluetoothGattCallback#onServicesDiscovered} callback is + * triggered. If the discovery was successful, the remote services can be + * retrieved using the {@link #getServices} function. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device to explore + * @return true, if the remote service discovery has been started + */ + public boolean discoverServices(BluetoothDevice device) { + if (DBG) Log.d(TAG, "discoverServices() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return false; + + mServices.clear(); + + try { + mService.discoverServices(mClientIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Returns a list of GATT services offered by the remote device. + * + * <p>This function requires that service discovery has been completed + * for the given device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + * @return List of services on the remote device. Returns an empty list + * if service discovery has not yet been performed. + */ + public List<BluetoothGattService> getServices(BluetoothDevice device) { + List<BluetoothGattService> result = + new ArrayList<BluetoothGattService>(); + + for (BluetoothGattService service : mServices) { + if (service.getDevice().equals(device)) { + result.add(service); + } + } + + return result; + } + + /** + * Returns a {@link BluetoothGattService}, if the requested UUID is + * supported by the remote device. + * + * <p>This function requires that service discovery has been completed + * for the given device. + * + * <p>If multiple instances of the same service (as identified by UUID) + * exist, the first instance of the service is returned. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + * @param uuid UUID of the requested service + * @return BluetoothGattService if supported, or null if the requested + * service is not offered by the remote device. + */ + public BluetoothGattService getService(BluetoothDevice device, UUID uuid) { + for (BluetoothGattService service : mServices) { + if (service.getDevice().equals(device) && + service.getUuid().equals(uuid)) { + return service; + } + } + + return null; + } + + /** + * Reads the requested characteristic from the associated remote device. + * + * <p>This is an asynchronous operation. The result of the read operation + * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} + * callback. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic Characteristic to read from the remote device + * @return true, if the read operation was initiated successfully + */ + public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { + if ((characteristic.getProperties() & + BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false; + + if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); + if (mService == null || mClientIf == 0) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + BluetoothDevice device = service.getDevice(); + if (device == null) return false; + + try { + mService.readCharacteristic(mClientIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Writes a given characteristic and it's values to the associated remote + * device. + * + * <p>Once the write operation has been completed, the + * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, + * reporting the result of the operation. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic Characteristic to write on the remote device + * @return true, if the write operation was initiated successfully + */ + public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { + if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 + && (characteristic.getProperties() & + BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false; + + if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); + if (mService == null || mClientIf == 0) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + BluetoothDevice device = service.getDevice(); + if (device == null) return false; + + try { + mService.writeCharacteristic(mClientIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), + characteristic.getWriteType(), AUTHENTICATION_NONE, + characteristic.getValue()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Reads the value for a given descriptor from the associated remote device. + * + * <p>Once the read operation has been completed, the + * {@link BluetoothGattCallback#onDescriptorRead} callback is + * triggered, signaling the result of the operation. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param descriptor Descriptor value to read from the remote device + * @return true, if the read operation was initiated successfully + */ + public boolean readDescriptor(BluetoothGattDescriptor descriptor) { + if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); + if (mService == null || mClientIf == 0) return false; + + BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); + if (characteristic == null) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + BluetoothDevice device = service.getDevice(); + if (device == null) return false; + + try { + mService.readDescriptor(mClientIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), + new ParcelUuid(descriptor.getUuid()), AUTHENTICATION_NONE); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Write the value of a given descriptor to the associated remote device. + * + * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is + * triggered to report the result of the write operation. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param descriptor Descriptor to write to the associated remote device + * @return true, if the write operation was initiated successfully + */ + public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { + if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); + if (mService == null || mClientIf == 0) return false; + + BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); + if (characteristic == null) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + BluetoothDevice device = service.getDevice(); + if (device == null) return false; + + try { + mService.writeDescriptor(mClientIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), + new ParcelUuid(descriptor.getUuid()), + characteristic.getWriteType(), AUTHENTICATION_NONE, + descriptor.getValue()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Initiates a reliable write transaction for a given remote device. + * + * <p>Once a reliable write transaction has been initiated, all calls + * to {@link #writeCharacteristic} are sent to the remote device for + * verification and queued up for atomic execution. The application will + * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback + * in response to every {@link #writeCharacteristic} call and is responsible + * for verifying if the value has been transmitted accurately. + * + * <p>After all characteristics have been queued up and verified, + * {@link #executeReliableWrite} will execute all writes. If a characteristic + * was not written correctly, calling {@link #abortReliableWrite} will + * cancel the current transaction without commiting any values on the + * remote device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + * @return true, if the reliable write transaction has been initiated + */ + public boolean beginReliableWrite(BluetoothDevice device) { + if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return false; + + try { + mService.beginReliableWrite(mClientIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Executes a reliable write transaction for a given remote device. + * + * <p>This function will commit all queued up characteristic write + * operations for a given remote device. + * + * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is + * invoked to indicate whether the transaction has been executed correctly. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + * @return true, if the request to execute the transaction has been sent + */ + public boolean executeReliableWrite(BluetoothDevice device) { + if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return false; + + try { + mService.endReliableWrite(mClientIf, device.getAddress(), true); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Cancels a reliable write transaction for a given device. + * + * <p>Calling this function will discard all queued characteristic write + * operations for a given remote device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + */ + public void abortReliableWrite(BluetoothDevice device) { + if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return; + + try { + mService.endReliableWrite(mClientIf, device.getAddress(), false); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Enable or disable notifications/indications for a given characteristic. + * + * <p>Once notifications are enabled for a characteristic, a + * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be + * triggered if the remote device indicates that the given characteristic + * has changed. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic The characteristic for which to enable notifications + * @param enable Set to true to enable notifications/indications + * @return true, if the requested notification status was set successfully + */ + public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, + boolean enable) { + if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid() + + " enable: " + enable); + if (mService == null || mClientIf == 0) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + BluetoothDevice device = service.getDevice(); + if (device == null) return false; + + try { + mService.registerForNotification(mClientIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), + enable); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Clears the internal cache and forces a refresh of the services from the + * remote device. + * @hide + */ + public boolean refresh(BluetoothDevice device) { + if (DBG) Log.d(TAG, "refresh() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return false; + + try { + mService.refreshDevice(mClientIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Read the RSSI for a connected remote device. + * + * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be + * invoked when the RSSI value has been read. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + * @return true, if the RSSI value has been requested successfully + */ + public boolean readRemoteRssi(BluetoothDevice device) { + if (DBG) Log.d(TAG, "readRssi() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return false; + + try { + mService.readRemoteRssi(mClientIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Get the current connection state of the profile. + * + * <p>This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote bluetooth device. + * @return State of the profile connection. One of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (DBG) Log.d(TAG,"getConnectionState()"); + if (mService == null) return STATE_DISCONNECTED; + + List<BluetoothDevice> connectedDevices = getConnectedDevices(); + for(BluetoothDevice connectedDevice : connectedDevices) { + if (device.equals(connectedDevice)) { + return STATE_CONNECTED; + } + } + + return STATE_DISCONNECTED; + } + + /** + * Get connected devices for the Gatt profile. + * + * <p> Return the set of devices which are in state {@link #STATE_CONNECTED} + * + * <p>This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return List of devices. The list will be empty on error. + */ + @Override + public List<BluetoothDevice> getConnectedDevices() { + if (DBG) Log.d(TAG,"getConnectedDevices"); + + List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>(); + if (mService == null) return connectedDevices; + + try { + connectedDevices = mService.getDevicesMatchingConnectionStates( + new int[] { BluetoothProfile.STATE_CONNECTED }); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return connectedDevices; + } + + /** + * Get a list of devices that match any of the given connection + * states. + * + * <p> If none of the devices match any of the given states, + * an empty list will be returned. + * + * <p>This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param states Array of states. States can be one of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, + * @return List of devices. The list will be empty on error. + */ + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); + + List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); + if (mService == null) return devices; + + try { + devices = mService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return devices; + } +} diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java new file mode 100644 index 0000000..afa4539 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothGattCallback.java @@ -0,0 +1,157 @@ +/* + * 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 android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +import android.util.Log; + +/** + * This abstract class is used to implement {@link BluetoothGatt} callbacks. + * @hide + */ +public abstract class BluetoothGattCallback { + /** + * Callback to inform change in registration state of the application. + * + * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application + * was successfully registered. + */ + public void onAppRegistered(int status) { + } + + /** + * Callback reporting an LE device found during a device scan initiated + * by the {@link BluetoothGatt#startScan} function. + * + * @param device Identifies the remote device + * @param rssi The RSSI value for the remote device as reported by the + * Bluetooth hardware. 0 if no RSSI value is available. + * @param scanRecord The content of the advertisement record offered by + * the remote device. + */ + public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) { + } + + /** + * Callback indicating when a remote device has been connected or disconnected. + * + * @param device Remote device that has been connected or disconnected. + * @param status Status of the connect or disconnect operation. + * @param newState Returns the new connection state. Can be one of + * {@link BluetoothProfile#STATE_DISCONNECTED} or + * {@link BluetoothProfile#STATE_CONNECTED} + */ + public void onConnectionStateChange(BluetoothDevice device, int status, + int newState) { + } + + /** + * Callback invoked when the list of remote services, characteristics and + * descriptors for the remote device have been updated. + * + * @param device Remote device + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device + * has been explored successfully. + */ + public void onServicesDiscovered(BluetoothDevice device, int status) { + } + + /** + * Callback reporting the result of a characteristic read operation. + * + * @param characteristic Characteristic that was read from the associated + * remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation + * was completed successfully. + */ + public void onCharacteristicRead(BluetoothGattCharacteristic characteristic, + int status) { + } + + /** + * Callback indicating the result of a characteristic write operation. + * + * <p>If this callback is invoked while a reliable write transaction is + * in progress, the value of the characteristic represents the value + * reported by the remote device. An application should compare this + * value to the desired value to be written. If the values don't match, + * the application must abort the reliable write transaction. + * + * @param characteristic Characteristic that was written to the associated + * remote device. + * @param status The result of the write operation + */ + public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic, + int status) { + } + + /** + * Callback triggered as a result of a remote characteristic notification. + * + * @param characteristic Characteristic that has been updated as a result + * of a remote notification event. + */ + public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) { + } + + /** + * Callback reporting the result of a descriptor read operation. + * + * @param descriptor Descriptor that was read from the associated + * remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation + * was completed successfully + */ + public void onDescriptorRead(BluetoothGattDescriptor descriptor, + int status) { + } + + /** + * Callback indicating the result of a descriptor write operation. + * + * @param descriptor Descriptor that was writte to the associated + * remote device. + * @param status The result of the write operation + */ + public void onDescriptorWrite(BluetoothGattDescriptor descriptor, + int status) { + } + + /** + * Callback invoked when a reliable write transaction has been completed. + * + * @param device Remote device + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write + * transaction was executed successfully + */ + public void onReliableWriteCompleted(BluetoothDevice device, int status) { + } + + /** + * Callback reporting the RSSI for a remote device connection. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#readRemoteRssi} function. + * + * @param device Identifies the remote device + * @param rssi The RSSI value for the remote device + * @param status 0 if the RSSI was read successfully + */ + public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) { + } +} diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java new file mode 100644 index 0000000..18492ab --- /dev/null +++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -0,0 +1,670 @@ +/* + * 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 android.bluetooth; + +import java.util.ArrayList; +import java.util.IllegalFormatConversionException; +import java.util.List; +import java.util.UUID; + +/** + * Represents a Bluetooth Gatt Characteristic + * @hide + */ +public class BluetoothGattCharacteristic { + + /** + * Characteristic proprty: Characteristic is broadcastable. + */ + public static final int PROPERTY_BROADCAST = 0x01; + + /** + * Characteristic property: Characteristic is readable. + */ + public static final int PROPERTY_READ = 0x02; + + /** + * Characteristic property: Characteristic can be written without response. + */ + public static final int PROPERTY_WRITE_NO_RESPONSE = 0x04; + + /** + * Characteristic property: Characteristic can be written. + */ + public static final int PROPERTY_WRITE = 0x08; + + /** + * Characteristic property: Characteristic supports notification + */ + public static final int PROPERTY_NOTIFY = 0x10; + + /** + * Characteristic property: Characteristic supports indication + */ + public static final int PROPERTY_INDICATE = 0x20; + + /** + * Characteristic property: Characteristic supports write with signature + */ + public static final int PROPERTY_SIGNED_WRITE = 0x40; + + /** + * Characteristic property: Characteristic has extended properties + */ + public static final int PROPERTY_EXTENDED_PROPS = 0x80; + + /** + * Characteristic read permission + */ + public static final int PERMISSION_READ = 0x01; + + /** + * Characteristic permission: Allow encrypted read operations + */ + public static final int PERMISSION_READ_ENCRYPTED = 0x02; + + /** + * Characteristic permission: Allow reading with man-in-the-middle protection + */ + public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; + + /** + * Characteristic write permission + */ + public static final int PERMISSION_WRITE = 0x10; + + /** + * Characteristic permission: Allow encrypted writes + */ + public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; + + /** + * Characteristic permission: Allow encrypted writes with man-in-the-middle + * protection + */ + public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; + + /** + * Characteristic permission: Allow signed write operations + */ + public static final int PERMISSION_WRITE_SIGNED = 0x80; + + /** + * Characteristic permission: Allow signed write operations with + * man-in-the-middle protection + */ + public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; + + /** + * Write characteristic, requesting acknoledgement by the remote device + */ + public static final int WRITE_TYPE_DEFAULT = 0x02; + + /** + * Wrtite characteristic without requiring a response by the remote device + */ + public static final int WRITE_TYPE_NO_RESPONSE = 0x01; + + /** + * Write characteristic including and authenticated signature + */ + public static final int WRITE_TYPE_SIGNED = 0x04; + + /** + * Characteristic value format type uint8 + */ + public static final int FORMAT_UINT8 = 0x11; + + /** + * Characteristic value format type uint16 + */ + public static final int FORMAT_UINT16 = 0x12; + + /** + * Characteristic value format type uint32 + */ + public static final int FORMAT_UINT32 = 0x14; + + /** + * Characteristic value format type sint8 + */ + public static final int FORMAT_SINT8 = 0x21; + + /** + * Characteristic value format type sint16 + */ + public static final int FORMAT_SINT16 = 0x22; + + /** + * Characteristic value format type sint32 + */ + public static final int FORMAT_SINT32 = 0x24; + + /** + * Characteristic value format type sfloat (16-bit float) + */ + public static final int FORMAT_SFLOAT = 0x32; + + /** + * Characteristic value format type float (32-bit float) + */ + public static final int FORMAT_FLOAT = 0x34; + + + /** + * The UUID of this characteristic. + * @hide + */ + protected UUID mUuid; + + /** + * Instance ID for this characteristic. + * @hide + */ + protected int mInstance; + + /** + * Characteristic properties. + * @hide + */ + protected int mProperties; + + /** + * Characteristic permissions. + * @hide + */ + protected int mPermissions; + + /** + * Key size (default = 16). + * @hide + */ + protected int mKeySize = 16; + + /** + * Write type for this characteristic. + * See WRITE_TYPE_* constants. + * @hide + */ + protected int mWriteType; + + /** + * Back-reference to the service this characteristic belongs to. + * @hide + */ + protected BluetoothGattService mService; + + /** + * The cached value of this characteristic. + * @hide + */ + protected byte[] mValue; + + /** + * List of descriptors included in this characteristic. + */ + protected List<BluetoothGattDescriptor> mDescriptors; + + /** + * Create a new BluetoothGattCharacteristic + * @hide + */ + /*package*/ BluetoothGattCharacteristic(BluetoothGattService service, + UUID uuid, int instanceId, + int properties, int permissions) { + mUuid = uuid; + mInstance = instanceId; + mProperties = properties; + mPermissions = permissions; + mService = service; + mValue = null; + mDescriptors = new ArrayList<BluetoothGattDescriptor>(); + + if ((mProperties & PROPERTY_WRITE_NO_RESPONSE) != 0) { + mWriteType = WRITE_TYPE_NO_RESPONSE; + } else { + mWriteType = WRITE_TYPE_DEFAULT; + } + } + + /** + * Returns the deisred key size. + * @hide + */ + /*package*/ int getKeySize() { + return mKeySize; + } + + /** + * Add a descriptor to this characteristic + * @hide + */ + /*package*/ void addDescriptor(BluetoothGattDescriptor descriptor) { + mDescriptors.add(descriptor); + } + + /** + * Returns the service this characteristic belongs to. + * @return The asscociated service + */ + public BluetoothGattService getService() { + return mService; + } + + /** + * Returns the UUID of this characteristic + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return UUID of this characteristic + */ + public UUID getUuid() { + return mUuid; + } + + /** + * Returns the instance ID for this characteristic. + * + * <p>If a remote device offers multiple characteristics with the same UUID, + * the instance ID is used to distuinguish between characteristics. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Instance ID of this characteristic + */ + public int getInstanceId() { + return mInstance; + } + + /** + * Returns the properties of this characteristic. + * + * <p>The properties contain a bit mask of property flags indicating + * the features of this characteristic. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Properties of this characteristic + */ + public int getProperties() { + return mProperties; + } + + /** + * Returns the permissions for this characteristic. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Permissions of this characteristic + */ + public int getPermissions() { + return mPermissions; + } + + /** + * Gets the write type for this characteristic. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Write type for this characteristic + */ + public int getWriteType() { + return mWriteType; + } + + /** + * Set the write type for this characteristic + * + * <p>Setting the write type of a characteristic determines how the + * {@link BluetoothGatt#writeCharacteristic} function write this + * characteristic. + * + * <p>The default write type for a characteristic is + * {@link #WRITE_TYPE_DEFAULT}. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param writeType The write type to for this characteristic. Can be one + * of: + * {@link #WRITE_TYPE_DEFAULT}, + * {@link #WRITE_TYPE_NO_RESPONSE} or + * {@link #WRITE_TYPE_SIGNED}. + */ + public void setWriteType(int writeType) { + mWriteType = writeType; + } + + /** + * Returns a list of descriptors for this characteristic. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Descriptors for this characteristic + */ + public List<BluetoothGattDescriptor> getDescriptors() { + return mDescriptors; + } + + /** + * Returns a descriptor with a given UUID out of the list of + * descriptors for this characteristic. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Gatt descriptor object or null if no descriptor with the + * given UUID was found. + */ + public BluetoothGattDescriptor getDescriptor(UUID uuid) { + for(BluetoothGattDescriptor descriptor : mDescriptors) { + if (descriptor.getUuid().equals(uuid)) { + return descriptor; + } + } + return null; + } + + /** + * Get the stored value for this characteristic. + * + * <p>This function returns the stored value for this characteristic as + * retrieved by calling {@link BluetoothGatt#readCharacteristic}. To cached + * value of the characteristic is updated as a result of a read characteristic + * operation or if a characteristic update notification has been received. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Cached value of the characteristic + */ + public byte[] getValue() { + return mValue; + } + + /** + * Return the stored value of this characteristic. + * + * <p>The formatType parameter determines how the characteristic value + * is to be interpreted. For example, settting formatType to + * {@link #FORMAT_UINT16} specifies that the first two bytes of the + * characteristic value at the given offset are interpreted to generate the + * return value. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param formatType The format type used to interpret the characteristic + * value. + * @param offset Offset at which the integer value can be found. + * @return Cached value of the characteristic or null of offset exceeds + * value size. + */ + public Integer getIntValue(int formatType, int offset) { + if ((offset + getTypeLen(formatType)) > mValue.length) return null; + + switch (formatType) { + case FORMAT_UINT8: + return unsignedByteToInt(mValue[offset]); + + case FORMAT_UINT16: + return unsignedBytesToInt(mValue[offset], mValue[offset+1]); + + case FORMAT_UINT32: + return unsignedBytesToInt(mValue[offset], mValue[offset+1], + mValue[offset+2], mValue[offset+3]); + case FORMAT_SINT8: + return unsignedToSigned(unsignedByteToInt(mValue[offset]), 8); + + case FORMAT_SINT16: + return unsignedToSigned(unsignedBytesToInt(mValue[offset], + mValue[offset+1]), 16); + + case FORMAT_SINT32: + return unsignedToSigned(unsignedBytesToInt(mValue[offset], + mValue[offset+1], mValue[offset+2], mValue[offset+3]), 32); + } + + return null; + } + + /** + * Return the stored value of this characteristic. + * <p>See {@link #getValue} for details. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param formatType The format type used to interpret the characteristic + * value. + * @param offset Offset at which the float value can be found. + * @return Cached value of the characteristic at a given offset or null + * if the requested offset exceeds the value size. + */ + public Float getFloatValue(int formatType, int offset) { + if ((offset + getTypeLen(formatType)) > mValue.length) return null; + + switch (formatType) { + case FORMAT_SFLOAT: + return bytesToFloat(mValue[offset], mValue[offset+1]); + + case FORMAT_FLOAT: + return bytesToFloat(mValue[offset], mValue[offset+1], + mValue[offset+2], mValue[offset+3]); + } + + return null; + } + + /** + * Return the stored value of this characteristic. + * <p>See {@link #getValue} for details. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * @param offset Offset at which the string value can be found. + * @return Cached value of the characteristic + */ + public String getStringValue(int offset) { + if (offset > mValue.length) return null; + byte[] strBytes = new byte[mValue.length - offset]; + for (int i=0; i != (mValue.length-offset); ++i) strBytes[i] = mValue[offset+i]; + return new String(strBytes); + } + + /** + * Updates the locally stored value of this characteristic. + * + * <p>This function modifies the locally stored cached value of this + * characteristic. To send the value to the remote device, call + * {@link BluetoothGatt#writeCharacteristic} to send the value to the + * remote device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param value New value for this characteristic + * @return true if the locally stored value has been set, false if the + * requested value could not be stored locally. + */ + public boolean setValue(byte[] value) { + mValue = value; + return true; + } + + /** + * Set the locally stored value of this characteristic. + * <p>See {@link #setValue(byte[])} for details. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param value New value for this characteristic + * @param formatType Integer format type used to transform the value parameter + * @param offset Offset at which the value should be placed + * @return true if the locally stored value has been set + */ + public boolean setValue(int value, int formatType, int offset) { + int len = offset + getTypeLen(formatType); + if (mValue == null) mValue = new byte[len]; + if (len > mValue.length) return false; + + switch (formatType) { + case FORMAT_SINT8: + value = intToSignedBits(value, 8); + // Fall-through intended + case FORMAT_UINT8: + mValue[offset] = (byte)(value & 0xFF); + break; + + case FORMAT_SINT16: + value = intToSignedBits(value, 16); + // Fall-through intended + case FORMAT_UINT16: + mValue[offset++] = (byte)(value & 0xFF); + mValue[offset] = (byte)((value >> 8) & 0xFF); + break; + + case FORMAT_SINT32: + value = intToSignedBits(value, 32); + // Fall-through intended + case FORMAT_UINT32: + mValue[offset++] = (byte)(value & 0xFF); + mValue[offset++] = (byte)((value >> 8) & 0xFF); + mValue[offset] = (byte)((value >> 16) & 0xFF); + break; + + default: + return false; + } + return true; + } + + /** + * Set the locally stored value of this characteristic. + * <p>See {@link #setValue(byte[])} for details. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * @param mantissa Mantissa for this characteristic + * @param exponent exponent value for this characteristic + * @param formatType Float format type used to transform the value parameter + * @param offset Offset at which the value should be placed + * @return true if the locally stored value has been set + */ + public boolean setValue(int mantissa, int exponent, int formatType, int offset) { + int len = offset + getTypeLen(formatType); + if (mValue == null) mValue = new byte[len]; + if (len > mValue.length) return false; + + switch (formatType) { + case FORMAT_SFLOAT: + mantissa = intToSignedBits(mantissa, 12); + exponent = intToSignedBits(exponent, 4); + mValue[offset++] = (byte)(mantissa & 0xFF); + mValue[offset] = (byte)((mantissa >> 8) & 0x0F); + mValue[offset] += (byte)((exponent & 0x0F) << 4); + break; + + case FORMAT_FLOAT: + mantissa = intToSignedBits(mantissa, 24); + exponent = intToSignedBits(exponent, 8); + mValue[offset++] = (byte)(mantissa & 0xFF); + mValue[offset++] = (byte)((mantissa >> 8) & 0xFF); + mValue[offset++] = (byte)((mantissa >> 16) & 0xFF); + mValue[offset] += (byte)(exponent & 0xFF); + break; + + default: + return false; + } + + return true; + } + + /** + * Set the locally stored value of this characteristic. + * <p>See {@link #setValue(byte[])} for details. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * @param value New value for this characteristic + * @return true if the locally stored value has been set + */ + public boolean setValue(String value) { + mValue = value.getBytes(); + return true; + } + + /** + * Returns the size of a give value type. + * @hide + */ + private int getTypeLen(int formatType) { + return formatType & 0xF; + } + + /** + * Convert a signed byte to an unsigned int. + * @hide + */ + private int unsignedByteToInt(byte b) { + return b & 0xFF; + } + + /** + * Convert signed bytes to a 16-bit unsigned int. + * @hide + */ + private int unsignedBytesToInt(byte b0, byte b1) { + return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)); + } + + /** + * Convert signed bytes to a 32-bit unsigned int. + * @hide + */ + private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) { + return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)) + + (unsignedByteToInt(b2) << 16) + (unsignedByteToInt(b3) << 24); + } + + /** + * Convert signed bytes to a 16-bit short float value. + * @hide + */ + private float bytesToFloat(byte b0, byte b1) { + int mantissa = unsignedToSigned(unsignedByteToInt(b0) + + ((unsignedByteToInt(b1) & 0x0F) << 8), 12); + int exponent = unsignedToSigned(unsignedByteToInt(b1) >> 4, 4); + return (float)(mantissa * Math.pow(10, exponent)); + } + + /** + * Convert signed bytes to a 32-bit short float value. + * @hide + */ + private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) { + int mantissa = unsignedToSigned(unsignedByteToInt(b0) + + (unsignedByteToInt(b1) << 8) + + (unsignedByteToInt(b2) << 16), 24); + return (float)(mantissa * Math.pow(10, b3)); + } + + /** + * Convert an unsigned integer value to a two's-complement encoded + * signed value. + * @hide + */ + private int unsignedToSigned(int unsigned, int size) { + if ((unsigned & (1 << size-1)) != 0) { + unsigned = -1 * ((1 << size-1) - (unsigned & ((1 << size-1) - 1))); + } + return unsigned; + } + + /** + * Convert an integer into the signed bits of a given length. + * @hide + */ + private int intToSignedBits(int i, int size) { + if (i < 0) { + i = (1 << size-1) + (i & ((1 << size-1) - 1)); + } + return i; + } +} diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java new file mode 100644 index 0000000..ba1f28a --- /dev/null +++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java @@ -0,0 +1,185 @@ +/* + * 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 android.bluetooth; + +import java.util.UUID; + +/** + * Represents a Bluetooth Gatt Descriptor + * @hide + */ +public class BluetoothGattDescriptor { + + /** + * Value used to enable notification for a client configuration descriptor + */ + public static final byte[] ENABLE_NOTIFICATION_VALUE = {0x01, 0x00}; + + /** + * Value used to enable indication for a client configuration descriptor + */ + public static final byte[] ENABLE_INDICATION_VALUE = {0x02, 0x00}; + + /** + * Value used to disable notifications or indicatinos + */ + public static final byte[] DISABLE_NOTIFICATION_VALUE = {0x00, 0x00}; + + /** + * Descriptor read permission + */ + public static final int PERMISSION_READ = 0x01; + + /** + * Descriptor permission: Allow encrypted read operations + */ + public static final int PERMISSION_READ_ENCRYPTED = 0x02; + + /** + * Descriptor permission: Allow reading with man-in-the-middle protection + */ + public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; + + /** + * Descriptor write permission + */ + public static final int PERMISSION_WRITE = 0x10; + + /** + * Descriptor permission: Allow encrypted writes + */ + public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; + + /** + * Descriptor permission: Allow encrypted writes with man-in-the-middle + * protection + */ + public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; + + /** + * Descriptor permission: Allow signed write operations + */ + public static final int PERMISSION_WRITE_SIGNED = 0x80; + + /** + * Descriptor permission: Allow signed write operations with + * man-in-the-middle protection + */ + public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; + + /** + * The UUID of this descriptor. + * @hide + */ + protected UUID mUuid; + + /** + * Permissions for this descriptor + * @hide + */ + protected int mPermissions; + + /** + * Back-reference to the characteristic this descriptor belongs to. + * @hide + */ + protected BluetoothGattCharacteristic mCharacteristic; + + /** + * The value for this descriptor. + * @hide + */ + protected byte[] mValue; + + /** + * Create a new BluetoothGattDescriptor. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic The characteristic this descriptor belongs to + * @param uuid The UUID for this descriptor + * @param permissions Permissions for this descriptor + */ + /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, + int permissions) { + mCharacteristic = characteristic; + mUuid = uuid; + mPermissions = permissions; + } + + /** + * Returns the characteristic this descriptor belongs to. + * @return The characteristic. + */ + public BluetoothGattCharacteristic getCharacteristic() { + return mCharacteristic; + } + + /** + * Returns the UUID of this descriptor. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return UUID of this descriptor + */ + public UUID getUuid() { + return mUuid; + } + + /** + * Returns the permissions for this descriptor. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Permissions of this descriptor + */ + public int getPermissions() { + return mPermissions; + } + + /** + * Returns the stored value for this descriptor + * + * <p>This function returns the stored value for this descriptor as + * retrieved by calling {@link BluetoothGatt#readDescriptor}. To cached + * value of the descriptor is updated as a result of a descriptor read + * operation. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Cached value of the descriptor + */ + public byte[] getValue() { + return mValue; + } + + /** + * Updates the locally stored value of this descriptor. + * + * <p>This function modifies the locally stored cached value of this + * descriptor. To send the value to the remote device, call + * {@link BluetoothGatt#writeDescriptor} to send the value to the + * remote device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param value New value for this descriptor + * @return true if the locally stored value has been set, false if the + * requested value could not be stored locally. + */ + public boolean setValue(byte[] value) { + mValue = value; + return true; + } +} diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java new file mode 100644 index 0000000..91a1a94 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -0,0 +1,900 @@ +/* + * 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 android.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.bluetooth.IBluetoothManager; +import android.bluetooth.IBluetoothStateChangeCallback; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Public API for the Bluetooth Gatt Profile server role. + * + * <p>This class provides Bluetooth Gatt server role functionality, + * allowing applications to create and advertise Bluetooth Smart services + * and characteristics. + * + * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service + * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the + * BluetoothGatt proxy object. + * @hide + */ +public final class BluetoothGattServer implements BluetoothProfile { + private static final String TAG = "BluetoothGattServer"; + private static final boolean DBG = true; + + private Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetoothGatt mService; + private BluetoothGattServerCallback mCallback; + private int mServerIf; + + private List<BluetoothGattService> mServices; + + /** + * Bluetooth state change handlers + */ + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (DBG) Log.d(TAG,"Unbinding service..."); + synchronized (mConnection) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } else { + synchronized (mConnection) { + try { + if (mService == null) { + if (DBG) Log.d(TAG,"Binding service..."); + if (!mContext.bindService(new + Intent(IBluetoothGatt.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth GATT Service"); + } + } + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; + + /** + * Service binder handling + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothGatt.Stub.asInterface(service); + ServiceListener serviceListner = mServiceListener; + if (serviceListner != null) { + serviceListner.onServiceConnected(BluetoothProfile.GATT_SERVER, + BluetoothGattServer.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + ServiceListener serviceListner = mServiceListener; + if (serviceListner != null) { + serviceListner.onServiceDisconnected(BluetoothProfile.GATT_SERVER); + } + } + }; + + /** + * Bluetooth GATT interface callbacks + */ + private final IBluetoothGattServerCallback mBluetoothGattServerCallback = + new IBluetoothGattServerCallback.Stub() { + /** + * Application interface registered - app is ready to go + * @hide + */ + public void onServerRegistered(int status, int serverIf) { + if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status + + " serverIf=" + serverIf); + mServerIf = serverIf; + try { + mCallback.onAppRegistered(status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Callback reporting an LE scan result. + * @hide + */ + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + + try { + mCallback.onScanResult(mAdapter.getRemoteDevice(address), + rssi, advData); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Server connection state changed + * @hide + */ + public void onServerConnectionState(int status, int serverIf, + boolean connected, String address) { + if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status + + " serverIf=" + serverIf + " device=" + address); + try { + mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, + connected ? BluetoothProfile.STATE_CONNECTED : + BluetoothProfile.STATE_DISCONNECTED); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Service has been added + * @hide + */ + public void onServiceAdded(int status, int srvcType, + int srvcInstId, ParcelUuid srvcId) { + UUID srvcUuid = srvcId.getUuid(); + if (DBG) Log.d(TAG, "onServiceAdded() - service=" + srvcUuid + + "status=" + status); + + BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); + if (service == null) return; + + try { + mCallback.onServiceAdded((int)status, service); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote client characteristic read request. + * @hide + */ + public void onCharacteristicReadRequest(String address, int transId, + int offset, boolean isLong, int srvcType, int srvcInstId, + ParcelUuid srvcId, int charInstId, ParcelUuid charId) { + UUID srvcUuid = srvcId.getUuid(); + UUID charUuid = charId.getUuid(); + if (DBG) Log.d(TAG, "onCharacteristicReadRequest() - " + + "service=" + srvcUuid + ", characteristic=" + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid); + if (characteristic == null) return; + + try { + mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote client descriptor read request. + * @hide + */ + public void onDescriptorReadRequest(String address, int transId, + int offset, boolean isLong, int srvcType, int srvcInstId, + ParcelUuid srvcId, int charInstId, ParcelUuid charId, + ParcelUuid descrId) { + UUID srvcUuid = srvcId.getUuid(); + UUID charUuid = charId.getUuid(); + UUID descrUuid = descrId.getUuid(); + if (DBG) Log.d(TAG, "onCharacteristicReadRequest() - " + + "service=" + srvcUuid + ", characteristic=" + charUuid + + "descriptor=" + descrUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); + if (characteristic == null) return; + + BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid); + if (descriptor == null) return; + + try { + mCallback.onDescriptorReadRequest(device, transId, offset, descriptor); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote client characteristic write request. + * @hide + */ + public void onCharacteristicWriteRequest(String address, int transId, + int offset, int length, boolean isPrep, boolean needRsp, + int srvcType, int srvcInstId, ParcelUuid srvcId, + int charInstId, ParcelUuid charId, byte[] value) { + UUID srvcUuid = srvcId.getUuid(); + UUID charUuid = charId.getUuid(); + if (DBG) Log.d(TAG, "onCharacteristicWriteRequest() - " + + "service=" + srvcUuid + ", characteristic=" + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); + if (characteristic == null) return; + + try { + mCallback.onCharacteristicWriteRequest(device, transId, characteristic, + isPrep, needRsp, offset, value); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + + } + + /** + * Remote client descriptor write request. + * @hide + */ + public void onDescriptorWriteRequest(String address, int transId, + int offset, int length, boolean isPrep, boolean needRsp, + int srvcType, int srvcInstId, ParcelUuid srvcId, + int charInstId, ParcelUuid charId, ParcelUuid descrId, + byte[] value) { + UUID srvcUuid = srvcId.getUuid(); + UUID charUuid = charId.getUuid(); + UUID descrUuid = descrId.getUuid(); + if (DBG) Log.d(TAG, "onDescriptorWriteRequest() - " + + "service=" + srvcUuid + ", characteristic=" + charUuid + + "descriptor=" + descrUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + + BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); + if (characteristic == null) return; + + BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid); + if (descriptor == null) return; + + try { + mCallback.onDescriptorWriteRequest(device, transId, descriptor, + isPrep, needRsp, offset, value); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Execute pending writes. + * @hide + */ + public void onExecuteWrite(String address, int transId, + boolean execWrite) { + if (DBG) Log.d(TAG, "onExecuteWrite() - " + + "device=" + address + ", transId=" + transId + + "execWrite=" + execWrite); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onExecuteWrite(device, transId, execWrite); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + }; + + /** + * Create a BluetoothGattServer proxy object. + */ + /*package*/ BluetoothGattServer(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mServices = new ArrayList<BluetoothGattService>(); + + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); + if (b != null) { + IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re); + } + } else { + Log.e(TAG, "Unable to get BluetoothManager interface."); + throw new RuntimeException("BluetoothManager inactive"); + } + + //Bind to the service only if the Bluetooth is ON + if(mAdapter.isEnabled()){ + if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Gatt Service"); + } + } + } + + /** + * Close the connection to the gatt service. + */ + /*package*/ void close() { + if (DBG) Log.d(TAG, "close()"); + + unregisterApp(); + mServiceListener = null; + + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); + if (b != null) { + IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re); + } + } + + synchronized (mConnection) { + if (mService != null) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + + /** + * Returns a service by UUID, instance and type. + * @hide + */ + /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { + for(BluetoothGattService svc : mServices) { + if (svc.getType() == type && + svc.getInstanceId() == instanceId && + svc.getUuid().equals(uuid)) { + return svc; + } + } + return null; + } + + /** + * Register an application callback to start using Gatt. + * + * <p>This is an asynchronous call. The callback is used to notify + * success or failure if the function returns true. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback Gatt callback handler that will receive asynchronous + * callbacks. + * @return true, if application was successfully registered. + */ + public boolean registerApp(BluetoothGattServerCallback callback) { + if (DBG) Log.d(TAG, "registerApp()"); + if (mService == null) return false; + + mCallback = callback; + UUID uuid = UUID.randomUUID(); + if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); + + try { + mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Unregister the current application and callbacks. + */ + public void unregisterApp() { + if (DBG) Log.d(TAG, "unregisterApp() - mServerIf=" + mServerIf); + if (mService == null || mServerIf == 0) return; + + try { + mCallback = null; + mService.unregisterServer(mServerIf); + mServerIf = 0; + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Starts a scan for Bluetooth LE devices. + * + * <p>Results of the scan are reported using the + * {@link BluetoothGattServerCallback#onScanResult} callback. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the scan was started successfully + */ + public boolean startScan() { + if (DBG) Log.d(TAG, "startScan()"); + if (mService == null || mServerIf == 0) return false; + + try { + mService.startScan(mServerIf, true); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Starts a scan for Bluetooth LE devices, looking for devices that + * advertise given services. + * + * <p>Devices which advertise all specified services are reported using the + * {@link BluetoothGattServerCallback#onScanResult} callback. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param serviceUuids Array of services to look for + * @return true, if the scan was started successfully + */ + public boolean startScan(UUID[] serviceUuids) { + if (DBG) Log.d(TAG, "startScan() - with UUIDs"); + if (mService == null || mServerIf == 0) return false; + + try { + ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(serviceUuids[i]); + } + mService.startScanWithUuids(mServerIf, true, uuids); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Stops an ongoing Bluetooth LE device scan. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + */ + public void stopScan() { + if (DBG) Log.d(TAG, "stopScan()"); + if (mService == null || mServerIf == 0) return; + + try { + mService.stopScan(mServerIf, true); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Initiate a connection to a Bluetooth Gatt capable device. + * + * <p>The connection may not be established right away, but will be + * completed when the remote device is available. A + * {@link BluetoothGattCallback#onConnectionStateChange} callback will be + * invoked when the connection state changes as a result of this function. + * + * <p>The autoConnect paramter determines whether to actively connect to + * the remote device, or rather passively scan and finalize the connection + * when the remote device is in range/available. Generally, the first ever + * connection to a device should be direct (autoConnect set to false) and + * subsequent connections to known devices should be invoked with the + * autoConnect parameter set to false. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device to connect to + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @return true, if the connection attempt was initiated successfully + */ + public boolean connect(BluetoothDevice device, boolean autoConnect) { + if (DBG) Log.d(TAG, "connect: " + device.getAddress() + ", auto: " + autoConnect); + if (mService == null || mServerIf == 0) return false; + + try { + mService.serverConnect(mServerIf, device.getAddress(), + autoConnect ? false : true); // autoConnect is inverse of "isDirect" + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Disconnects an established connection, or cancels a connection attempt + * currently in progress. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + */ + public void cancelConnection(BluetoothDevice device) { + if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress()); + if (mService == null || mServerIf == 0) return; + + try { + mService.serverDisconnect(mServerIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Send a response to a read or write request to a remote device. + * + * <p>This function must be invoked in when a remote read/write request + * is received by one of these callback methots: + * + * <ul> + * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest} + * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest} + * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest} + * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest} + * </ul> + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device The remote device to send this response to + * @param requestId The ID of the request that was received with the callback + * @param status The status of the request to be sent to the remote devices + * @param offset Value offset for partial read/write response + * @param value The value of the attribute that was read/written (optional) + */ + public boolean sendResponse(BluetoothDevice device, int requestId, + int status, int offset, byte[] value) { + if (DBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress()); + if (mService == null || mServerIf == 0) return false; + + try { + mService.sendResponse(mServerIf, device.getAddress(), requestId, + status, offset, value); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + return true; + } + + /** + * Send a notification or indication that a local characteristic has been + * updated. + * + * <p>A notification or indication is sent to the remote device to signal + * that the characteristic has been updated. This function should be invoked + * for every client that requests notifications/indications by writing + * to the "Client Configuration" descriptor for the given characteristic. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device The remote device to receive the notification/indication + * @param characteristic The local characteristic that has been updated + * @param confirm true to request confirmation from the client (indication), + * false to send a notification + * @return true, if the notification has been triggered successfully + */ + public boolean notifyCharacteristicChanged(BluetoothDevice device, + BluetoothGattCharacteristic characteristic, boolean confirm) { + if (DBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); + if (mService == null || mServerIf == 0) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + try { + mService.sendNotification(mServerIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), confirm, + characteristic.getValue()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Add a service to the list of services to be advertised. + * + * <p>Once a service has been addded to the the list, the service and it's + * included characteristics will be advertised by the local device. + * + * <p>If the local device is already advertising services when this function + * is called, a service update notification will be sent to all clients. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param service Service to be added to the list of services advertised + * by this device. + * @return true, if the service has been added successfully + */ + public boolean addService(BluetoothGattService service) { + if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); + if (mService == null || mServerIf == 0) return false; + + mServices.add(service); + + try { + mService.beginServiceDeclaration(mServerIf, service.getType(), + service.getInstanceId(), service.getHandles(), + new ParcelUuid(service.getUuid())); + + List<BluetoothGattService> includedServices = service.getIncludedServices(); + for (BluetoothGattService includedService : includedServices) { + mService.addIncludedService(mServerIf, + includedService.getType(), + includedService.getInstanceId(), + new ParcelUuid(includedService.getUuid())); + } + + List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics(); + for (BluetoothGattCharacteristic characteristic : characteristics) { + int permission = ((characteristic.getKeySize() - 7) << 12) + + characteristic.getPermissions(); + mService.addCharacteristic(mServerIf, + new ParcelUuid(characteristic.getUuid()), + characteristic.getProperties(), permission); + + List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors(); + for (BluetoothGattDescriptor descriptor: descriptors) { + mService.addDescriptor(mServerIf, + new ParcelUuid(descriptor.getUuid()), + descriptor.getPermissions()); + } + } + + mService.endServiceDeclaration(mServerIf); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Removes a service from the list of services to be advertised. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param service Service to beremoved. + * @return true, if the service has been removed + */ + public boolean removeService(BluetoothGattService service) { + if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid()); + if (mService == null || mServerIf == 0) return false; + + BluetoothGattService intService = getService(service.getUuid(), + service.getInstanceId(), service.getType()); + if (intService == null) return false; + + try { + mService.removeService(mServerIf, service.getType(), + service.getInstanceId(), new ParcelUuid(service.getUuid())); + mServices.remove(intService); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Remove all services from the list of advertised services. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + */ + public void clearServices() { + if (DBG) Log.d(TAG, "clearServices()"); + if (mService == null || mServerIf == 0) return; + + try { + mService.clearServices(mServerIf); + mServices.clear(); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Returns a list of GATT services offered bu this device. + * + * <p>An application must call {@link #addService} to add a serice to the + * list of services offered by this device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return List of services. Returns an empty list + * if no services have been added yet. + */ + public List<BluetoothGattService> getServices() { + return mServices; + } + + /** + * Returns a {@link BluetoothGattService} from the list of services offered + * by this device. + * + * <p>If multiple instances of the same service (as identified by UUID) + * exist, the first instance of the service is returned. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid UUID of the requested service + * @return BluetoothGattService if supported, or null if the requested + * service is not offered by this device. + */ + public BluetoothGattService getService(UUID uuid) { + for (BluetoothGattService service : mServices) { + if (service.getUuid().equals(uuid)) { + return service; + } + } + + return null; + } + + /** + * Get the current connection state of the profile. + * + * <p>This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote bluetooth device. + * @return State of the profile connection. One of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (DBG) Log.d(TAG,"getConnectionState()"); + if (mService == null) return STATE_DISCONNECTED; + + List<BluetoothDevice> connectedDevices = getConnectedDevices(); + for(BluetoothDevice connectedDevice : connectedDevices) { + if (device.equals(connectedDevice)) { + return STATE_CONNECTED; + } + } + + return STATE_DISCONNECTED; + } + + /** + * Get connected devices for the Gatt profile. + * + * <p> Return the set of devices which are in state {@link #STATE_CONNECTED} + * + * <p>This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return List of devices. The list will be empty on error. + */ + @Override + public List<BluetoothDevice> getConnectedDevices() { + if (DBG) Log.d(TAG,"getConnectedDevices"); + + List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>(); + if (mService == null) return connectedDevices; + + try { + connectedDevices = mService.getDevicesMatchingConnectionStates( + new int[] { BluetoothProfile.STATE_CONNECTED }); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return connectedDevices; + } + + /** + * Get a list of devices that match any of the given connection + * states. + * + * <p> If none of the devices match any of the given states, + * an empty list will be returned. + * + * <p>This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param states Array of states. States can be one of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, + * @return List of devices. The list will be empty on error. + */ + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); + + List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); + if (mService == null) return devices; + + try { + devices = mService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return devices; + } +} diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java new file mode 100644 index 0000000..4f608ff --- /dev/null +++ b/core/java/android/bluetooth/BluetoothGattServerCallback.java @@ -0,0 +1,158 @@ +/* + * 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 android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +import android.util.Log; + +/** + * This abstract class is used to implement {@link BluetoothGattServer} callbacks. + * @hide + */ +public abstract class BluetoothGattServerCallback { + /** + * Callback to inform change in registration state of the application. + * + * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application + * was successfully registered. + */ + public void onAppRegistered(int status) { + } + + /** + * Callback reporting an LE device found during a device scan initiated + * by the {@link BluetoothGattServer#startScan} function. + * + * @param device Identifies the remote device + * @param rssi The RSSI value for the remote device as reported by the + * Bluetooth hardware. 0 if no RSSI value is available. + * @param scanRecord The content of the advertisement record offered by + * the remote device. + */ + public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) { + } + + /** + * Callback indicating when a remote device has been connected or disconnected. + * + * @param device Remote device that has been connected or disconnected. + * @param status Status of the connect or disconnect operation. + * @param newState Returns the new connection state. Can be one of + * {@link BluetoothProfile#STATE_DISCONNECTED} or + * {@link BluetoothProfile#STATE_CONNECTED} + */ + public void onConnectionStateChange(BluetoothDevice device, int status, + int newState) { + } + + /** + * Indicates whether a local service has been added successfully. + * + * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service + * was added successfully. + * @param service The service that has been added + */ + public void onServiceAdded(int status, BluetoothGattService service) { + } + + /** + * A remote client has requested to read a local characteristic. + * + * <p>An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the read operation + * @param requestId The Id of the request + * @param offset Offset into the value of the characteristic + * @param characteristic Characteristic to be read + */ + public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, + int offset, BluetoothGattCharacteristic characteristic) { + } + + /** + * A remote client has requested to write to a local characteristic. + * + * <p>An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operation + * @param requestId The Id of the request + * @param characteristic Characteristic to be written to. + * @param preparedWrite true, if this write operation should be queued for + * later execution. + * @param responseNeeded true, if the remote device requires a response + * @param offset The offset given for the value + * @param value The value the client wants to assign to the characteristic + */ + public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, + BluetoothGattCharacteristic characteristic, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { + } + + /** + * A remote client has requested to read a local descriptor. + * + * <p>An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the read operation + * @param requestId The Id of the request + * @param offset Offset into the value of the characteristic + * @param descriptor Descriptor to be read + */ + public void onDescriptorReadRequest(BluetoothDevice device, int requestId, + int offset, BluetoothGattDescriptor descriptor) { + } + + /** + * A remote client has requested to write to a local descriptor. + * + * <p>An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operation + * @param requestId The Id of the request + * @param descriptor Descriptor to be written to. + * @param preparedWrite true, if this write operation should be queued for + * later execution. + * @param responseNeeded true, if the remote device requires a response + * @param offset The offset given for the value + * @param value The value the client wants to assign to the descriptor + */ + public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, + BluetoothGattDescriptor descriptor, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { + } + + /** + * Execute all pending write operations for this device. + * + * <p>An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operations + * @param requestId The Id of the request + * @param execute Whether the pending writes should be executed (true) or + * cancelled (false) + */ + public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { + } +} diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java new file mode 100644 index 0000000..6a3ce66 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothGattService.java @@ -0,0 +1,232 @@ +/* + * 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 android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Represents a Bluetooth Gatt Service + * @hide + */ +public class BluetoothGattService { + + /** + * Primary service + */ + public static final int SERVICE_TYPE_PRIMARY = 0; + + /** + * Secondary service (included by primary services) + */ + public static final int SERVICE_TYPE_SECONDARY = 1; + + + /** + * The remote device his service is associated with. + * This applies to client applications only. + * @hide + */ + protected BluetoothDevice mDevice; + + /** + * The UUID of this service. + * @hide + */ + protected UUID mUuid; + + /** + * Instance ID for this service. + * @hide + */ + protected int mInstanceId; + + /** + * Handle counter override (for conformance testing). + * @hide + */ + protected int mHandles = 0; + + /** + * Service type (Primary/Secondary). + * @hide + */ + protected int mServiceType; + + /** + * List of characteristics included in this service. + */ + protected List<BluetoothGattCharacteristic> mCharacteristics; + + /** + * List of included services for this service. + */ + protected List<BluetoothGattService> mIncludedServices; + + /** + * Create a new BluetoothGattService. + * @hide + */ + /*package*/ BluetoothGattService(UUID uuid, int serviceType) { + mDevice = null; + mUuid = uuid; + mInstanceId = 0; + mServiceType = serviceType; + mCharacteristics = new ArrayList<BluetoothGattCharacteristic>(); + mIncludedServices = new ArrayList<BluetoothGattService>(); + } + + /** + * Create a new BluetoothGattService + * @hide + */ + /*package*/ BluetoothGattService(BluetoothDevice device, UUID uuid, + int instanceId, int serviceType) { + mDevice = device; + mUuid = uuid; + mInstanceId = instanceId; + mServiceType = serviceType; + mCharacteristics = new ArrayList<BluetoothGattCharacteristic>(); + mIncludedServices = new ArrayList<BluetoothGattService>(); + } + + /** + * Returns the device associated with this service. + * @hide + */ + /*package*/ BluetoothDevice getDevice() { + return mDevice; + } + + /** + * Add a characteristic to this service. + * @hide + */ + /*package*/ void addCharacteristic(BluetoothGattCharacteristic characteristic) { + mCharacteristics.add(characteristic); + } + + /** + * Get characteristic by UUID and instanceId. + * @hide + */ + /*package*/ BluetoothGattCharacteristic getCharacteristic(UUID uuid, int instanceId) { + for(BluetoothGattCharacteristic characteristic : mCharacteristics) { + if (uuid.equals(characteristic.getUuid()) && + mInstanceId == instanceId) + return characteristic; + } + return null; + } + + /** + * Get the handle count override (conformance testing. + * @hide + */ + /*package*/ int getHandles() { + return mHandles; + } + + /** + * Add an included service to the internal map. + * @hide + */ + /*package*/ void addIncludedService(BluetoothGattService includedService) { + mIncludedServices.add(includedService); + } + + /** + * Returns the UUID of this service + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return UUID of this service + */ + public UUID getUuid() { + return mUuid; + } + + /** + * Returns the instance ID for this service + * + * <p>If a remote device offers multiple services with the same UUID + * (ex. multiple battery services for different batteries), the instance + * ID is used to distuinguish services. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Instance ID of this service + */ + public int getInstanceId() { + return mInstanceId; + } + + /** + * Get the type of this service (primary/secondary) + * @hide + */ + public int getType() { + return mServiceType; + } + + /** + * Get the list of included Gatt services for this service. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return List of included services or empty list if no included services + * were discovered. + */ + public List<BluetoothGattService> getIncludedServices() { + return mIncludedServices; + } + + /** + * Returns a list of characteristics included in this service. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Characteristics included in this service + */ + public List<BluetoothGattCharacteristic> getCharacteristics() { + return mCharacteristics; + } + + /** + * Returns a characteristic with a given UUID out of the list of + * characteristics offered by this service. + * + * <p>This is a convenience function to allow access to a given characteristic + * without enumerating over the list returned by {@link #getCharacteristics} + * manually. + * + * <p>If a remote service offers multiple characteristics with the same + * UUID, the first instance of a characteristic with the given UUID + * is returned. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Gatt characteristic object or null if no characteristic with the + * given UUID was found. + */ + public BluetoothGattCharacteristic getCharacteristic(UUID uuid) { + for(BluetoothGattCharacteristic characteristic : mCharacteristics) { + if (uuid.equals(characteristic.getUuid())) + return characteristic; + } + return null; + } +} diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 1920efa..9ee202a 100755..100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -88,6 +88,18 @@ public interface BluetoothProfile { public static final int PBAP = 6; /** + * GATT + * @hide + */ + static public final int GATT = 7; + + /** + * GATT_SERVER + * @hide + */ + static public final int GATT_SERVER = 8; + + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile * @hide diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl new file mode 100644 index 0000000..c89d132 --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -0,0 +1,90 @@ +/* + * 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 android.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.os.ParcelUuid; + +import android.bluetooth.IBluetoothGattCallback; +import android.bluetooth.IBluetoothGattServerCallback; + +/** + * API for interacting with BLE / GATT + * @hide + */ +interface IBluetoothGatt { + List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states); + + void startScan(in int appIf, in boolean isServer); + void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids); + void stopScan(in int appIf, in boolean isServer); + + void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); + void unregisterClient(in int clientIf); + void clientConnect(in int clientIf, in String address, in boolean isDirect); + void clientDisconnect(in int clientIf, in String address); + void refreshDevice(in int clientIf, in String address); + void discoverServices(in int clientIf, in String address); + void readCharacteristic(in int clientIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in int authReq); + void writeCharacteristic(in int clientIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in int writeType, in int authReq, in byte[] value); + void readDescriptor(in int clientIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in ParcelUuid descrUuid, in int authReq); + void writeDescriptor(in int clientIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in ParcelUuid descrId, in int writeType, + in int authReq, in byte[] value); + void registerForNotification(in int clientIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in boolean enable); + void beginReliableWrite(in int clientIf, in String address); + void endReliableWrite(in int clientIf, in String address, in boolean execute); + void readRemoteRssi(in int clientIf, in String address); + + void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); + void unregisterServer(in int serverIf); + void serverConnect(in int servertIf, in String address, in boolean isDirect); + void serverDisconnect(in int serverIf, in String address); + void beginServiceDeclaration(in int serverIf, in int srvcType, + in int srvcInstanceId, in int minHandles, + in ParcelUuid srvcId); + void addIncludedService(in int serverIf, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId); + void addCharacteristic(in int serverIf, in ParcelUuid charId, + in int properties, in int permissions); + void addDescriptor(in int serverIf, in ParcelUuid descId, + in int permissions); + void endServiceDeclaration(in int serverIf); + void removeService(in int serverIf, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId); + void clearServices(in int serverIf); + void sendResponse(in int serverIf, in String address, in int requestId, + in int status, in int offset, in byte[] value); + void sendNotification(in int serverIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in boolean confirm, in byte[] value); +} diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl new file mode 100644 index 0000000..fc52172 --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -0,0 +1,65 @@ +/* + * 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 android.bluetooth; + +import android.os.ParcelUuid; + + +/** + * Callback definitions for interacting with BLE / GATT + * @hide + */ +interface IBluetoothGattCallback { + void onClientRegistered(in int status, in int clientIf); + void onClientConnectionState(in int status, in int clientIf, + in boolean connected, in String address); + void onScanResult(in String address, in int rssi, in byte[] advData); + void onGetService(in String address, in int srvcType, in int srvcInstId, + in ParcelUuid srvcUuid); + void onGetIncludedService(in String address, in int srvcType, in int srvcInstId, + in ParcelUuid srvcUuid, in int inclSrvcType, + in int inclSrvcInstId, in ParcelUuid inclSrvcUuid); + void onGetCharacteristic(in String address, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in int charProps); + void onGetDescriptor(in String address, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in ParcelUuid descrUuid); + void onSearchComplete(in String address, in int status); + void onCharacteristicRead(in String address, in int status, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in byte[] value); + void onCharacteristicWrite(in String address, in int status, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid); + void onExecuteWrite(in String address, in int status); + void onDescriptorRead(in String address, in int status, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in ParcelUuid descrUuid, in byte[] value); + void onDescriptorWrite(in String address, in int status, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in ParcelUuid descrUuid); + void onNotify(in String address, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in byte[] value); + void onReadRemoteRssi(in String address, in int rssi, in int status); +} diff --git a/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl new file mode 100644 index 0000000..ae9bffc --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl @@ -0,0 +1,61 @@ +/* + * 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 android.bluetooth; + +import android.os.ParcelUuid; + + +/** + * Callback definitions for interacting with BLE / GATT + * @hide + */ +interface IBluetoothGattServerCallback { + void onServerRegistered(in int status, in int serverIf); + void onScanResult(in String address, in int rssi, in byte[] advData); + void onServerConnectionState(in int status, in int serverIf, + in boolean connected, in String address); + void onServiceAdded(in int status, in int srvcType, + in int srvcInstId, in ParcelUuid srvcId); + void onCharacteristicReadRequest(in String address, in int transId, + in int offset, in boolean isLong, + in int srvcType, + in int srvcInstId, in ParcelUuid srvcId, + in int charInstId, in ParcelUuid charId); + void onDescriptorReadRequest(in String address, in int transId, + in int offset, in boolean isLong, + in int srvcType, + in int srvcInstId, in ParcelUuid srvcId, + in int charInstId, in ParcelUuid charId, + in ParcelUuid descrId); + void onCharacteristicWriteRequest(in String address, in int transId, + in int offset, in int length, + in boolean isPrep, + in boolean needRsp, + in int srvcType, + in int srvcInstId, in ParcelUuid srvcId, + in int charInstId, in ParcelUuid charId, + in byte[] value); + void onDescriptorWriteRequest(in String address, in int transId, + in int offset, in int length, + in boolean isPrep, + in boolean needRsp, + in int srvcType, + in int srvcInstId, in ParcelUuid srvcId, + in int charInstId, in ParcelUuid charId, + in ParcelUuid descrId, + in byte[] value); + void onExecuteWrite(in String address, in int transId, in boolean execWrite); +} diff --git a/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java b/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java new file mode 100644 index 0000000..c05abb2 --- /dev/null +++ b/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java @@ -0,0 +1,67 @@ +/* + * 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 android.bluetooth; + +import java.util.ArrayList; +import java.util.IllegalFormatConversionException; +import java.util.List; +import java.util.UUID; + +/** + * Mutable variant of a Bluetooth Gatt Characteristic + * @hide + */ +public class MutableBluetoothGattCharacteristic extends BluetoothGattCharacteristic { + + /** + * Create a new MutableBluetoothGattCharacteristic. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this characteristic + * @param properties Properties of this characteristic + * @param permissions Permissions for this characteristic + */ + public MutableBluetoothGattCharacteristic(UUID uuid, int properties, int permissions) { + super(null, uuid, 0, properties, permissions); + } + + /** + * Adds a descriptor to this characteristic. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param descriptor Descriptor to be added to this characteristic. + */ + public void addDescriptor(MutableBluetoothGattDescriptor descriptor) { + mDescriptors.add(descriptor); + descriptor.setCharacteristic(this); + } + + /** + * Set the desired key size. + * @hide + */ + public void setKeySize(int keySize) { + mKeySize = keySize; + } + + /** + * Sets the service associated with this device. + * @hide + */ + /*package*/ void setService(BluetoothGattService service) { + mService = service; + } +} diff --git a/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java b/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java new file mode 100644 index 0000000..e455392 --- /dev/null +++ b/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java @@ -0,0 +1,45 @@ +/* + * 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 android.bluetooth; + +import java.util.UUID; + +/** + * Mutable variant of a Bluetooth Gatt Descriptor + * @hide + */ +public class MutableBluetoothGattDescriptor extends BluetoothGattDescriptor { + + /** + * Create a new BluetoothGattDescriptor. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this descriptor + * @param permissions Permissions for this descriptor + */ + public MutableBluetoothGattDescriptor(UUID uuid, int permissions) { + super(null, uuid, permissions); + } + + /** + * Set the back-reference to the associated characteristic + * @hide + */ + /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) { + mCharacteristic = characteristic; + } +} diff --git a/core/java/android/bluetooth/MutableBluetoothGattService.java b/core/java/android/bluetooth/MutableBluetoothGattService.java new file mode 100644 index 0000000..927f5ab --- /dev/null +++ b/core/java/android/bluetooth/MutableBluetoothGattService.java @@ -0,0 +1,83 @@ +/* + * 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 android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Represents a Bluetooth Gatt Service + * @hide + */ +public class MutableBluetoothGattService extends BluetoothGattService { + + /** + * Create a new MutableBluetoothGattService. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this service + * @param serviceType The type of this service (primary/secondary) + */ + public MutableBluetoothGattService(UUID uuid, int serviceType) { + super(uuid, serviceType); + } + + /** + * Add an included service to this service. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param service The service to be added + * @return true, if the included service was added to the service + */ + public boolean addService(BluetoothGattService service) { + mIncludedServices.add(service); + return true; + } + + /** + * Add a characteristic to this service. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic The characteristics to be added + * @return true, if the characteristic was added to the service + */ + public boolean addCharacteristic(MutableBluetoothGattCharacteristic characteristic) { + mCharacteristics.add(characteristic); + characteristic.setService(this); + return true; + } + + /** + * Force the instance ID. + * This is needed for conformance testing only. + * @hide + */ + public void setInstanceId(int instanceId) { + mInstanceId = instanceId; + } + + /** + * Force the number of handles to reserve for this service. + * This is needed for conformance testing only. + * @hide + */ + public void setHandles(int handles) { + mHandles = handles; + } +} |