The service works like this: when you connect a device, its address is stored in the database. Gatt remains in the connect state and waits for a new connection. If you turn off Bluetooth on your phone and turn it back on, the connection is lost, and lost only on Android 6.
Here is my message recipient:
protected class ConnectBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(@NonNull Context context, @NonNull Intent intent) { logger.v(TAG + "------------------------ConnectBroadcastReceiver : onReceive " + intent.getAction()); if ((intent.getAction().equals(BluetoothDevice.ACTION_ACL_CONNECTED))) { logger.v(TAG + "------------------------ConnectBroadcastReceiver : onReceive " + intent.getAction()); logger.v(TAG + "PairingBroadcastReceiver : CONNECTED"); } if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter.isEnabled()) { bleDeviceManager.setApplicationContext(BluetoothLeService.this); bleDeviceManager.connectAll(bluetoothAdapter); } } } } Here is its filter:
IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(connectBroadcastReceiver, filter); This connectAll method connectAll not give any error, but the connection on Android 6 after turning off and on the bluetooth is not restored.
@Override public void connectAll(final BluetoothAdapter bluetoothAdapter) { mainThreadHandler.postDelayed(new Runnable() { @Override public void run() { connectedGatt.clear(); devicesList.clear(); deviceHandlerMap.clear(); logger.d(TAG + "connectAll"); List<Sensor> sensors = databaseRepository.getConnectedSensors(); for (Sensor sensor : sensors) { BluetoothDevice device = bluetoothAdapter.getRemoteDevice(sensor.getAddress()); connectedGatt.add(new BleConnectionCompat(applicationContext, logger).connectGatt(device, true, callback)); logger.d(TAG + "connect " + device.getName() + " " + device.getAddress()); } } }, 10000); } Class BleConnectionCompat :
public class BleConnectionCompat { private final Context context; private Logger logger; public BleConnectionCompat(Context context, Logger logger) { this.context = context; this.logger = logger; } public BluetoothGatt connectGatt(BluetoothDevice remoteDevice, boolean autoConnect, BluetoothGattCallback bluetoothGattCallback) { if (remoteDevice == null) { return null; } /** * Issue that caused a race condition mentioned below was fixed in 7.0.0_r1 * https://android.googlesource.com/platform/frameworks/base/+/android-7.0.0_r1/core/java/android/bluetooth/BluetoothGatt.java#649 * compared to * https://android.googlesource.com/platform/frameworks/base/+/android-6.0.1_r72/core/java/android/bluetooth/BluetoothGatt.java#739 * issue: https://android.googlesource.com/platform/frameworks/base/+/d35167adcaa40cb54df8e392379dfdfe98bcdba2%5E%21/#F0 */ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M || !autoConnect) { return connectGattCompat(bluetoothGattCallback, remoteDevice, autoConnect); } /** * Some implementations of Bluetooth Stack have a race condition where autoConnect flag * is not properly set before calling connectGatt. That's the reason for using reflection * to set the flag manually. */ try { logger.v("Trying to connectGatt using reflection."); Object iBluetoothGatt = getIBluetoothGatt(getIBluetoothManager()); if (iBluetoothGatt == null) { logger.w("Couldn't get iBluetoothGatt object"); return connectGattCompat(bluetoothGattCallback, remoteDevice, true); } BluetoothGatt bluetoothGatt = createBluetoothGatt(iBluetoothGatt, remoteDevice); if (bluetoothGatt == null) { logger.w("Couldn't create BluetoothGatt object"); return connectGattCompat(bluetoothGattCallback, remoteDevice, true); } boolean connectedSuccessfully = connectUsingReflection(bluetoothGatt, bluetoothGattCallback, true); if (!connectedSuccessfully) { logger.w("Connection using reflection failed, closing gatt"); bluetoothGatt.close(); } return bluetoothGatt; } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException | NoSuchFieldException exception) { logger.w(exception + "Error during reflection"); return connectGattCompat(bluetoothGattCallback, remoteDevice, true); } } private BluetoothGatt connectGattCompat(BluetoothGattCallback bluetoothGattCallback, BluetoothDevice device, boolean autoConnect) { logger.v("Connecting without reflection"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return device.connectGatt(context, autoConnect, bluetoothGattCallback, TRANSPORT_LE); } else { return device.connectGatt(context, autoConnect, bluetoothGattCallback); } } private boolean connectUsingReflection(BluetoothGatt bluetoothGatt, BluetoothGattCallback bluetoothGattCallback, boolean autoConnect) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { logger.v("Connecting using reflection"); setAutoConnectValue(bluetoothGatt, autoConnect); Method connectMethod = bluetoothGatt.getClass().getDeclaredMethod("connect", Boolean.class, BluetoothGattCallback.class); connectMethod.setAccessible(true); return (Boolean) (connectMethod.invoke(bluetoothGatt, true, bluetoothGattCallback)); } @TargetApi(Build.VERSION_CODES.M) private BluetoothGatt createBluetoothGatt(Object iBluetoothGatt, BluetoothDevice remoteDevice) throws IllegalAccessException, InvocationTargetException, InstantiationException { Constructor bluetoothGattConstructor = BluetoothGatt.class.getDeclaredConstructors()[0]; bluetoothGattConstructor.setAccessible(true); logger.v("Found constructor with args count = " + bluetoothGattConstructor.getParameterTypes().length); if (bluetoothGattConstructor.getParameterTypes().length == 4) { return (BluetoothGatt) (bluetoothGattConstructor.newInstance(context, iBluetoothGatt, remoteDevice, TRANSPORT_LE)); } else { return (BluetoothGatt) (bluetoothGattConstructor.newInstance(context, iBluetoothGatt, remoteDevice)); } } private Object getIBluetoothGatt(Object iBluetoothManager) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { if (iBluetoothManager == null) { return null; } Method getBluetoothGattMethod = getMethodFromClass(iBluetoothManager.getClass(), "getBluetoothGatt"); return getBluetoothGattMethod.invoke(iBluetoothManager); } private Object getIBluetoothManager() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { return null; } Method getBluetoothManagerMethod = getMethodFromClass(bluetoothAdapter.getClass(), "getBluetoothManager"); return getBluetoothManagerMethod.invoke(bluetoothAdapter); } private Method getMethodFromClass(Class<?> cls, String methodName) throws NoSuchMethodException { Method method = cls.getDeclaredMethod(methodName); method.setAccessible(true); return method; } private void setAutoConnectValue(BluetoothGatt bluetoothGatt, boolean autoConnect) throws NoSuchFieldException, IllegalAccessException { Field autoConnectField = bluetoothGatt.getClass().getDeclaredField("mAutoConnect"); autoConnectField.setAccessible(true); autoConnectField.setBoolean(bluetoothGatt, autoConnect); } } It was taken from here , from the link in the first answer.
If instead of connecting through this class to do just that
connectedGatt.add(device.connectGatt(applicationContext, true, callback)); - nothing changes.
But here is the connectAll method:
public void connectAll(final BluetoothAdapter btAdapter) { final BluetoothAdapter bluetoothAdapter; logger.d(TAG + "reconnectAll"); mainThreadHandler.postDelayed(new Runnable() { @Override public void run() { for (BluetoothGatt gatt : connectedGatt) { gatt.connect(); } } }, 1000); } It gives just such an error:
02-27 18:13:12.639 24192-24192/com.tel.btandroid.debug core::DevManager :: reconnectAll 02-27 18:13:12.643 24192-24192/com.tel.btandroid.debug E/BluetoothGatt: android.os.DeadObjectException at android.os.BinderProxy.transactNative(Native Method) at android.os.BinderProxy.transact(Binder.java:503) at android.bluetooth.IBluetoothGatt$Stub$Proxy.unregisterClient(IBluetoothGatt.java:891) at android.bluetooth.BluetoothGatt.unregisterApp(BluetoothGatt.java:692) at android.bluetooth.BluetoothGatt.close(BluetoothGatt.java:631) at com.tel.android.core.sensors.bluetooth.BLEDeviceManagerImpl$8.run(BLEDeviceManagerImpl.java:404) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5551) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620) connectedGatt is an array in which all gatt are gatt when new devices appear. And this error only on Android 6. And on the fifth and seventh works without problems. And the connection is restored in both options connectAll() . What is wrong with the six, how to reconnect?