简体   繁体   English

BLE - 蓝牙GATT服务无法关闭连接

[英]BLE - Bluetooth GATT service can't close connection

I have implemented BLE and it works. 我已经实现了BLE并且它有效。 The flow is: DrawerActivity starts, it sets a fragmentA , which has BLE implementation, because I want active BLE only in fragmentA . 流程是: DrawerActivity启动,它设置一个fragmentA ,它具有BLE实现,因为我只想在fragmentA中使用活动BLE。 So if you switch to fragmentB it should terminate the BLE connection and upair the device. 因此,如果切换到fragmentB,它应该终止BLE连接并使设备更加通用。

What happens is that the only time it completely disconnects is, when you close the app, or turn off the bluetooth. 发生的事情是它完全断开连接的唯一一次是关闭应用程序或关闭蓝牙。 If you close the fragmentA and open it again it works from drawerActivity . 如果您关闭fragmentA并再次打开它,它将从drawerActivity中工作 If you do it again, so this is now the 3rd time, it won't pair to the BLE device. 如果你再次这样做,那么现在这是第3次,它将不会与BLE设备配对。 When I investigated further, it won't even find the correct BLE device. 当我进一步调查时, 它甚至找不到正确的BLE设备。 . Meaning if you run the fragment the 4th, 5th time it is the same result. 意思是如果你运行片段第4次,第5次它是相同的结果。

What I want to achieve is when onDestroy in Fragment is called it should disconnect from the BLE and destroy all references. 我想要实现的是当调用Fragment中的onDestroy时它应该与BLE断开连接并销毁所有引用。 And then if you go into the fragmentA again it should recreate everything again, no matter how many times you open the fragmentA .But now the device isn't found anymore, probably because it didn't disconnect properly and BLE device has old references or something. 然后,如果再次进入fragmentA ,它应该重新创建所有内容,无论你打开fragmentA多少次。但现在找不到设备了,可能是因为它没有正确断开连接而且BLE设备有旧引用或者一些东西。 This is how I disconnect. 这就是我断开连接的方式。

This is onDestroy method: 这是onDestroy方法:

    override fun onDestroy() {
    super.onDestroy()

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

And in bluetoothManager 并在bluetoothManager

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

And at the bluetoothService : bluetoothService

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

Here are the all 3 files that are used for BLE. 以下是用于BLE的所有3个文件。

FragmentA 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 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 && 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 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
    }

}

I found the solution. 我找到了解决方案。 The problem vas in: 问题在于:

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

Because it was trying to connect again and again it stopped broadcasting. 因为它一次又一次地试图连接它停止广播。 I solved it with: 我解决了它:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM