BLE - сервис Bluetooth GATT не может закрыть соединение - PullRequest
0 голосов
/ 18 апреля 2019

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

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

Чего я хочу добиться, так это когда onDestroy в Fragment вызывается, он должен отключиться от BLE и уничтожить все ссылки. И затем, если вы снова войдете во фрагмент A , он должен снова все воссоздать, независимо от того, сколько раз вы открываете фрагмент A . Но теперь устройство больше не найдено, возможно, потому что оно не отключился должным образом, и устройство 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 && 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 голосов
/ 18 апреля 2019

Я нашел решение.Проблема в:

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

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

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