2

I'm developing an app which communicates with a bluetooth module (RN4020 by microchip). This app use a service which handles the communication with the module (connection/read/write). When I use one activity there aren't any kind of problem. When I use 2 activity there is a problem. The phone disconnects from the module and crashes when I send data to the module. I think the problem is the service, it doesn't bind in the second activity according to me. How can I solve it?

This is the service:

public class BluetoothLeService extends Service {

private final static String TAG = BluetoothLeService.class.getSimpleName();         //Get name of service to tag debug and warning messages
private BluetoothManager mBluetoothManager;                                         //BluetoothManager used to get the BluetoothAdapter
private BluetoothAdapter mBluetoothAdapter;                                         //The BluetoothAdapter controls the BLE radio in the phone/tablet
private BluetoothGatt mBluetoothGatt;                                               //BluetoothGatt controls the Bluetooth communication link
private String mBluetoothDeviceAddress;                                             //Address of the connected BLE device

public final static String ACTION_GATT_CONNECTED = "com.example.app.ACTION_GATT_CONNECTED"; //Strings representing actions to broadcast to activities
public final static String ACTION_GATT_DISCONNECTED = "com.example.app.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.app.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE = "com.example.app.ACTION_DATA_AVAILABLE";
public final static String ACTION_DATA_WRITTEN = "com.example.app.ACTION_DATA_WRITTEN";
public final static String EXTRA_DATA = "com.example.app.EXTRA_DATA";

public final static UUID UUID_MLDP_DATA_PRIVATE_CHARACTERISTIC = UUID.fromString(WorkActivity.MLDP_DATA_PRIVATE_CHAR);
public final static UUID UUID_CHARACTERISTIC_NOTIFICATION_CONFIG = UUID.fromString(WorkActivity.CHARACTERISTIC_NOTIFICATION_CONFIG);

private final IBinder mBinder = new LocalBinder();                                  //Binder for Activity that binds to this Service

// ----------------------------------------------------------------------------------------------------------------
// An activity has bound to this service
@Override
public IBinder onBind(Intent intent) {
    return mBinder;                                                                 //Return LocalBinder when an Activity binds to this Service
}

// ----------------------------------------------------------------------------------------------------------------
// An activity has unbound from this service 
@Override
public boolean onUnbind(Intent intent) {
    /*if (mBluetoothGatt != null) {                                                   //Check for existing BluetoothGatt connection
        mBluetoothGatt.close();                                                     //Close BluetoothGatt coonection for proper cleanup
        mBluetoothGatt = null;                                                      //No longer have a BluetoothGatt connection
    }*/
    return super.onUnbind(intent);

}

// ----------------------------------------------------------------------------------------------------------------
// A Binder to return to an activity to let it bind to this service 
public class LocalBinder extends Binder {
    BluetoothLeService getService() {
        return BluetoothLeService.this;                                             //Return this instance of BluetoothLeService so clients can call its public methods
    }
}

// ----------------------------------------------------------------------------------------------------------------
// Implements callback methods for GATT events that the app cares about.  For example: connection change and services discovered.
// When onConnectionStateChange() is called with newState = STATE_CONNECTED then it calls mBluetoothGatt.discoverServices()
// resulting in another callback to onServicesDiscovered()
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { //Change in connection state
        if (newState == BluetoothProfile.STATE_CONNECTED) {                         //See if we are connected
            broadcastUpdate(ACTION_GATT_CONNECTED);                                 //Go broadcast an intent to say we are connected
            Log.i(TAG, "Connected to GATT server, starting service discovery");
            mBluetoothGatt.discoverServices();                                      //Discover services on connected BLE device
        } 
        else if (newState == BluetoothProfile.STATE_DISCONNECTED) {                 //See if we are not connected
            broadcastUpdate(ACTION_GATT_DISCONNECTED);                              //Go broadcast an intent to say we are disconnected
            Log.i(TAG, "Disconnected from GATT server.");
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {              //BLE service discovery complete
        if (status == BluetoothGatt.GATT_SUCCESS) {                                 //See if the service discovery was successful
            broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);                       //Go broadcast an intent to say we have discovered services
        } 
        else {                                                                      //Service discovery failed so log a warning
            Log.w(TAG, "onServicesDiscovered received: " + status);
        }
    }

    //For information only. This application uses Indication to receive updated characteristic data, not Read
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //A request to Read has completed
        if (status == BluetoothGatt.GATT_SUCCESS) {                                 //See if the read was successful
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);                 //Go broadcast an intent with the characteristic data
        }
    }

    //For information only. This application sends small packets infrequently and does not need to know what the previous write completed
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //A request to Write has completed
        if (status == BluetoothGatt.GATT_SUCCESS) {                                 //See if the write was successful
            broadcastUpdate(ACTION_DATA_WRITTEN, characteristic);                   //Go broadcast an intent to say we have have written data
        }
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { //Indication or notification was received
        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);                     //Go broadcast an intent with the characteristic data
    }
};

// ----------------------------------------------------------------------------------------------------------------
// Broadcast an intent with a string representing an action
private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);                                       //Create new intent to broadcast the action
    sendBroadcast(intent);                                                          //Broadcast the intent
}

// ----------------------------------------------------------------------------------------------------------------
// Broadcast an intent with a string representing an action an extra string with the data
// Modify this code for data that is not in a string format 
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);                                       //Create new intent to broadcast the action
    if(action.equals(ACTION_DATA_AVAILABLE)) {                                      //See if we need to send data
        if (UUID_MLDP_DATA_PRIVATE_CHARACTERISTIC.equals(characteristic.getUuid())) { //See if this is the correct characteristic 
            String dataValue = characteristic.getStringValue(0);                    //Get the data (in this case it is a string)
            intent.putExtra(EXTRA_DATA, dataValue);                                 //Add the data string to the intent
        }
    }
    else {                                                                          //Did not get an action string we expect 
        Log.d(TAG, "Action: " + action);
    }
    sendBroadcast(intent);                                                          //Broadcast the intent
}

// ----------------------------------------------------------------------------------------------------------------
// Initialize by getting the BluetoothManager and BluetoothAdapter 
public boolean initialize() {
    if (mBluetoothManager == null) {                                                //See if we do not already have the BluetoothManager
        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); //Get the BluetoothManager
        if (mBluetoothManager == null) {                                            //See if we failed
            Log.e(TAG, "Unable to initialize BluetoothManager.");
            return false;                                                           //Report the error
        }
    }

    mBluetoothAdapter = mBluetoothManager.getAdapter();                             //Ask the BluetoothManager to get the BluetoothAdapter
    if (mBluetoothAdapter == null) {                                                //See if we failed
        Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
        return false;                                                               //Report the error
    }

    return true;                                                                    //Success, we have a BluetoothAdapter to control the radio
}

// ----------------------------------------------------------------------------------------------------------------
// Open a BluetoothGatt connection to a BLE device given its address
public boolean connect(final String address) {
    if (mBluetoothAdapter == null || address == null) {                             //Check that we have a Bluetooth adappter and device address
        Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");     //Log a warning that something went wrong
        return false;                                                               //Failed to connect
    }

    // Previously connected device.  Try to reconnect.
    if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { //See if there was previous connection to the device
        Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
        if (mBluetoothGatt.connect()) {                                             //See if we can connect with the existing BluetoothGatt to connect
            return true;                                                            //Success
        } 
        else {
            return false;                                                           //Were not able to connect
        }
    }

    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);      //No previous device so get the Bluetooth device by referencing its address
    if (device == null) {                                                           //Check whether a device was returned
        Log.w(TAG, "Device not found.  Unable to connect.");                        //Warn that something went wrong
        return false;                                                               //Failed to find the device
    }

    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);                //Directly connect to the device so autoConnect is false
    Log.d(TAG, "Trying to create a new connection.");
    mBluetoothDeviceAddress = address;                                              //Record the address in case we bneed to reconnect with the existing BluetoothGatt
    return true;
}

// ----------------------------------------------------------------------------------------------------------------
// Retrieve and return a list of supported GATT services on the connected device
public List<BluetoothGattService> getSupportedGattServices() {
    if (mBluetoothGatt == null) {                                                   //Check that we have a valid GATT connection
        return null;
    }
    return mBluetoothGatt.getServices();                                            //Get the list of services
}

// ----------------------------------------------------------------------------------------------------------------
// Disconnects an existing connection or cancel a pending connection
// BluetoothGattCallback.onConnectionStateChange() will get the result
public void disconnect() {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {                      //Check that we have a GATT connection to disconnect
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.disconnect();                                                    //Disconnect GATT connection
}

// ----------------------------------------------------------------------------------------------------------------
// Request a read of a given BluetoothGattCharacteristic. The Read result is reported asynchronously through the
// BluetoothGattCallback onCharacteristicRead callback method.
// For information only. This application uses Indication to receive updated characteristic data, not Read
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {                      //Check that we have access to a Bluetooth radio
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.readCharacteristic(characteristic);                              //Request the BluetoothGatt to Read the characteristic
}

// ----------------------------------------------------------------------------------------------------------------
// Write to a given characteristic. The completion of the write is reported asynchronously through the
// BluetoothGattCallback onCharacteristicWrire callback method.
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {                      //Check that we have access to a Bluetooth radio
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    int test = characteristic.getProperties();                                      //Get the properties of the characteristic
    if ((test & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 && (test & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { //Check that the property is writable 
        return;
    }

    if (mBluetoothGatt.writeCharacteristic(characteristic)) {                       //Request the BluetoothGatt to do the Write
        Log.d(TAG, "writeCharacteristic successful");                               //The request was accepted, this does not mean the write completed
    } 
    else {
        Log.d(TAG, "writeCharacteristic failed");                                   //Write request was not accepted by the BluetoothGatt
    }
}

// ----------------------------------------------------------------------------------------------------------------
// Enable notification on a characteristic
// For information only. This application uses Indication, not Notification
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {                      //Check that we have a GATT connection
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);          //Enable notification and indication for the characteristic
//        if (UUID_MLDP_DATA_PRIVATE_CHARACTERISTIC.equals(characteristic.getUuid())) { 
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID_CHARACTERISTIC_NOTIFICATION_CONFIG); //Get the descripter that enables notification on the server
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);         //Set the value of the descriptor to enable notification
    mBluetoothGatt.writeDescriptor(descriptor);                                     //Write the descriptor
//        }
}

// ----------------------------------------------------------------------------------------------------------------
// Enable indication on a characteristic
public void setCharacteristicIndication(BluetoothGattCharacteristic characteristic, boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {                      //Check that we have a GATT connection
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);          //Enable notification and indication for the characteristic

    // This is specific to our custom profile
//        if (UUID_MLDP_DATA_PRIVATE_CHARACTERISTIC.equals(characteristic.getUuid())) {
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID_CHARACTERISTIC_NOTIFICATION_CONFIG); //Get the descripter that enables indication on the server
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);           //Set the value of the descriptor to enable indication
    mBluetoothGatt.writeDescriptor(descriptor);                                     //Write the descriptor
//        }
}

}

In the first activity in the onCreate() I call:

Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
startService(gattServiceIntent);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);

And in onPause():

super.onPause();
unregisterReceiver(mGattUpdateReceiver);
unbindService(mServiceConnection);
mBluetoothLeService = null;

In the second activity in the onCreate() I call:

Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);

Thank you in advance and sorry for my bad english

I edit this question with the help of @pskink

Damien
  • 921
  • 4
  • 13
  • 31
  • make the service started before calling `bindService`, more [here](https://developer.android.com/guide/components/bound-services.html), type ^F `Binding to a Started Service` – pskink Aug 01 '16 at 08:43
  • I recommended you Dependency Injection but, as @pslink reminded me, services are singleton by default so it wouldn't solve your problem. Sorry :( – Juan Aguilar Guisado Aug 01 '16 at 08:55
  • @pskink I tried to start the service before calling bindService but is the same – Damien Aug 01 '16 at 13:53
  • ok this is probably because you call `mBluetoothGatt.close();` inside `onUnbind`, call it somewhere else – pskink Aug 01 '16 at 14:07
  • It doesn't work anyway. In the first activity I call: `gattServiceIntent = new Intent(this, BluetoothLeService.class);` `startService(gattServiceIntent);` `bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);` in the onPause() I write: `super.onPause();` `unregisterReceiver(mGattUpdateReceiver);` `h.removeCallbacksAndMessages(null);` `unbindService(mServiceConnection);` In the other activity I call just `bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);` And in the onUnBind I removed mBluetoothGatt.close(); But it crashes in the second activity – Damien Aug 01 '16 at 14:30

1 Answers1

0

There are two types of services in Android: Started and Bound. You're using the second one, Bound. This type is more related to the lifecycle of the component that started it.

In your scenario, I'd suggest to use Started Android service and to call:

startService(intent);

within your Activity's onCreate() callback to start it. Eventually you can add it in the Application java file as well.

More info at: https://developer.android.com/guide/components/services.html

Lino
  • 5,084
  • 3
  • 21
  • 39
  • the third type is: "started and bound', see my comment above – pskink Aug 01 '16 at 12:22
  • 1
    Thank you for your answer, I agree with you that is better to use "Started" instead of "Bound". But how have I to modify my Service to adapt it to "Started" – Damien Aug 01 '16 at 13:52