Android BLE Gatt 连接更改状态

Posted

技术标签:

【中文标题】Android BLE Gatt 连接更改状态【英文标题】:Android BLE Gatt connection change statuses 【发布时间】:2017-12-16 19:34:54 【问题描述】:

我有一个 android 应用程序可以连接到 BLE 设备并对其进行写入。我可以成功连接、读取和写入它。作为测试的一部分,我们正在尝试不同的断开连接场景。

有时,如果 BLE 设备断开连接,我将连接更改为断开状态,状态值为 19。此外,如果有任何绑定错误,状态等于 22。如果我以编程方式断开连接,此状态给我 0。但是android documentation 中除了 0 之外没有指定这些状态。

发布示例 BluetoothGattCallback

private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() 
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) 
        Log.i(TAG, "onConnectionStateChange status: "+status+", newState: "+newState);
        /*i need to know the possible values for this status variable*/
        if(newState == BluetoothProfile.STATE_CONNECTED) 
            gatt.discoverServices();
         else 
            gatt.close();
        
    

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) 
        Log.i(TAG, "onServicesDiscovered service discovered");
    
;

有没有人遇到同样的问题并整理出状态列表。我需要知道 onConnectionStateChange 方法中状态变量的可能值

【问题讨论】:

发布您的代码.. @Dus 我已经发布了一个示例代码,因为我的代码有点冗长且与当前情况无关 【参考方案1】:

这是我拥有的代码列表

以编程方式断开连接 - 0 设备超出范围 - 8 已被设备断开 - 19 债券问题 - 22 未找到设备 - 133(某些手机给出 62)

我已经在 5.0.2、5.1、6.0 和 6.0.1 中测试了断开连接场景。但是只在6.0.1 android版本中找到了这个债券发行代码。

【讨论】:

如何解决绑定错误的问题?我们应该关闭 gatt 并重新建立连接,还是应该尝试重新连接现有的 gatt?在这里查看我的问题***.com/q/47596419/4111151 尝试删除绑定并重新绑定。我认为这将解决债券错误。 谢谢,但我不使用绑定,我使用连接 GATT 方法进行通信。您对此有任何想法吗? 根据我的经验,错误 22 将给出错误的债券错误。检查蓝牙设备是否绑定? 您将从 GattCallback 中的 BluetoothGatt 对象中获取 BluetoothDevice 对象。蓝牙设备设备 = gatt.getDevice(); int bondState = device.getBondState();文档developer.android.com/reference/android/bluetooth/…【参考方案2】:

很抱歉提出一个老问题,但这是我在使用蓝牙 (BLE) 4.0 时遇到的许多问题的解决方案。再次为下面的大类感到抱歉,但请确保它们是必需的,并且没有不相关或未使用的方法。

public abstract class AbstractBluetoothBroadcaster extends BroadcastReceiver 
    protected static final String LOG_TAG = BluetoothLowEnergy.LOG_TAG;

    protected BluetoothLowEnergy bluetoothLowEnergy;

    public AbstractBluetoothBroadcaster(BluetoothLowEnergy bluetoothLowEnergy, String action)
        super();
        this.bluetoothLowEnergy = bluetoothLowEnergy;

        IntentFilter intentFilterStateChange = new IntentFilter(action);
        intentFilterStateChange.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        this.bluetoothLowEnergy.getActivity().registerReceiver(this, intentFilterStateChange);
    

    public void onDestroy()
        this.bluetoothLowEnergy.getActivity().unregisterReceiver(this);
    



public class BluetoothBondStateBroadcaster extends AbstractBluetoothBroadcaster 

    private BluetoothLowEnergy bluetoothLowEnergy;
    private boolean deviceBonded;

    public BluetoothBondStateBroadcaster(BluetoothLowEnergy bluetoothLowEnergy) 
        super(bluetoothLowEnergy, BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        this.bluetoothLowEnergy = bluetoothLowEnergy;
        this.deviceBonded = false;
    

    @Override
    public void onReceive(Context context, Intent intent) 
        String action = intent.getAction();
        if (action == null)
            return;
        
        BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED) &&
                bluetoothDevice != null &&
                bluetoothDevice.getAddress().equals(bluetoothLowEnergy.getDeviceUUID())) 
            int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
            switch (state) 
                case BluetoothDevice.BOND_NONE:
                    Log.d(LOG_TAG, "  NOT BONDED - dev " + bluetoothDevice.getAddress());
                    this.deviceBonded = false;
                    break;
                case BluetoothDevice.BOND_BONDING:
                    Log.d(LOG_TAG, " BONDING ... - dev " + bluetoothDevice.getAddress());
                    break;
                case BluetoothDevice.BOND_BONDED:
                    Log.d(LOG_TAG, " BONDED - dev " + bluetoothDevice.getAddress());
                    deviceBonded = true;
                    bluetoothLowEnergy.onBluetoothBonded();
                    break;
                default:
                    break;
            
        
    

    public void resetDeviceBonded()
        this.deviceBonded = false;
    

    public boolean isDeviceBonded() 
        return deviceBonded;
    



public class BluetoothPairingBroadcaster extends AbstractBluetoothBroadcaster 

    private String devicePIN;

    public BluetoothPairingBroadcaster(BluetoothLowEnergy bluetoothLowEnergy)
        super(bluetoothLowEnergy, BluetoothDevice.ACTION_PAIRING_REQUEST);
        this.devicePIN = "";
    

    @Override
    public void onReceive(Context context, Intent intent) 
        String action = intent.getAction();
        if (action == null)
            return;
        
        BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        int pairingType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
        if (action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST) &&
                bluetoothDevice != null &&
                bluetoothDevice.getAddress().equals(bluetoothLowEnergy.getDeviceUUID()) &&
                !getDevicePIN().isEmpty()) 
            if (pairingType == BluetoothDevice.PAIRING_VARIANT_PIN)
                bluetoothDevice.setPin(getDevicePIN().getBytes());
                Log.d(LOG_TAG," Auto-entering pin - " + getDevicePIN());
                bluetoothDevice.createBond();
                Log.d(LOG_TAG," pin entered and request sent...");
                abortBroadcast();
            
        
    

    public void setDevicePIN(String pin)
        this.devicePIN = pin;
    

    public String getDevicePIN()
        return this.devicePIN ;
    



public class BluetoothLowEnergy extends BluetoothGattCallback 

    // listener that has the methods that the application (activity)
    // will use to send / receive data, or to reflect the system state 
    // in the UI
    public interface BluetoothListener 
        /**
         * Triggered when the scanning has started successfully
         */
        void onBluetoothStartScan();

        /**
         * Triggered when the scanning stops
         * @param scanResults results of the scanning
         */
        void onBluetoothStopScan(Collection<BluetoothDevice> scanResults);

        /**
         * Triggered when the device is ready to send/receive data
         */
        void onBluetoothConnectionReady();

        /**
         * Triggered when a bluetooth msg is received
         * @param msg message received
         */
        void onBluetoothReceiveMsg(String msg);

        /**
         * Triggered whenever data is send
         * @param success true means data was sent fine to the remote device, false otherwise
         */
        void onBluetoothSend(String data, boolean success);

        /**
         * Triggered if no bluetooth is connected, and we need a connection
         * to send / receive / discover services
         */
        void onBluetoothNotConnected();

    

    // custom exceptions
    public class BluetoothNotEnabledException extends Exception  
    public class BluetoothLowEnergyNotSupported extends Exception  
    public class BluetoothDeviceNotFound extends Exception  

    // service and characteristic uuids that are going to be used to
    // send / receive data between central and peripheral GATTs
    private static final String SERVICE_UUID = "FFE0-";
    private static final String CHARACTERISTIC_UUID = "FFE1-";

    // timeout for bluetooth scan (in ms)
    public static final int SCAN_TIMEOUT = 5000;

    // BLE LOG TAG
    public static final String LOG_TAG = "BLUETOOTH_BLE";

    // model
    private boolean bluetoothScanning;
    private boolean bluetoothConnected;
    private Map<String, BluetoothDevice> bluetoothScanResults;

    // gui
    private Activity activity;

    // bluetooth
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothLeScanner bluetoothLeScanner;
    private ScanCallback bluetoothScanCallback;
    private BluetoothGatt bluetoothGatt;
    private BluetoothGattCharacteristic characteristic;


    public BluetoothLowEnergy(Activity activity, BluetoothListener bluetoothListener)
        this.activity = activity;
        this.bluetoothListener = bluetoothListener;

        // this keeps track of the scanning and connection states
        this.bluetoothScanning = this.bluetoothConnected = false;

        // keeps track of the scanning results
        this.bluetoothScanResults = new HashMap<>();

        // set bluetooth pairing request and bonded callback
        // these broadcasters will be responsible to detect and validate
        // the bonded state of your device
        this.pairingRequestBroadcaster = new BluetoothPairingBroadcaster(this);
        this.bondedBroadcaster = new BluetoothBondStateBroadcaster(this);

        // set the scan callback methods that will add results to 
        // this.bluetoothScanResults map
        this.bluetoothScanCallback = new ScanCallback() 
            @Override
            public void onScanResult(int callbackType, ScanResult result) 
                super.onScanResult(callbackType, result);
                addScanResult(result);
            

            @Override
            public void onBatchScanResults(List<ScanResult> results) 
                super.onBatchScanResults(results);
                for (ScanResult result: results) 
                    addScanResult(result);
                
            

            @Override
            public void onScanFailed(int errorCode) 
                super.onScanFailed(errorCode);
                Log.e(LOG_TAG, "Scan Failed with code " + errorCode);
            

            private void addScanResult(ScanResult result) 
                BluetoothDevice device = result.getDevice();
                String deviceAddress = device.getAddress();
                bluetoothScanResults.put(deviceAddress, device);
                Log.d(LOG_TAG, "Found device " + deviceAddress);
            
        ;

        // Use this to determine whether BLE is supported on the device.
        if (!this.activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) 
            throw new BluetoothLowEnergyNotSupported();
        
    

    /**
     * This method should be called when the activity is destroyed
     */
    public void onDestroy()
        this.bondedBroadcaster.onDestroy();
        this.pairingRequestBroadcaster.onDestroy();
        this.disconnect();
    

    /**
     * This method is called when we finish pairing/bonding to the device
     */
    public void onBluetoothBonded()
        // if we have the services already discovered, then we can 
        // send/receive data, to do so we call the bluetooth listener below
        if (servicesDiscovered)
            this.bluetoothListener.onBluetoothConnectionReady();
        // if we know we have a connection established, then we can 
        // discover services
         else if (bluetoothConnected)
            bluetoothGatt.discoverServices();
        
    

    /**
     * This method is called whenever a connection is established or a disconnection happens
     */
    @Override        
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) 
        super.onConnectionStateChange(gatt, status, newState);

        BluetoothDevice bluetoothDevice = gatt.getDevice();

        // if these conditions == true, then we have a disconnect
        if ( status == BluetoothGatt.GATT_FAILURE ||
                status != BluetoothGatt.GATT_SUCCESS ||
                newState == BluetoothProfile.STATE_DISCONNECTED) 
            Log.d(LOG_TAG, String.format(Locale.getDefault(),
                    "Disconnected from %s (%s) - status %d - state %d",
                    bluetoothDevice.getName(),
                    bluetoothDevice.getAddress(),
                    status,
                    newState
            ));
            this.disconnect();
        // if these conditions == true, then we have a successful connection
         else if (newState == BluetoothProfile.STATE_CONNECTED) 
            bluetoothConnected = true;
            Log.d(LOG_TAG, String.format(Locale.getDefault(),
                    "Connected to %s (%s) - status %d - state %d",
                    bluetoothDevice.getName(),
                    bluetoothDevice.getAddress(),
                    status,
                    newState
            ));
            // this sleep is here to avoid TONS of problems in BLE, that occur whenever we start 
            // service discovery immediately after the connection is established 
            try 
                Thread.sleep(600);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            gatt.discoverServices();
        
    

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) 
        super.onServicesDiscovered(gatt, status);
        if (status != BluetoothGatt.GATT_SUCCESS) 
            return;
        
        // BEGIN - find the service and characteristic that we want (defined as a static attribute 
        // of the BluetoothLowEnergy class)
        Log.d(LOG_TAG, "Discovering services ...");
        BluetoothGattService service = null;
        for (BluetoothGattService serv: gatt.getServices())
            Log.d(LOG_TAG, "Found service " + serv.getUuid().toString());
            if (serv.getUuid().toString().toUpperCase().contains(SERVICE_UUID))
                service = serv;
                Log.d(LOG_TAG, "---> Selected service " + serv.getUuid().toString());
                break;
            
        
        if (service == null)
            return;
        
        for (BluetoothGattCharacteristic charac: service.getCharacteristics())
            Log.d(LOG_TAG, "Found characteristic " + charac.getUuid().toString());
            if (charac.getUuid().toString().toUpperCase().contains(CHARACTERISTIC_UUID))
                this.characteristic = charac;
                Log.d(LOG_TAG, "---> Selected characteristic " + charac.getUuid().toString());
                break;
            
        
        if (this.characteristic == null)
            return;
        
        Log.d(LOG_TAG, "Setting write and notification to the characteristic ...");
        bluetoothAdapter.cancelDiscovery();
        // END - find the service and characteristic 
        // set that we want to write to the selected characteristic and be notified if
        // it changes (the remote GATT peripheral sends data to the Android's GATT Center)
        this.characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
        gatt.setCharacteristicNotification(this.characteristic, true);
        // we finished service discovery
        this.servicesDiscovered = true;
        // if we have paired/bonded then we are ready to send/receive data            
        if (pairingRequestBroadcaster.getDevicePIN().isEmpty() || bondedBroadcaster.isDeviceBonded()) 
            this.bluetoothListener.onBluetoothConnectionReady();
        
    

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic charac, int status) 
        super.onCharacteristicRead(gatt, charac, status);
        restartDisconnectTimeout();
        if (status != BluetoothGatt.GATT_SUCCESS) 
            return;
        
        try 
            String characValue = new String(charac.getValue(), CHARSET)
                    .replaceAll(DATA_FILTER_REGEX,"");
            Log.i(LOG_TAG, String.format(Locale.getDefault(),
                    "Characteristic Read - %s",
                    characValue
            ));
            if (charac.getUuid().equals(this.characteristic.getUuid())) 
                this.bluetoothListener.onBluetoothReceiveMsg(characValue);
            
         catch (UnsupportedEncodingException e) 
            Log.e(LOG_TAG, "Characteristic Read - Failed to convert message string to byte array");
        
    

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic charac, int status) 
        super.onCharacteristicWrite(gatt, charac, status);
        restartDisconnectTimeout();
        try 
            String characValue = new String(charac.getValue(), CHARSET);
            Log.i(LOG_TAG, String.format(Locale.getDefault(),
                    "Characteristic Write - SUCCESS - %s",
                    characValue
            ));
            bluetoothListener.onBluetoothSend( characValue, (status == BluetoothGatt.GATT_SUCCESS) );
         catch (UnsupportedEncodingException e) 
            Log.e(LOG_TAG, "Characteristic Write - Failed to convert message string to byte array");
        
    


    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic charac) 
        super.onCharacteristicChanged(gatt, charac);
        Log.d(LOG_TAG,"Characteristic Changed");
        onCharacteristicRead(gatt, charac, BluetoothGatt.GATT_SUCCESS);
    

    /**
     * Remove pairing/bonding of the device 
     * @param device Device to remove bonding
     */
    public static void removeBond(BluetoothDevice device)
        try 
            if (device == null)
                throw new Exception();
            
            Method method = device.getClass().getMethod("removeBond", (Class[]) null);
            method.invoke(device, (Object[]) null);
            Log.d(LOG_TAG, "removeBond() called");
            Thread.sleep(600);
            Log.d(LOG_TAG, "removeBond() - finished method");
         catch (Exception e) 
            e.printStackTrace();
        
    

    /**
     * Clears the GATT services cache, so that new services can be discovered 
     * @param bluetoothGatt GATT Client to clear service's discovery cache
     */
    public static void refresh(BluetoothGatt bluetoothGatt)
        try 
            Method method = bluetoothGatt.getClass().getMethod("refresh", (Class[]) null);
            method.invoke(bluetoothGatt, (Object[]) null);
         catch (Exception e)
            e.printStackTrace();
        
    

    /**
     * Connect to the GATT Peripheral device
     * @param uuid GATT Peripheral address / mac / uuid to connect to
     * @param pin PIN to authenticate and pair to the device
     */
    public void connect(String uuid, String pin) throws BluetoothNotEnabledException, BluetoothDeviceNotFound 
        checkBluetooth();
        // do not connect twice
        if (this.isConnected())
            return;
        
        // get device
        BluetoothDevice device = this.bluetoothScanResults.get(uuid);
        if (device == null)
            throw new BluetoothDeviceNotFound();
        
        this.deviceUUID = uuid;
        pairingRequestBroadcaster.setDevicePIN(pin);
        removeBond(device);
        // create connection to the bluetooth device
        bluetoothGatt = device.connectGatt(activity, false, this);
        refresh(bluetoothGatt);
    

    /**
     * Disconnect from BLE device. This method should be called whenever we want to 
     * close the APP, or the BLE connection.
     */
    public void disconnect()             
        Log.d(LOG_TAG, "disconnect() - executed");
        if (bluetoothGatt != null) 
            if (characteristic != null) 
                bluetoothGatt.setCharacteristicNotification(characteristic, false);
            
            //remove device authorization/ bond/ pairing
            removeBond(bluetoothGatt);
            // disconnect now
            bluetoothGatt.disconnect();
            bluetoothGatt.close();
            Log.d(LOG_TAG, "disconnect() - bluetoothGatt disconnect happened");
        
        bluetoothGatt = null;
        characteristic = null;
        bluetoothConnected = false;
        servicesDiscovered = false;
        // set device as not bonded anymore
        bondedBroadcaster.resetDeviceBonded();
    

    /**
     * bluetooth nearby devices scan is on
     * @return true if scanning is on, false otherwise
     */
    public boolean isScanning()
        return (this.bluetoothScanning);
    

    /**
     * Check bluetooth system state (on or off)
     * @return true if system is on, false otherwise
     */
    public boolean isEnabled()
        try 
            checkBluetooth();
            return bluetoothAdapter.isEnabled();
         catch (BluetoothNotEnabledException e) 
            return false;
        
    

    /**
     * Check bluetooth connection
     * @return true if connected, false otherwise
     */
    public boolean isConnected()
        return (this.bluetoothConnected);
    

    /**
     * Start bluetooth scan for nearby devices
     * @param filters Scan filters that define what devices to scan for
     */
    public void startScan(List<ScanFilter> filters)
            throws BluetoothNotEnabledException
        checkBluetooth();
        // dont run two scans simultaneously
        if (isScanning()) 
            return;
        
        // disconnect previously connected devices
        if (isConnected()) 
            this.disconnect();
            return;
        
        // setup bluetooth scanning settings
        ScanSettings settings = new ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
                .build();

        // start scanning
        this.bluetoothScanning = true;
        this.bluetoothScanResults.clear();
        this.bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();

        // Stops scanning after a pre-defined scan period.
        Handler bluetoothHandler = new Handler();
        bluetoothHandler.postDelayed(new Runnable() 
            @Override
            public void run() 
                stopScan();
            
        , SCAN_TIMEOUT);

        // start scan with default scan callback
        this.bluetoothLeScanner.startScan(filters, settings, bluetoothScanCallback);
        // we have started successfully the BLE scanning
        bluetoothListener.onBluetoothStartScan();
    

    /**
     * Stop bluetooth scan for nearby devices
     */
    public void stopScan()
        if (!bluetoothScanning) 
            return;
        
        // set app scan state to false
        bluetoothScanning = false;
        if (bluetoothLeScanner != null) 
            bluetoothLeScanner.stopScan(bluetoothScanCallback);
            bluetoothLeScanner = null;
        
        // we have stopped BLE scanning, call the user's callback
        bluetoothListener.onBluetoothStopScan(bluetoothScanResults.values());
    

    /**
     * Send a message via bluetooth
     * @param msg message to send
     */
    public void send(String msg) 
        if (!bluetoothConnected || characteristic == null)
            bluetoothListener.onBluetoothNotConnected();
            return;
        
        try 
            msg = msg.replaceAll(DATA_FILTER_REGEX, "") + TERMINATION_CHAR;
            Log.d(LOG_TAG, String.format(Locale.getDefault(),
                    "Sending message: %s",
                    msg));
            characteristic.setValue(msg.getBytes(CHARSET));
            bluetoothGatt.writeCharacteristic(characteristic);
         catch (UnsupportedEncodingException e) 
            Log.e(LOG_TAG,
                "BluetoothLowEnergy.send: Failed to convert message string to byte array");
        
    

    public String getDeviceUUID()
        return deviceUUID;
    

    public Activity getActivity()
        return activity;
    

    /**
     * Check if bluetooth is enabled and working properly
     */
    private void checkBluetooth() throws BluetoothNotEnabledException
        if (bluetoothAdapter == null) 
            final BluetoothManager bluetoothManager =
                    (BluetoothManager) activity.getSystemService(Context.BLUETOOTH_SERVICE);
            if (bluetoothManager == null)
                throw new BluetoothNotEnabledException();
            
            bluetoothAdapter = bluetoothManager.getAdapter();
        

        // Ensures Bluetooth is available on the device and it is enabled. If not,
        // displays a dialog requesting user permission to enable Bluetooth.
        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) 
            throw new BluetoothNotEnabledException();
        
    


避免上述问题的关键方法和功能是:

Thread.sleep(600) removeBond(device) refresh(gatt) gatt.disconnect() gatt.close()

【讨论】:

这是一个有趣的代码转储,但可以使用一些解释来说明如何避免错误 19 和 22。 感谢分享。我基于它在我的代码中添加了一些东西。我还注意到一些看起来多余的小东西:status == BluetoothGatt.GATT_FAILURE 并在 BluetoothGattCallback 的覆盖方法上调用 super【参考方案3】:

在我的情况下,我从蓝牙堆栈收到此响应,因为该设备已与我的手机绑定。我从我的设置中删除了它,错误 22 消失了。

【讨论】:

【参考方案4】:

在 aosp(android 源代码)中。您可以在蓝牙源代码中找到任何错误,并了解状态码的含义。 文件路径为 system/bt/stack/include/gatt_api.h

这是链接:https://android.googlesource.com/platform/system/bt/+/master/stack/include/gatt_api.h。但它都以十六进制显示。

例如:

hex Decimal reason
0x08 8 connection timeout
0x13 19 connection terminate by peer user
0x16 22 connectionterminated by local host
0x22 34 connection fail for LMP response tout
0x85 133 gatt_error

【讨论】:

以上是关于Android BLE Gatt 连接更改状态的主要内容,如果未能解决你的问题,请参考以下文章

Android BLE - 连接到多个设备似乎失败并且两个连接的 GATT 响应相同?

BLE GATT 上传数据 - Android

我在 Android 应用程序(Java)和 ESP32 BLE 服务器之间的 Gatt 连接有问题

BLE Gatt onConnectionStateChange 失败,状态 133 和 257

Android BLE GATT 外设模式通知

Android BLE 无法从设备接收 Gatt 特性通知