Android connectGatt流程及源码分析

在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
  • 18
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值