Служба BLE — Bluetooth GATT не может закрыть соединение

#android #bluetooth #bluetooth-lowenergy #android-ble

#Android #bluetooth #bluetooth-низкое энергопотребление #android-ble

Вопрос:

Я внедрил BLE, и он работает. Поток таков: запускается DrawerActivity, он устанавливает фрагмент, который имеет реализацию BLE, потому что я хочу, чтобы активный BLE был только во фрагменте. Поэтому, если вы переключитесь на FragmentB, он должен прервать соединение BLE и подключить устройство.

Происходит то, что он полностью отключается только тогда, когда вы закрываете приложение или выключаете Bluetooth. Если вы закроете фрагмент и снова откроете его, он будет работать из DrawerActivity. Если вы сделаете это снова, так что это уже 3-й раз, он не будет сопряжен с устройством BLE. Когда я исследовал дальше, он даже не найдет правильное устройство BLE .. Это означает, что если вы запустите фрагмент в 4-й, 5-й раз, это тот же результат.

Чего я хочу добиться, так это того, что при вызове onDestroy во фрагменте он должен отключиться от BLE и уничтожить все ссылки. И затем, если вы снова войдете во фрагмент, он должен воссоздать все заново, независимо от того, сколько раз вы открываете фрагмент.Но теперь устройство больше не найдено, вероятно, потому, что оно не отключилось должным образом, а на устройстве BLE есть старые ссылки или что-то в этом роде. Вот как я отключаюсь.

Это метод onDestroy:

     override fun onDestroy() {
    super.onDestroy()

    activity?.unregisterReceiver(bluetoothReceiver)
    bluetoothManager?.disconnectBluetoothService()
    bluetoothManager = null
}
 

И в BluetoothManager

 fun disconnectBluetoothService() {
    bluetoothService?.disconnectGattServer()
}
 

И в BluetoothService:

 fun disconnectGattServer() {
    mConnected = false
    mBluetoothGatt?.disconnect()
    mBluetoothGatt?.close()
    mBluetoothGatt = null
}
 

Вот все 3 файла, которые используются для BLE.

FragmentA

 private var bluetoothManager: MyBluetoothManager? = null

private val bluetoothReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val action = intent.action

            if (action == BluetoothAdapter.ACTION_STATE_CHANGED) {
                when (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
                    BluetoothAdapter.STATE_OFF -> {}
                    BluetoothAdapter.STATE_ON -> {
                        initBluetoothIfPossible()
                        bluetoothManager?.scanForBluetoothDevicesIfPossible(true)
                    }
                }
            }
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        listenToBluetoothChanges()
    }

    override fun onDestroy() {
        super.onDestroy()

        activity?.unregisterReceiver(bluetoothReceiver)
        bluetoothManager?.disconnectBluetoothService()
        bluetoothManager = null
    }

    private fun listenToBluetoothChanges() {
        val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
        carSharingActivity?.registerReceiver(bluetoothReceiver, filter)
    }

    private fun initBluetoothIfPossible() {
        bluetoothToken ?: return
        if (bluetoothManager != null) {
            bluetoothManager!!.pairDevice()
        } else {
            bluetoothManager = MyBluetoothManager(activity as Activity,
                    this,
                    bluetoothToken!!.token,
                    bluetoothToken!!.sessionKey,
                    bluetoothToken!!.uuid)
        }
        setImageForBluetoothStatus()
    }
 

MyBluetoothManager

 class ACCarBluetoothManager(var activity: Activity,
                            var listener: MyBluetoothListener,
                            private var token: String,
                            private var sessionKey: String,
                            private var accessDeviceUID: String) {
        // Bluetooth adapter
        private var bluetoothAdapter: BluetoothAdapter?

        // Bluetooth service
        private var bluetoothService: MyBluetoothService? = null
        private var isBluetoothAvailable: Boolean = false

        val isBluetoothEnabled: Boolean
            get() = bluetoothAdapter?.isEnabled == true
        var connectionStatus: Boolean = false
            set(value) {
                if (field == value) return
                field = value
                if (value) stopScanning()
                else startScanning()
            }
        private var savedDevice: BluetoothDevice? = null
    /**
         * Service lifecyle management.
         */
        private val serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(componentName: ComponentName, service: IBinder) {
                bluetoothService = (service as MyBluetoothService.LocalBinder).service

                bluetoothService?.isConnectedListener = { isConnected ->
                    listener.isConnected(isConnected)
                    connectionStatus = isConnected
                }

                isBluetoothAvailable = bluetoothService?.initialize() == true
            }

            override fun onServiceDisconnected(componentName: ComponentName) {
                bluetoothService = null
                connectionStatus = false
            }
        }

        /**
         * Broadcast receiver.
         */

        private val gattUpdateReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                try {
                    when (intent.action) {
                        BluetoothConstants.ACTION_GATT_SERVICES_DISCOVERED -> bluetoothService?.initializeIndications()
                        BluetoothConstants.ACTION_INDICATIONS_INITIALIZED -> bluetoothService?.startAuthentication(token)
                    }
                } catch (e: Exception) {
                    Log.e("GattUpdateReciever", e.message)
                }
            }
        }

        /**
         * Bluetooth device scanning callback. The scanned device is added to the list of available
         * devices.
         */
        private val bluetoothScanCallback = object : ScanCallback() {
            override fun onScanResult(callbackType: Int, result: ScanResult) {
                super.onScanResult(callbackType, result)
                val btDevice = result.device
                if (btDevice.name.isNullOrEmpty()) return

                if (deviceMatchesUID(btDevice)) {
                    savedDevice = btDevice
                    pairDevice()
                }
            }
        }  

        init {
            val gattServiceIntent = Intent(activity, MyBluetoothService::class.java)
            activity.bindService(gattServiceIntent, this.serviceConnection, Context.BIND_AUTO_CREATE)

            // Setup bluetooth adapter
            val bluetoothManager = activity.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
            bluetoothAdapter = bluetoothManager.adapter

            // If bluetooth is not enabled, request permission, otherwise start scanning process, Not IMPLEMENTED, because it is not needed.
            scanForBluetoothDevicesIfPossible()
            activity.registerReceiver(gattUpdateReceiver, BluetoothConstants.makeGattUpdateIntentFilter())
        }

        fun scanForBluetoothDevicesIfPossible(enable: Boolean = isBluetoothEnabled) {
            val hasLocationPermission = ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
            if (enable) {
                if (hasLocationPermission) {
                    startScanning()
                }
                //You can request for location permission if he doesn't have permission
            } else {
                stopScanning()
            }
        }


        fun pairDevice() {
            if (isBluetoothAvailable amp;amp; savedDevice != null) {
                bluetoothService?.connect(savedDevice!!)
            }
        }

        fun startScanning() {
            bluetoothAdapter?.bluetoothLeScanner?.startScan(bluetoothScanCallback)
        }

        fun stopScanning() {
            bluetoothAdapter?.bluetoothLeScanner?.stopScan(bluetoothScanCallback)
        }

        fun deviceMatchesUID(device: BluetoothDevice): Boolean {
            return device.name.equals(accessDeviceUID, ignoreCase = true)
        }
}
 

MyBluetoothService

 class ACCarBluetoothService : Service() {
    var isConnectedListener: ((Boolean) -> Unit)? = null
    var mConnected = false
        set(value) {
            field = value
            isConnectedListener?.invoke(value)
        }
    private val mBinder = LocalBinder()
    private var mBluetoothManager: BluetoothManager? = null
    private var mBluetoothAdapter: BluetoothAdapter? = null
    private var mBluetoothGatt: BluetoothGatt? = null
    private var mDividedTokenList: MutableList<ByteArray>? = null
    // Various callback methods defined by the BLE API.
    private val mGattCallback = object : BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            if (status == BluetoothGatt.GATT_FAILURE
                    || status != BluetoothGatt.GATT_SUCCESS
                    || newState == BluetoothProfile.STATE_DISCONNECTED) {
                disconnectGattServer()
                return
            }

            if (newState == BluetoothProfile.STATE_CONNECTED) {
                gatt.discoverServices()
            }

        }

        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) onServiceDiscoveryReady()
        }

        override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
            when {
                descriptor.characteristic.uuid == BluetoothConstants.UUID_COMMAND_CHALLENGE -> setCharacteristicNotification(
                        BluetoothConstants.UUID_DEBUG,
                        true)
                descriptor.characteristic.uuid == BluetoothConstants.UUID_DEBUG -> setCharacteristicNotification(
                        BluetoothConstants.UUID_STATUS_1,
                        true)
                descriptor.characteristic.uuid == BluetoothConstants.UUID_STATUS_1 -> setCharacteristicNotification(
                        BluetoothConstants.UUID_STATUS_2,
                        true)

                descriptor.characteristic.uuid == BluetoothConstants.UUID_STATUS_2-> setCharacteristicNotification(
                        BluetoothConstants.UUID_STATUS_3,
                        true)
                else -> onIndicationsInitialized()
            }
        }

        override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) broadcastUpdate(characteristic)
        }

        override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
            if (characteristic.uuid == BluetoothConstants.UUID_COMMAND_CHALLENGE) {
                commandChallenge = characteristic.value
            } else {
                broadcastUpdate(characteristic)
            }
        }

        override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
            if (BluetoothConstants.UUID_AUTHORIZE_PHONE == characteristic.uuid) writeNextPartToken()
        }
    }

    override fun onBind(intent: Intent): IBinder? {
        return mBinder
    }

    /**
     * Initializes a reference to the local Bluetooth adapter.
     *
     * @return Return true if the initialization is successful.
     */
    fun initialize(): Boolean {
        if (mBluetoothManager == null) {
            mBluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
            if (mBluetoothManager == null) return false
        }

        mBluetoothAdapter = mBluetoothManager!!.adapter
        if (mBluetoothAdapter == null) return false
        return true
    }


    fun initializeIndications() {
        setCharacteristicNotification(BluetoothConstants.UUID_COMMAND_CHALLENGE, true)
    }

    fun startAuthentication(token: String) {
        mDividedTokenList = Tools.divideArray(Tools.decodeBase64(token))
        writeNextPartToken()
    }

    fun writeCommand(sessionKey: String, command: ByteArray) {
        val safeCommand = Tools.generateSafeCommand(command, commandChallenge, Tools.decodeBase64(sessionKey))
        val commandCharacteristic = mBluetoothGatt!!.getService(BluetoothConstants.UUID_CAR_CONTROL_SERVICE)
                .getCharacteristic(BluetoothConstants.UUID_COMMAND_PHONE)
        commandCharacteristic.value = safeCommand
        mBluetoothGatt!!.writeCharacteristic(commandCharacteristic)
    }

    fun connect(device: BluetoothDevice) {
        mBluetoothGatt = device.connectGatt(this, false, this.mGattCallback)
    }

    fun disconnectGattServer() {
        mConnected = false
        mBluetoothGatt?.disconnect()
        mBluetoothGatt?.close()
        mBluetoothGatt = null
    }


    private fun onIndicationsInitialized() {
        val intent = Intent()
        intent.action = BluetoothConstants.ACTION_INDICATIONS_INITIALIZED
        sendBroadcast(intent)
    }

    private fun onServiceDiscoveryReady() {
        val intent = Intent()
        intent.action = BluetoothConstants.ACTION_GATT_SERVICES_DISCOVERED
        sendBroadcast(intent)
    }

    private fun writeNextPartToken() {
        if (mDividedTokenList!!.isEmpty()) {
            broadcastUpdate(BluetoothConstants.ACTION_INIT_READY)
            return
        }
        writeValue(BluetoothConstants.UUID_AUTHORIZE_PHONE, mDividedTokenList!!.removeAt(0))
    }

    private fun broadcastUpdate(action: String) {
        val intent = Intent(action)
        sendBroadcast(intent)
    }

    private fun writeValue(characteristicUUID: UUID, valueBytes: ByteArray) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) return

        val service = mBluetoothGatt!!.getService(BluetoothConstants.UUID_CAR_CONTROL_SERVICE)
        val characteristic = service.getCharacteristic(characteristicUUID)

        characteristic.value = valueBytes
        mBluetoothGatt!!.writeCharacteristic(characteristic)
    }

    private fun setCharacteristicNotification(characteristicUUID: UUID, enabled: Boolean) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) return

        val characteristic = mBluetoothGatt!!
                .getService(BluetoothConstants.UUID_CAR_INFORMATION_SERVICE)
                .getCharacteristic(characteristicUUID)
        mBluetoothGatt!!.setCharacteristicNotification(characteristic, enabled)

        characteristic.getDescriptor(CONFIG_DESCRIPTOR)?.let {
            it.value = if (enabled) BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
            else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
            mBluetoothGatt!!.writeDescriptor(it)
        }
    }


    private fun broadcastUpdate(characteristic: BluetoothGattCharacteristic) {
        val intent = Intent()
        if (BluetoothConstants.UUID_STATUS_1 == characteristic.uuid) {
            if (!hasDataInBluetooth(characteristic.value)) {
                mConnected = true
                statusListener?.invoke()
            }
            intent.action = BluetoothConstants.ACTION_STATUS_1_AVAILABLE
            intent.putExtra(BluetoothConstants.EXTRA_DATA, characteristic.value)
        }
        if (BluetoothConstants.UUID_DEBUG == characteristic.uuid) {
            intent.action = BluetoothConstants.ACTION_DEBUG_AVAILABLE
            intent.putExtra(BluetoothConstants.EXTRA_DATA, characteristic.value)
        }
        if (BluetoothConstants.UUID_STATUS_2 == characteristic.uuid) {
            intent.action = BluetoothConstants.ACTION_STATUS_1_AVAILABLE
            intent.putExtra(BluetoothConstants.EXTRA_DATA, characteristic.value)
        }
        if (BluetoothConstants.UUID_STATUS_3 == characteristic.uuid) {
            intent.action = BluetoothConstants.ACTION_STATUS_1_AVAILABLE
            intent.putExtra(BluetoothConstants.EXTRA_DATA, characteristic.value)
        }

        sendBroadcast(intent)
    }

    private fun hasDataInBluetooth(byteArray: ByteArray): Boolean {
        for (b in byteArray) {
            if (b.toInt() != 0) {
                return false
            }
        }
        return true
    }

    inner class LocalBinder : Binder() {
        val service: MyBluetoothService
            get() = this@MyBluetoothService
    }

}
 

Ответ №1:

Я нашел решение. Проблема заключается в:

      fun pairDevice() {
        if (isBluetoothAvailable amp;amp; savedDevice != null) {
            bluetoothService?.connect(savedDevice!!)
        }
    }
 

Поскольку он пытался подключиться снова и снова, он прекратил вещание.
Я решил это с помощью:

 fun pairDevice() {
    if (isConnected) return
    if (isBluetoothAvailable amp;amp; savedDevice != null) {
        bluetoothService?.connect(savedDevice!!)
        isConnected = true
    }
}