Skip to content

Commit

Permalink
1.Fix bugs; 2.Update demo
Browse files Browse the repository at this point in the history
  • Loading branch information
Ficat committed Jan 21, 2025
1 parent 350780e commit f2fe3d6
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 123 deletions.
211 changes: 151 additions & 60 deletions README.md

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions easyble/src/main/java/com/ficat/easyble/BleManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ public void setMtu(BleDevice device, int mtu, BleMtuCallback callback) {
*/
public List<ServiceInfo> getDeviceServices(BleDevice device) {
if (device == null) {
return null;
throw new IllegalArgumentException("BleDevice is null");
}
return getDeviceServices(device.getAddress());
}
Expand Down Expand Up @@ -493,7 +493,7 @@ public static boolean supportBle(Context context) {
* Turn on local bluetooth, calling the method will show users a request dialog
* to grant or reject,so you can get the result from Activity#onActivityResult().
* <p>
* Note that if Android12(api31) or higher, only the permission
* Note that if on Android12(api31) or higher devices, only the permission
* {@link android.Manifest.permission#BLUETOOTH_CONNECT} has been granted by user,
* calling this method can work, or it will return false directly.
*
Expand Down Expand Up @@ -590,6 +590,7 @@ public static List<String> getBleRequiredPermissions() {
//BLUETOOTH_CONNECT: used to get peripheral device name (BluetoothDevice#getName())
list.add(Manifest.permission.BLUETOOTH_SCAN);
list.add(Manifest.permission.BLUETOOTH_CONNECT);
list.add(Manifest.permission.BLUETOOTH_ADVERTISE);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {//Android10
list.add(Manifest.permission.ACCESS_FINE_LOCATION);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//Android6
Expand Down
2 changes: 1 addition & 1 deletion easyble/src/main/java/com/ficat/easyble/gatt/BleGatt.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import java.util.List;

public interface BleGatt {
void connect(int connectTimeout, BleDevice device, BleConnectCallback callback,
void connect(int timeoutMills, BleDevice device, BleConnectCallback callback,
BleHandlerThread bleHandlerThread);

void disconnect(String address);
Expand Down
138 changes: 81 additions & 57 deletions easyble/src/main/java/com/ficat/easyble/gatt/BleGattImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ public final class BleGattImpl implements BleGatt {
private static final int MTU_MAX = 512;
private static final int MTU_MIN = 23;
private static final int ATT_OCCUPY_BYTES_NUM = 3;
private static final int DEFAULT_TIME_OUT_MILLS = 10000;//default 10s

private final Context mContext;
private int mConnectTimeout = 10000;//default 10s
private final Map<String, BleGattCommunicator> mBleGattCommunicatorMap;
private final Handler mMainHandler;

Expand All @@ -62,36 +62,33 @@ public final class BleGattImpl implements BleGatt {
}

@Override
public void connect(int connectTimeout, final BleDevice device, final BleConnectCallback callback,
public void connect(int timeoutMills, final BleDevice device, final BleConnectCallback callback,
BleHandlerThread bleHandlerThread) {
if (!mBleGattCommunicatorMap.containsKey(device.getAddress())) {
mBleGattCommunicatorMap.put(device.getAddress(), new BleGattCommunicator(device, new AccessKey()));
BleGattCommunicator c = mBleGattCommunicatorMap.get(device.getAddress());
if (c == null) {
c = new BleGattCommunicator(device, new AccessKey());
mBleGattCommunicatorMap.put(device.getAddress(), c);
}
final BleGattCommunicator communicator = mBleGattCommunicatorMap.get(device.getAddress());
if (communicator == null) {
callback.onFailure(BleCallback.FAILURE_OTHER, "BleGattCommunicator not found", device);
return;
}
if (communicator.mBleHandlerThread != bleHandlerThread) {
// Old BleHandlerThread should be shut down, and reset handler to main thread handler
communicator.stopHandlerThreadAndResetHandler();
// If select a specified thread, all callbacks run in this thread,
// otherwise run in main thread
communicator.mBleHandlerThread = bleHandlerThread;
if (bleHandlerThread != null) {
if (!bleHandlerThread.isLooperPrepared()) {
bleHandlerThread.start();
}
communicator.mHandler = new Handler(bleHandlerThread.getLooperInThread());
final BleGattCommunicator communicator = c;

// If select a specified thread, all callbacks run in this thread,
// otherwise run in main thread
Handler handler = mMainHandler;
if (bleHandlerThread != null) {
if (!bleHandlerThread.isLooperPrepared()) {
bleHandlerThread.start();
}
handler = new Handler(bleHandlerThread.getLooperInThread());
}

// Check if the connection can start
boolean bluetoothOff = !BleManager.isBluetoothOn();
boolean noPermission = !BleManager.connectionPermissionGranted(mContext);
boolean reachConnectionMaxNum = reachConnectionMaxNum();
boolean isConnecting = communicator.mDevice.isConnecting();
boolean isConnected = communicator.mDevice.isConnected();
if (bluetoothOff || noPermission || reachConnectionMaxNum || isConnecting || isConnected) {
communicator.mHandler.post(new Runnable() {
handler.post(new Runnable() {
@Override
public void run() {
String tips = "";
Expand All @@ -106,18 +103,17 @@ public void run() {
} else {
tips = "Connection has been established already";
}
//Use the previous BleDevice object that maintains correct connection state
callback.onStart(false, tips, communicator.mDevice);
callback.onStart(false, tips, device);
}
});
// Failed to start connection, so quit looper
if (bleHandlerThread != null) {
bleHandlerThread.quitLooperSafely();
}
return;
}
if (communicator.mDevice != device) {//Use the newest BleDevice object
communicator.mDevice = device;
}
communicator.mConnectCallback = callback;

// Connect to gatt service
// Connect to GATT
BluetoothGatt gatt;
// if (Build.VERSION.SDK_INT >= 26) {
// gatt = device.getBluetoothDevice().connectGatt(mContext, false, communicator,
Expand All @@ -126,26 +122,45 @@ public void run() {
// gatt = device.getBluetoothDevice().connectGatt(mContext, false, communicator);
// }
gatt = device.getBluetoothDevice().connectGatt(mContext, false, communicator);

// Failed to connect GATT
if (gatt == null) {
communicator.mHandler.post(new Runnable() {
handler.post(new Runnable() {
@Override
public void run() {
callback.onStart(false, "Failed to call BluetoothDevice#connectGatt()", device);
}
});
//Failed to start connection, so quit looper
if (bleHandlerThread != null) {
bleHandlerThread.quitLooperSafely();
}
return;
}
communicator.setBleDeviceConnectionState(BleDevice.CONNECTING);

// Connected with GATT, update communicator
if (communicator.mBleHandlerThread != bleHandlerThread) {
communicator.resetThreadAndHandler();
communicator.mBleHandlerThread = bleHandlerThread;
if (bleHandlerThread != null) {
communicator.mHandler = new Handler(bleHandlerThread.getLooperInThread());
}
}
if (communicator.mDevice != device) {// Use the newest BleDevice object
communicator.mDevice = device;
}
communicator.mGatt = gatt;
communicator.mConnectCallback = callback;
communicator.setBleDeviceConnectionState(BleDevice.CONNECTING);
communicator.mHandler.post(new Runnable() {
@Override
public void run() {
callback.onStart(true, "Connection started successfully", device);
communicator.mConnectCallback.onStart(true,
"Connection started successfully", communicator.mDevice);
}
});
if (connectTimeout > 0) {
mConnectTimeout = connectTimeout;
}

// Send connection timeout msg
Message msg = Message.obtain(communicator.mHandler, new Runnable() {
@Override
public void run() {
Expand All @@ -154,14 +169,14 @@ public void run() {
communicator.refreshDeviceCache();
communicator.mGatt.close();
}
communicator.clearAll();
communicator.setBleDeviceConnectionState(BleDevice.DISCONNECTED);
callback.onFailure(BleCallback.FAILURE_CONNECTION_TIMEOUT, "Connection timed out", communicator.mDevice);
communicator.stopHandlerThreadAndResetHandler();
communicator.mConnectCallback.onFailure(BleCallback.FAILURE_CONNECTION_TIMEOUT,
"Connection timed out", communicator.mDevice);
communicator.clearAndResetAll();
}
});
msg.obj = communicator.mDevice.getAddress();
communicator.mHandler.sendMessageDelayed(msg, mConnectTimeout);
communicator.mHandler.sendMessageDelayed(msg, timeoutMills > 0 ? timeoutMills : DEFAULT_TIME_OUT_MILLS);
}

@Override
Expand All @@ -180,7 +195,6 @@ public void disconnect(String address) {
if (communicator.mDevice.isConnecting()) {
communicator.refreshDeviceCache();
communicator.mGatt.close();
communicator.clearAll();
communicator.setBleDeviceConnectionState(BleDevice.DISCONNECTED);
communicator.mHandler.post(new Runnable() {
@Override
Expand All @@ -191,7 +205,7 @@ public void run() {
}
}
});
communicator.stopHandlerThreadAndResetHandler();
communicator.clearAndResetAll();
}
}

Expand Down Expand Up @@ -394,7 +408,7 @@ public void run() {
}
// Check data and length
Handler handler = communicator.mHandler;
int maxNumPerPack = MTU_MAX - ATT_OCCUPY_BYTES_NUM;
int maxNumPerPack = communicator.mCurrentMtu - ATT_OCCUPY_BYTES_NUM;
if (data == null || data.length < 1 || data.length > maxNumPerPack) {
handler.post(new Runnable() {
@Override
Expand Down Expand Up @@ -454,7 +468,7 @@ public void run() {
}
Handler handler = communicator.mHandler;
// Check data and data length
int maxNumPerPack = MTU_MAX - ATT_OCCUPY_BYTES_NUM;
int maxNumPerPack = communicator.mCurrentMtu - ATT_OCCUPY_BYTES_NUM;
boolean invalidData = writeData == null || writeData.length == 0;
boolean invalidPackLen = lengthPerPackage < 1 || lengthPerPackage > maxNumPerPack;
if (invalidData || invalidPackLen) {
Expand Down Expand Up @@ -535,9 +549,9 @@ public void onFailure(int failureCode, String info, BleDevice device) {

private void writeData(final BleDevice device, String serviceUuid, String writeUuid, byte[] data,
final BleWriteCallback callback, BleGattCommunicator communicator) {
int minNumPerPack = MTU_MIN - ATT_OCCUPY_BYTES_NUM;
int minNumPerPack = communicator.mCurrentMtu - ATT_OCCUPY_BYTES_NUM;
if (data.length > minNumPerPack) {
Logger.w("Data length is greater than the default(20 Bytes), make sure MTU >= " +
Logger.w("Data length is greater than the default(" + minNumPerPack + " Bytes), make sure MTU >= " +
(data.length + ATT_OCCUPY_BYTES_NUM));
}
// Add callback to communicator
Expand Down Expand Up @@ -624,7 +638,7 @@ public void run() {
handler.post(new Runnable() {
@Override
public void run() {
callback.onFailure(BleCallback.FAILURE_OTHER, "Failed to read rssi due to unknown reason", device);
callback.onFailure(BleCallback.FAILURE_OTHER, "Failed to request MTU due to unknown reason", device);
}
});
}
Expand Down Expand Up @@ -706,7 +720,7 @@ private static final class BleGattCommunicator extends BluetoothGattCallback {
private final Map<OperationIdentify, BleNotifyCallback> mNotifyCallbackMap;
private final Map<OperationIdentify, BleReadCallback> mReadCallbackMap;
private final Map<OperationIdentify, BleWriteCallback> mWriteCallbackMap;

private volatile int mCurrentMtu;
private BluetoothGatt mGatt;

private BleGattCommunicator(BleDevice device, AccessKey key) {
Expand All @@ -723,6 +737,7 @@ private BleGattCommunicator(BleDevice device, AccessKey key) {
this.mReadCallbackMap = new ConcurrentHashMap<>();
this.mWriteCallbackMap = new ConcurrentHashMap<>();
this.mServiceList = new ArrayList<>();
setCurrentMtu(BleGattImpl.MTU_MIN);
}

@Override
Expand All @@ -742,7 +757,6 @@ public void onConnectionStateChange(BluetoothGatt gatt, final int status, int ne
case BluetoothProfile.STATE_DISCONNECTED:
refreshDeviceCache();
gatt.close();
clearAll();
setBleDeviceConnectionState(BleDevice.DISCONNECTED);
runOrQueueCallback(new Runnable() {
@Override
Expand All @@ -752,13 +766,12 @@ public void run() {
}
}
});
stopHandlerThreadAndResetHandler();
clearAndResetAll();
break;
}
} else {
refreshDeviceCache();
gatt.close();
clearAll();
if (mDevice.isConnecting()) {
//Remove connection timeout message
mHandler.removeCallbacksAndMessages(address);
Expand All @@ -785,7 +798,7 @@ public void run() {
}
});
}
stopHandlerThreadAndResetHandler();
clearAndResetAll();
}
}

Expand Down Expand Up @@ -952,6 +965,7 @@ public void onMtuChanged(BluetoothGatt gatt, final int mtu, int status) {
return;
}
if (status == BluetoothGatt.GATT_SUCCESS) {
setCurrentMtu(mtu);
runOrQueueCallback(new Runnable() {
@Override
public void run() {
Expand Down Expand Up @@ -986,29 +1000,39 @@ private ServiceInfo getService(String serviceUuid) {
return null;
}

private void clearAll() {
mConnectCallback = null;
mMtuCallback = null;
mRssiCallback = null;
private void clearAndResetAll() {
mReadCallbackMap.clear();
mWriteCallbackMap.clear();
mNotifyCallbackMap.clear();
mServiceList.clear();
mConnectCallback = null;
mMtuCallback = null;
mRssiCallback = null;
mGatt = null;
// Reset MTU
setCurrentMtu(BleGattImpl.MTU_MIN);
// Reset BleHandlerThread and Handler
resetThreadAndHandler();
}

private void stopHandlerThreadAndResetHandler() {
//Stop BleHandlerThread
private void resetThreadAndHandler() {
// Stop BleHandlerThread
if (mBleHandlerThread != null) {
mBleHandlerThread.quitLooperSafely();
mBleHandlerThread = null;
}
//Reset handler
// Reset handler
if (mHandler.getLooper() != Looper.getMainLooper()) {
mHandler = new Handler(Looper.getMainLooper());
}
}

private void setCurrentMtu(int mtu) {
synchronized (BleGattCommunicator.this) {
mCurrentMtu = mtu;
}
}

private void setBleDeviceConnectionState(int newConnState) {
BleDeviceAccessor.setBleDeviceConnection(mDevice, newConnState, mAccessKey);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Looper getLooperInThread() {
}

boolean quitLooperSafely() {
Looper looper = super.getLooper();
Looper looper = getLooperInThread();
if (looper != null) {
looper.quitSafely();
return true;
Expand Down
4 changes: 4 additions & 0 deletions sample/src/main/java/com/ficat/sample/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_request_permission:
List<String> list = BleManager.getBleRequiredPermissions();
// Lower version devices may not require any permissions, so check it
if (list.size() <= 0){
return;
}
EasyPermissions
.with(this)
.request(list.toArray(new String[0]))
Expand Down
Loading

0 comments on commit f2fe3d6

Please sign in to comment.