在Android中,connectGatt
方法是用于建立与BLE(Bluetooth Low Energy)设备的GATT(Generic Attribute Profile)连接的核心方法。这个过程涉及到多个组件和步骤,包括Android的蓝牙服务层、协议栈、控制器和BLE设备本身。本文对 connectGatt
流程及其源码进行分析。
一、BLE连接流程概述
1. 应用层(Java):
-
通过调用BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)方法来尝试与指定的蓝牙设备建立GATT连接。
-
connectGatt方法内部会初始化一个BluetoothGatt对象,并尝试通过调用mService.registerClient来注册这个GATT客户端。
2. 系统服务层(BluetoothManagerService):
-
mService.registerClient是BluetoothManagerService中的一个方法,用于注册一个新的GATT客户端。如果注册成功,会通过回调方式(如onClientRegistered)通知BluetoothGatt对象。
-
在onClientRegistered回调中,如果注册状态是GATT_SUCCESS,则会调用mService.clientConnect方法来尝试与蓝牙设备建立连接。
3. JNI调用:
-
mService.clientConnect方法内部会通过JNI调用到本地代码(C/C++层),即gattClientConnectNative函数。
-
gattClientConnectNative函数是JNI的本地实现,负责将Java层的连接请求转发到底层的蓝牙协议栈。
4. 蓝牙HAL层:
-
gattClientConnectNative函数最终会调用蓝牙HAL层的函数来执行实际的连接操作。
-
btif_gattc_open是蓝牙HAL层的一个接口,它直接与蓝牙硬件通信,执行连接蓝牙设备的具体操作。
二、connectGatt源码分析
要操作BLE设备,第一步就是要连接设备,其实就是连接BLE设备上的GATT service。这里结合源码,分析GATT连接流程,以及各个模块是怎么相互交互。
主要两个操作:
-
向GATT协议栈注册并获得新的client_if。此过程只是在gatt_cb.cl_rcb得到一个可用位置,不会触发蓝牙数据传输。
-
调用JNI注册的回调函数:btgattc_register_app_cb会通过JNI调用Java层的BluetoothGatt.mBluetoothGattCallback.onClientRegistered方法。这个回调不会直接回调到应用层,而是继续在BluetoothGatt内部处理,调用mService.clientConnect尝试连接到GATT服务器。
首先,一般应用层都是 BluetoothDevice
对象的 connectGatt
方法发起连接请求,来创建一个 GATT 连接。
mBluetoothGatt = device.connectGatt(this, false, mGattCallback, TRANSPORT_LE);
这里调用了方法connectGatt(),来看一下源码。
2.1. 调用 connectGatt
connectGatt
方法接受三个参数:上下文(Context
)、BluetoothGattCallback
(用于接收连接和通信事件的回调)以及一个布尔值(指示是否应该自动连接,即当设备变得可用时自动尝试连接)。
1. BluetoothDevice.connectGatt
/packages/modules/Bluetooth/framework/java/android/bluetooth/BluetoothDevice.java
public BluetoothGatt connectGatt(
Context context,
boolean autoConnect,
BluetoothGattCallback callback,
int transport,
boolean opportunistic,
int phy,
Handler handler) {
if (callback == null) {
throw new NullPointerException("callback is null");
}
// TODO(Bluetooth) check whether platform support BLE
// Do the check here or in GattServer?
IBluetoothGatt iGatt = BluetoothAdapter.getDefaultAdapter().getBluetoothGatt();
if (iGatt == null) {
// BLE is not supported
return null;
} else if (NULL_MAC_ADDRESS.equals(mAddress)) {
Log.e(TAG, "Unable to connect gatt, invalid address " + mAddress);
return null;
}
// 创建一个 BluetoothGatt 对象
BluetoothGatt gatt =
new BluetoothGatt(iGatt, this, transport, opportunistic, phy, mAttributionSource);
gatt.connect(autoConnect, callback, handler); // 发起连接
return gatt;
}
通过Binder机制获得一个蓝牙管理服务的代理对象,进而获得iGatt,iGatt也是一个Binder代理对象。然后调用了 BluetoothGatt的connect()方法,需要注意这里有一个参数autoConnect,如果为false,则表示直接连接,true表示自动连接,意思是等到设备可用,则会自动连接上。
接下来看gatt.connect()的实现。
2. BluetoothGatt.connect
/packages/modules/Bluetooth/framework/java/android/bluetooth/BluetoothGatt.java
/*package*/ boolean connect(
Boolean autoConnect, BluetoothGattCallback callback, Handler handler) {
if (DBG) {
Log.d(TAG, "connect() - device: " + mDevice + ", auto: " + autoConnect);
}
synchronized (mStateLock) {
// 判断当前连接状态
if (mConnState != CONN_STATE_IDLE) {
throw new IllegalStateException("Not idle");
}
mConnState = CONN_STATE_CONNECTING;
}
mAutoConnect = autoConnect;
// 这里向底层注册上层的应用
if (!registerApp(callback, handler)) {
synchronized (mStateLock) {
mConnState = CONN_STATE_IDLE;
}a
Log.e(TAG, "Failed to register callback");
return false;
}
// The connection will continue in the onClientRegistered callback
return true;
}
这里关键的一句是registerApp(callback),这是向底层注册App,底层就知道有App在使用蓝牙,有蓝牙消息的时候,就通过回调通知上层的App。BLE几乎所有操作都是通过异步回调实现的,即通过这个你自定义的 BluetoothGattCallback来通知你的应用。接下来继续看registerApp():
3. BluetoothGatt.registerApp
/packages/modules/Bluetooth/framework/java/android/bluetooth/BluetoothGatt.java
private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
return registerApp(callback, handler, false);
}
private boolean registerApp(
BluetoothGattCallback callback, Handler handler, boolean eattSupport) {
if (DBG) Log.d(TAG, "registerApp()");
if (mService == null) return false;
mCallback = callback;
mHandler = handler;
UUID uuid = UUID.randomUUID();
if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
try {
final SynchronousResultReceiver recv = SynchronousResultReceiver.get();
mService.registerClient(
new ParcelUuid(uuid),
mBluetoothGattCallback,
eattSupport,
mAttributionSource,
recv);
recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, "", e);
return false;
}
return true;
}
这里调用了mService.registerClient(),这里的mService就是connectGatt创建BluetoothGatt对象时传入的IBluetoothGatt类型的Binder代理对象。对于这个函数的名字为什么叫 registerClient,这是因为,在Binder机制中,被绑定的Service称为服务端,发起绑定的一方往往叫客户端。
在继续往下看registerClient()这个函数之前,我们的目标是连接 BLE 设备,到这一步了,还没有看到连接动作的踪影。这是怎么回事?前面我们说过,蓝牙几乎所有的操作都是依靠回调实现,我们先来看一下这里的mBluetoothGatt(class BluetoothGatt)的实现,看源代码中,这个回调对象(mBluetoothGattCallback)非常大,包含所有的 Gatt回调动作,我们这里主要看onClientRegistered()方法(后面会解释为什么会回调onClientRegistered,这里只要知道registerClient()会回调它)。
BluetoothGatt.onClientRegistered
/packages/modules/Bluetooth/framework/java/android/bluetooth/BluetoothGatt.java
private final IBluetoothGattCallback mBluetoothGattCallback =
new IBluetoothGattCallback.Stub() {
/**
* Application interface registered - app is ready to go
*
* @hide
*/
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
public void onClientRegistered(int status, int clientIf) {
if (DBG) {
Log.d(
TAG,
"onClientRegistered() -"
+ (" status=" + status)
+ (" clientIf=" + clientIf));
}
mClientIf = clientIf;
synchronized (mStateLock) {
if (mConnState == CONN_STATE_CLOSED) {
if (DBG) {
Log.d(
TAG,
"Client registration completed after closed,"
+ " unregistering");
}
unregisterApp();
return;
}
if (VDBG) {
if (mConnState != CONN_STATE_CONNECTING) {
Log.e(TAG, "Bad connection state: " + mConnState);
}
}
}
if (status != GATT_SUCCESS) {// 注册客户端失败,通知到应用的callback
runOrQueueCallback(
new Runnable() {
@Override
public void run() {
final BluetoothGattCallback callback = mCallback;
if (callback != null) {
callback.onConnectionStateChange(
BluetoothGatt.this,
GATT_FAILURE,
BluetoothProfile.STATE_DISCONNECTED);
}
}
});
synchronized (mStateLock) {
mConnState = CONN_STATE_IDLE;
}
return;
}
try {
final SynchronousResultReceiver recv = SynchronousResultReceiver.get();
// autoConnect is inverse of "isDirect"
mService.clientConnect(
mClientIf,
mDevice.getAddress(),
mDevice.getAddressType(),
!mAutoConnect,
mTransport,
mOpportunistic,
mPhy,
mAttributionSource,
recv); // 这里开始做真正的连接操作了
recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, "", e);
}
}
...
这个回调方法有两个参数status和clientIf,前者表示注册客户端是否成功。clientIf表示从底层返回的一个id,用来唯一标识这个客户端,接下来的所有客户端操作请求,都需要带上这个id。 这个回调方法中做的事情比较清晰,特别注意到mService.clientConnect(...),这里开始调用Service接口开始发起连接了。
从代码中可以看到,mService是一个很关键的对象,但是这个对象是从哪里来的呢?
4. 应用框架和蓝牙服务的衔接: Binder服务
在第一段代码的分析中就提到了iGatt对象,从BluetoothGatt的构造函数可以看出,其实mService = iGatt,iGatt是IBluetoothGatt接口的Binder。
我们看一下BluetoothAdapter是怎么获得的,BluetoothAdapter.getDefaultAdapter():
BluetoothAdapter.getDefaultAdapter
/packages/modules/Bluetooth/framework/java/android/bluetooth/BluetoothAdapter.java
public static synchronized BluetoothAdapter getDefaultAdapter() {
if (sAdapter == null) {
sAdapter = createAdapter(AttributionSource.myAttributionSource());
}
return sAdapter;
}
/** @hide */
public static BluetoothAdapter createAdapter(AttributionSource attributionSource) {
BluetoothServiceManager manager =
BluetoothFrameworkInitializer.getBluetoothServiceManager();
if (manager == null) {
Log.e(TAG, "BluetoothServiceManager is null");
return null;
}
IBluetoothManager service =
IBluetoothManager.Stub.asInterface(
manager.getBluetoothManagerServiceRegisterer().get());
if (service != null) {
return new BluetoothAdapter(service, attributionSource);
} else {
Log.e(TAG, "Bluetooth service is null");
return null;
}
}
这里是一个单例模式,通过系统API ServiceManager.getService() 获得的,这里大致逻辑就是,在系统启动时候,Android会启动一些系统服务。并通过ServiceManager管理这些服务,具体就不往下深究了。这里直接给出结论,这里Binder代理对象对应的本地对象是BluetoothManagerService,代码在 /packages/modules/Bluetooth/service/src/com/android/server/bluetooth/BluetoothManagerService.java
我们看一下怎么从BluetoothManagerService中获取到IBluetoothGatt的Binder本地对象。注意到 BluetoothManagerService中的一个方法:bluetoothStateChangeHandler(),这个方法是在蓝牙状态变化的时候,做一些处理的。跟踪以下这个函数的调用的地方,就能验证我们的猜想是对的。这一块和本文的关系不大。binder的详细分析这里不再继续。
2.2. 蓝牙服务:com.android.bluetooth.gatt.GattService
蓝牙服务的代码在<aosp>/packages/modules/Bluetooth/android/app/src/com/android/bluetooth,编译后生成Bluetooth.apk,安装在 /system/app/ 目录下面,GattService运行在com.android.bluetooth进程。接着来看 Binder 的 registerClient() 接口,这个 Binder 是 GattService 的一个内部类:
1. GattService.registerClient
/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/gatt/GattService.java
public void registerClient(ParcelUuid uuid, IBluetoothGattCallback callback,
boolean eattSupport, AttributionSource attributionSource,
SynchronousResultReceiver receiver) {
try {
registerClient(uuid, callback, eattSupport, attributionSource);
receiver.send(null);
} catch (RuntimeException e) {
receiver.propagateException(e);
}
}
实际上这里还是调用了GattService的registerClient方法:
2. registerClient
/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/gatt/GattService.java
void registerClient(UUID uuid, IBluetoothGattCallback callback, boolean eatt_support,
AttributionSource attributionSource) {
if (!Utils.checkConnectPermissionForDataDelivery(
this, attributionSource, "GattService registerClient")) {
return;
}
if (DBG) {
Log.d(TAG, "registerClient() - UUID=" + uuid);
}
mClientMap.add(uuid, null, callback, null, this);
mNativeInterface.gattClientRegisterApp(uuid.getLeastSignificantBits(),
uuid.getMostSignificantBits(), eatt_support);// 调用 native 接口
}
这里首先是把 uuid 以及对应的 callback 保存到一个 mClientMap 中去, uuid 是客户端的唯一标识,uuid 对应了客户端的回调函数 callback。接下来,调用gattClientRegisterAppNative() 接口向底层协议栈注册客户端,看一下函数定义:
private native void gattClientRegisterAppNative(long appUuidLsb, long appUuidMsb,
boolean eattSupport);
这里可以看出,实际上是客户端的标识 -- UUID 注册到底层去,UUID是128 bit,正好用两个long型的参数表示。这个函数是JNI的申明,具体的实现就在对应的 C/C++ 代码中。
2.3. 蓝牙服务和HAL的调用:JNI
上面的 gattClientRegisterAppNative() 对应的 JNI 的代码在哪里呢?通过查看 AndroidManifest.xml,我们知道Bluetooth 的自定义Application是“.btservice.AdapterApp”,里面有这样的代码:
/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java
public class AdapterService extends Service {
private static final String TAG = "BluetoothAdapterService";
private static final boolean DBG = true;
private static final boolean VERBOSE = false;
static {
if (DBG) {
Log.d(TAG, "Loading JNI Library");
}
if (Utils.isInstrumentationTestMode()) {
Log.w(TAG, "App is instrumented. Skip loading the native");
} else {
System.loadLibrary("bluetooth_jni");
}
}
这里是加载了libbluetooth_jni.so 动态库。我们再看jni目录的 Android.mk,这里正好是生成 libbluetooth_jni的编译脚本。这样我们就知道了对应的 C/C++ 代码在com_android_bluetooth_gatt.cpp。
/packages/modules/Bluetooth/android/app/jni/com_android_bluetooth_gatt.cpp
static int register_com_android_bluetooth_gatt_(JNIEnv* env) {
const JNINativeMethod methods[] = {
...
{"gattClientRegisterAppNative", "(JJZ)V",
(void*)gattClientRegisterAppNative},
...
这是注册JNI函数的标准方法,也是在该cpp,可以找到对应的函数实现。
1. gattClientRegisterAppNative
static void gattClientRegisterAppNative(JNIEnv* /* env */, jobject /* object */,
jlong app_uuid_lsb, jlong app_uu