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

时间:2019-04-18 11:38:26

标签: android bluetooth bluetooth-lowenergy android-ble

我已经实现了BLE,并且可以正常工作。流程为: DrawerActivity 开始,它设置了具有BLE实现的 fragmentA ,因为我只希望在 fragmentA 中激活BLE。因此,如果您切换到 fragmentB ,它应终止BLE连接并升级设备。

发生的事情是,只有当您关闭应用程序或关闭蓝牙时,它才能完全断开连接。如果您关闭 fragmentA ,然后再次将其打开,则可以从 drawerActivity 中使用它。如果再次执行此操作,那么这是第三次了,它将不会与BLE设备配对。当我进一步调查时,甚至找不到正确的BLE设备。。这意味着,如果您第四次,第五次运行该片段,则结果相同。

我要实现的是在Fragment中的onDestroy调用时,它应与BLE断开连接并销毁所有引用。然后,如果再次进入 fragmentA ,则无论您打开 fragmentA 多少次,它都应重新创建所有内容。但是现在可能找不到该设备了因为它没有正确断开连接,并且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
}

这是用于BLE的所有3个文件。

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 && 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 个答案:

答案 0 :(得分:0)

我找到了解决方案。 问题出在:

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

因为它试图再次连接,所以它停止了广播。 我用以下方法解决了这个问题:

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