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 响应相同?
我在 Android 应用程序(Java)和 ESP32 BLE 服务器之间的 Gatt 连接有问题