2015-08-03 23:09:49 +02:00
package nodomain.freeyourgadget.gadgetbridge.service.btle ;
2015-04-19 02:37:29 +02:00
2015-04-26 00:53:48 +02:00
import android.bluetooth.BluetoothGatt ;
import android.bluetooth.BluetoothGattCharacteristic ;
2015-05-24 14:39:36 +02:00
import android.bluetooth.BluetoothGattDescriptor ;
2015-04-26 00:53:48 +02:00
import android.bluetooth.BluetoothGattService ;
2015-05-18 20:56:19 +02:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
2015-04-19 02:37:29 +02:00
import java.io.IOException ;
2016-07-25 00:00:22 +02:00
import java.util.AbstractCollection ;
import java.util.ArrayList ;
2015-04-19 02:37:29 +02:00
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.List ;
import java.util.Set ;
import java.util.UUID ;
2015-08-03 23:09:49 +02:00
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport ;
2016-04-03 21:41:52 +02:00
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction ;
2016-07-25 00:00:22 +02:00
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile ;
2015-04-19 02:37:29 +02:00
/ * *
2015-08-02 00:12:21 +02:00
* Abstract base class for all devices connected through Bluetooth Low Energy ( LE ) aka
* Bluetooth Smart .
2015-08-07 16:59:52 +02:00
* < p / >
2015-08-02 00:12:21 +02:00
* The connection to the device and all communication is made with a generic { @link BtLEQueue } .
* Messages to the device are encoded as { @link BtLEAction actions } that are grouped with a
* { @link Transaction } and sent via { @link BtLEQueue } .
*
2015-04-19 02:37:29 +02:00
* @see TransactionBuilder
* @see BtLEQueue
* /
public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport implements GattCallback {
2015-05-12 06:28:11 +02:00
private static final Logger LOG = LoggerFactory . getLogger ( AbstractBTLEDeviceSupport . class ) ;
2015-04-19 11:28:03 +02:00
2015-04-19 02:37:29 +02:00
private BtLEQueue mQueue ;
private HashMap < UUID , BluetoothGattCharacteristic > mAvailableCharacteristics ;
2015-11-23 23:04:46 +01:00
private final Set < UUID > mSupportedServices = new HashSet < > ( 4 ) ;
2016-07-25 00:00:22 +02:00
private final List < AbstractBleProfile < ? > > mSupportedProfiles = new ArrayList < > ( ) ;
2015-04-19 02:37:29 +02:00
2015-08-07 16:59:52 +02:00
public static final String BASE_UUID = " 0000%s-0000-1000-8000-00805f9b34fb " ; //this is common for all BTLE devices. see http://stackoverflow.com/questions/18699251/finding-out-android-bluetooth-le-gatt-profiles
2015-04-19 02:37:29 +02:00
@Override
public boolean connect ( ) {
if ( mQueue = = null ) {
mQueue = new BtLEQueue ( getBluetoothAdapter ( ) , getDevice ( ) , this , getContext ( ) ) ;
2016-04-28 23:17:13 +02:00
mQueue . setAutoReconnect ( getAutoReconnect ( ) ) ;
2015-04-19 02:37:29 +02:00
}
return mQueue . connect ( ) ;
}
2016-04-28 23:17:13 +02:00
@Override
public void setAutoReconnect ( boolean enable ) {
super . setAutoReconnect ( enable ) ;
if ( mQueue ! = null ) {
mQueue . setAutoReconnect ( enable ) ;
}
}
2015-04-19 02:37:29 +02:00
/ * *
* Subclasses should populate the given builder to initialize the device ( if necessary ) .
2015-04-19 11:28:03 +02:00
*
2015-04-19 02:37:29 +02:00
* @param builder
* @return the same builder as passed as the argument
* /
protected TransactionBuilder initializeDevice ( TransactionBuilder builder ) {
return builder ;
}
@Override
public void dispose ( ) {
if ( mQueue ! = null ) {
mQueue . dispose ( ) ;
2015-05-22 23:15:45 +02:00
mQueue = null ;
2015-04-19 02:37:29 +02:00
}
}
2015-04-19 11:28:03 +02:00
2015-05-24 00:11:14 +02:00
protected TransactionBuilder createTransactionBuilder ( String taskName ) {
return new TransactionBuilder ( taskName ) ;
}
2015-04-19 02:37:29 +02:00
/ * *
* Send commands like this to the device :
* < p >
* < code > perform ( " sms notification " ) . write ( someCharacteristic , someByteArray ) . queue ( getQueue ( ) ) ; < / code >
* < / p >
* TODO : support orchestration of multiple reads and writes depending on returned values
2015-04-19 11:28:03 +02:00
*
2015-04-19 02:37:29 +02:00
* @see # performConnected ( Transaction )
* @see # initializeDevice ( TransactionBuilder )
* /
2015-08-18 00:28:17 +02:00
public TransactionBuilder performInitialized ( String taskName ) throws IOException {
2015-04-19 02:37:29 +02:00
if ( ! isConnected ( ) ) {
if ( ! connect ( ) ) {
throw new IOException ( " 1: Unable to connect to device: " + getDevice ( ) ) ;
}
}
if ( ! isInitialized ( ) ) {
// first, add a transaction that performs device initialization
2015-05-24 00:11:14 +02:00
TransactionBuilder builder = createTransactionBuilder ( " Initialize device " ) ;
2015-05-28 00:26:41 +02:00
builder . add ( new CheckInitializedAction ( gbDevice ) ) ;
2015-04-19 02:37:29 +02:00
initializeDevice ( builder ) . queue ( getQueue ( ) ) ;
}
2015-05-24 00:11:14 +02:00
return createTransactionBuilder ( taskName ) ;
2015-04-19 02:37:29 +02:00
}
/ * *
* @param transaction
* @throws IOException
* @see { @link # performInitialized ( String ) }
* /
protected void performConnected ( Transaction transaction ) throws IOException {
if ( ! isConnected ( ) ) {
if ( ! connect ( ) ) {
throw new IOException ( " 2: Unable to connect to device: " + getDevice ( ) ) ;
}
}
getQueue ( ) . add ( transaction ) ;
}
public BtLEQueue getQueue ( ) {
return mQueue ;
}
2015-04-19 11:28:03 +02:00
2015-04-19 02:37:29 +02:00
/ * *
* Subclasses should call this method to add services they support .
* Only supported services will be queried for characteristics .
2015-04-19 11:28:03 +02:00
*
2015-04-19 02:37:29 +02:00
* @param aSupportedService
* @see # getCharacteristic ( UUID )
* /
protected void addSupportedService ( UUID aSupportedService ) {
mSupportedServices . add ( aSupportedService ) ;
}
2016-07-25 00:00:22 +02:00
protected void addSupportedProfile ( AbstractBleProfile < ? > profile ) {
mSupportedProfiles . add ( profile ) ;
}
2015-04-19 02:37:29 +02:00
/ * *
* Returns the characteristic matching the given UUID . Only characteristics
* are returned whose service is marked as supported .
2015-04-19 11:28:03 +02:00
*
2015-04-19 02:37:29 +02:00
* @param uuid
* @return the characteristic for the given UUID or < code > null < / code >
* @see # addSupportedService ( UUID )
* /
2015-08-18 00:08:22 +02:00
public BluetoothGattCharacteristic getCharacteristic ( UUID uuid ) {
2015-04-19 02:37:29 +02:00
if ( mAvailableCharacteristics = = null ) {
return null ;
}
return mAvailableCharacteristics . get ( uuid ) ;
}
private void gattServicesDiscovered ( List < BluetoothGattService > discoveredGattServices ) {
if ( discoveredGattServices = = null ) {
2016-07-18 23:55:44 +02:00
LOG . warn ( " No gatt services discovered: null! " ) ;
2015-04-19 02:37:29 +02:00
return ;
}
Set < UUID > supportedServices = getSupportedServices ( ) ;
2015-10-19 23:36:10 +02:00
mAvailableCharacteristics = new HashMap < > ( ) ;
2015-04-19 02:37:29 +02:00
for ( BluetoothGattService service : discoveredGattServices ) {
if ( supportedServices . contains ( service . getUuid ( ) ) ) {
2016-07-25 00:00:22 +02:00
LOG . debug ( " discovered supported service: " + BleNamesResolver . resolveServiceName ( service . getUuid ( ) . toString ( ) ) + " : " + service . getUuid ( ) ) ;
2015-04-19 02:37:29 +02:00
List < BluetoothGattCharacteristic > characteristics = service . getCharacteristics ( ) ;
if ( characteristics = = null | | characteristics . isEmpty ( ) ) {
2015-05-12 06:28:11 +02:00
LOG . warn ( " Supported LE service " + service . getUuid ( ) + " did not return any characteristics " ) ;
2015-04-19 02:37:29 +02:00
continue ;
}
2015-09-25 17:45:06 +02:00
HashMap < UUID , BluetoothGattCharacteristic > intmAvailableCharacteristics = new HashMap < > ( characteristics . size ( ) ) ;
2015-04-19 02:37:29 +02:00
for ( BluetoothGattCharacteristic characteristic : characteristics ) {
2015-09-25 17:45:06 +02:00
intmAvailableCharacteristics . put ( characteristic . getUuid ( ) , characteristic ) ;
2016-07-25 00:00:22 +02:00
LOG . info ( " characteristic: " + BleNamesResolver . resolveCharacteristicName ( characteristic . getUuid ( ) . toString ( ) ) + " : " + characteristic . getUuid ( ) ) ;
2015-04-19 02:37:29 +02:00
}
2015-09-25 17:45:06 +02:00
mAvailableCharacteristics . putAll ( intmAvailableCharacteristics ) ;
2016-07-18 23:55:44 +02:00
} else {
2016-07-25 00:00:22 +02:00
LOG . debug ( " discovered unsupported service: " + BleNamesResolver . resolveServiceName ( service . getUuid ( ) . toString ( ) ) + " : " + service . getUuid ( ) ) ;
2015-04-19 02:37:29 +02:00
}
}
}
protected Set < UUID > getSupportedServices ( ) {
return mSupportedServices ;
}
// default implementations of event handler methods (gatt callbacks)
@Override
public void onConnectionStateChange ( BluetoothGatt gatt , int status , int newState ) {
2016-07-25 00:00:22 +02:00
for ( AbstractBleProfile profile : mSupportedProfiles ) {
profile . onConnectionStateChange ( gatt , status , newState ) ;
}
2015-04-19 02:37:29 +02:00
}
2015-04-19 11:28:03 +02:00
2015-04-19 02:37:29 +02:00
@Override
public void onServicesDiscovered ( BluetoothGatt gatt ) {
2016-05-26 19:03:38 +02:00
gattServicesDiscovered ( gatt . getServices ( ) ) ;
2015-05-24 00:11:14 +02:00
initializeDevice ( createTransactionBuilder ( " Initializing device " ) ) . queue ( getQueue ( ) ) ;
2015-04-19 02:37:29 +02:00
}
2015-04-19 11:28:03 +02:00
2015-04-19 02:37:29 +02:00
@Override
public void onCharacteristicRead ( BluetoothGatt gatt ,
2015-04-19 11:28:03 +02:00
BluetoothGattCharacteristic characteristic , int status ) {
2016-07-25 00:00:22 +02:00
for ( AbstractBleProfile profile : mSupportedProfiles ) {
profile . onCharacteristicRead ( gatt , characteristic , status ) ;
}
2015-04-19 02:37:29 +02:00
}
2015-04-19 11:28:03 +02:00
2015-04-19 02:37:29 +02:00
@Override
public void onCharacteristicWrite ( BluetoothGatt gatt ,
2015-04-19 11:28:03 +02:00
BluetoothGattCharacteristic characteristic , int status ) {
2016-07-25 00:00:22 +02:00
for ( AbstractBleProfile profile : mSupportedProfiles ) {
profile . onCharacteristicWrite ( gatt , characteristic , status ) ;
}
2015-04-19 02:37:29 +02:00
}
2015-04-19 11:28:03 +02:00
2015-05-24 14:39:36 +02:00
@Override
public void onDescriptorRead ( BluetoothGatt gatt , BluetoothGattDescriptor descriptor , int status ) {
2016-07-25 00:00:22 +02:00
for ( AbstractBleProfile profile : mSupportedProfiles ) {
profile . onDescriptorRead ( gatt , descriptor , status ) ;
}
2015-05-24 14:39:36 +02:00
}
@Override
public void onDescriptorWrite ( BluetoothGatt gatt , BluetoothGattDescriptor descriptor , int status ) {
2016-07-25 00:00:22 +02:00
for ( AbstractBleProfile profile : mSupportedProfiles ) {
profile . onDescriptorWrite ( gatt , descriptor , status ) ;
}
2015-05-24 14:39:36 +02:00
}
2015-04-19 02:37:29 +02:00
@Override
public void onCharacteristicChanged ( BluetoothGatt gatt ,
2015-04-19 11:28:03 +02:00
BluetoothGattCharacteristic characteristic ) {
2016-07-25 00:00:22 +02:00
for ( AbstractBleProfile profile : mSupportedProfiles ) {
profile . onCharacteristicChanged ( gatt , characteristic ) ;
}
2015-04-19 02:37:29 +02:00
}
2015-04-19 11:28:03 +02:00
2015-04-22 22:50:35 +02:00
@Override
2015-04-19 02:37:29 +02:00
public void onReadRemoteRssi ( BluetoothGatt gatt , int rssi , int status ) {
2016-07-25 00:00:22 +02:00
for ( AbstractBleProfile profile : mSupportedProfiles ) {
profile . onReadRemoteRssi ( gatt , rssi , status ) ;
}
2015-04-19 02:37:29 +02:00
}
}