[轉(zhuǎn)載]Android GATT 連接過程源碼分析

低功耗藍牙(BLE)設(shè)備的通信基本協(xié)議是 GATT, 要操作 BLE 設(shè)備吊档,第一步就是要連接設(shè)備簇爆,其實就是連接 BLE 設(shè)備上的 GATT service镀琉。 結(jié)合上一篇文章硕糊,我這里結(jié)合源碼院水,分析一下 GATT 連接的流程腊徙,以及各個模塊是怎么相互交互的。注意本文依據(jù)的是 Android 4.4 的源代碼檬某。

應(yīng)用框架層
首先撬腾,一般應(yīng)用層都是通過調(diào)用如下方法,來創(chuàng)建一個 GATT 連接的:

<code>mBluetoothGatt = device.connectGatt(this, false, mGattCallback); </code>
這里調(diào)用了方法 connectGatt()恢恼,我們來看一下源碼民傻,代碼在 /frameworks/base/core/java/android/bluetooth/BluetoothDevice.java:
<code>
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback) {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
IBluetoothManager managerService = adapter.getBluetoothManager();
try {
IBluetoothGatt iGatt = managerService.getBluetoothGatt();
if (iGatt == null) {
// BLE is not supported
return null;
}
// 創(chuàng)建一個 BluetoothGatt 對象
BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this);
// 發(fā)起連接
gatt.connect(autoConnect, callback);
return gatt;
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}</code>
這里通過 BluetoothAdapter 獲取 managerService ,這是通過 Binder 機制綁定的一個遠程藍牙管理服務(wù)场斑,進而獲得 iGatt漓踢,同樣,這也是一個遠程的 Binder 對象漏隐,這是一個非常關(guān)鍵的對象喧半,后面會詳細講。然后調(diào)用了 BluetoothGatt 的 connect() 方法青责,需要注意這里有一個參數(shù) autoConnect, 如果為 false挺据,則表示直接連接,true 表示自動連接脖隶,意思是等到設(shè)備可用扁耐,則會自動連接上。

接下來看 gatt.connect() 的實現(xiàn)产阱,代碼在/frameworks/base/core/java/android/bluetooth/BluetoothGatt.java:
<code>
boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
synchronized(mStateLock) {
// 判斷當前連接狀態(tài)
if (mConnState != CONN_STATE_IDLE) {
throw new IllegalStateException("Not idle");
}
mConnState = CONN_STATE_CONNECTING;
}
// 這里向底層注冊上層的應(yīng)用
if (!registerApp(callback)) {
synchronized(mStateLock) {
mConnState = CONN_STATE_IDLE;
}
Log.e(TAG, "Failed to register callback");
return false;
}

// the connection will continue after successful callback registration
mAutoConnect = autoConnect;
return true;

}</code>
這里面關(guān)鍵的一句是 registerApp(callback)婉称,這是向底層注冊 App,底層就知道有 App 在使用藍牙心墅,有藍牙消息的時候酿矢,就通過回調(diào)通知上層的 App。BLE 幾乎所有操作都是通過異步回調(diào)實現(xiàn)的怎燥,就是通過這個你自定義的 BluetoothGattCallback 來通知你的應(yīng)用的瘫筐。接下來我們繼續(xù)看 registerApp():
<code>
private boolean registerApp(BluetoothGattCallback callback) {
if (DBG) Log.d(TAG, "registerApp()");
if (mService == null) return false;

mCallback = callback;
UUID uuid = UUID.randomUUID();
if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);

try {
    mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
} catch (RemoteException e) {
    Log.e(TAG,"",e);
    return false;
}

return true;

}</code>
可以看到,這里調(diào)用了 mService.registerClient()铐姚,這里的 mService 就是第一步創(chuàng)建的 BluetoothGatt 對象的時候傳入的 IBluetoothGatt 類型的 Binder 對象策肝。對于這個函數(shù)的名字為什么叫 registerClient,這是因為隐绵,在 Binder 機制中之众,被綁定的 Service 作為稱為服務(wù)端,發(fā)起綁定的一方是客戶端依许。

在繼續(xù)往下看 registerClient() 這個函數(shù)之前棺禾,我們回憶一下,我們的目標是連接 BLE 設(shè)備峭跳,到這一步了膘婶,還沒有看到連接動作的蹤影缺前。這是怎么回事?前面我們說過悬襟,藍牙幾乎所有的操作都是依靠回調(diào)實現(xiàn)衅码,我們先來看一下這里的 mBluetoothGatt 的實現(xiàn),看源代碼中脊岳,這個回調(diào)對象非常大逝段,包含所有的 Gatt 回調(diào)動作,我們這里主要看 onClientRegistered() 方法:
<code>
private final IBluetoothGattCallback mBluetoothGattCallback =
new IBluetoothGattCallback.Stub() {
/**
* Application interface registered - app is ready to go
* @hide
*/
public void onClientRegistered(int status, int clientIf) {
if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
+ " clientIf=" + clientIf);
if (VDBG) {
synchronized(mStateLock) {
// 這里判斷狀態(tài)是不是 CONN_STATE_CONNECTING割捅,
// 注意到前面的 connect() 方法中已經(jīng)把 mConnState = CONN_STATE_CONNECTING;
if (mConnState != CONN_STATE_CONNECTING) {
Log.e(TAG, "Bad connection state: " + mConnState);
}
}
}
mClientIf = clientIf;
// 注冊客戶端失敗奶躯,通知到應(yīng)用的 callback
if (status != GATT_SUCCESS) {
mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
BluetoothProfile.STATE_DISCONNECTED);
synchronized(mStateLock) {
mConnState = CONN_STATE_IDLE;
}
return;
}
try {
// 這里開始做真正的連接操作了
mService.clientConnect(mClientIf, mDevice.getAddress(),
!mAutoConnect); // autoConnect is inverse of "isDirect"
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}

...

};</code>
這個回調(diào)方法有兩個參數(shù) status 和 clientIf拷肌,前者很好理解壶冒,就是表示注冊客戶端是否成功。clientIf 表示從底層返回的一個 id拾稳,用來唯一標示這個客戶端颊乘,接下來的所有客戶端的操作請求,都需要帶上這個 id醉锄。 這個回調(diào)方法中做的事情比較清晰乏悄,特別注意到 mService.clientConnect(...),這里開始調(diào)用 Service 接口開始發(fā)起連接了恳不。

從代碼中可以看到檩小,mService 是一個很關(guān)鍵的對象,但是這個對象是從哪里來的呢烟勋?

應(yīng)用框架和藍牙服務(wù)的銜接: Binder
在第一段代碼的分析中就提到了 iGatt 對象规求,從 BluetoothGatt 的構(gòu)造函數(shù)可以看出,其實 mService = iGatt卵惦,iGatt 是 IBluetoothGatt 接口的 Binder阻肿。

我們看一下 BluetoothAdapter 是怎么獲得的,BluetoothAdapter.getDefaultAdapter():
<code>
public static synchronized BluetoothAdapter getDefaultAdapter() {
if (sAdapter == null) {
IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
if (b != null) {
IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
sAdapter = new BluetoothAdapter(managerService);
} else {
Log.e(TAG, "Bluetooth binder is null");
}
}
return sAdapter;
}</code>
這里是一個單例模式沮尿,通過系統(tǒng)API ServiceManager.getService() 獲得的丛塌,這里大致邏輯就是,在系統(tǒng)那個啟動的時候畜疾,Android 會啟動一些系統(tǒng)服務(wù)并通過 ServiceManager 管理赴邻,具體我就不往下深究了,可以具體看一下老羅的這篇文章啡捶。這里直接給出結(jié)論姥敛,這里 Binder 對象對應(yīng)的服務(wù)是 BluetoothManagerService,代碼在 /frameworks/base/services/java/com/android/server/BluetoothManagerService.java瞎暑。

我們看一下怎么從 BluetoothManagerService 中獲取到 IBluetoothGatt 的 Binder 的彤敛。注意到 BluetoothManagerService 中有一個方法 bluetoothStateChangeHandler()与帆,沖方法名就大概可以知道這個方法是在藍牙狀態(tài)變化的時候,做一些處理的臊泌。跟蹤以下這個函數(shù)的調(diào)用的地方鲤桥,就能驗證我們的猜想是對的。這一塊和本文的關(guān)系不大渠概,我們現(xiàn)在來看一下 bluetoothStateChangeHandler() 的具體實現(xiàn):
<code>
private void bluetoothStateChangeHandler(int prevState, int newState) {
if (prevState != newState) {
//Notify all proxy objects first of adapter state change
if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
boolean isUp = (newState==BluetoothAdapter.STATE_ON);
sendBluetoothStateCallback(isUp);

        if (isUp) {
            // connect to GattService
            if (mContext.getPackageManager().hasSystemFeature(
                        PackageManager.FEATURE_BLUETOOTH_LE)) {
                Intent i = new Intent(IBluetoothGatt.class.getName());
                doBind(i, mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT);

                Intent iqc = new Intent(IQBluetooth.class.getName());
                doBind(iqc, mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT);
            }
        } else {
            //If Bluetooth is off, send service down event to proxy objects, and unbind
            if (!isUp && canUnbindBluetoothService()) {
                sendBluetoothServiceDownCallback();
                sendQBluetoothServiceDownCallback();
                unbindAndFinish();
            }
        }
    }

    //Send broadcast message to everyone else
    Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
    intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
    intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
    mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
            BLUETOOTH_PERM);
}

}</code>
看到 if (isUp) 這個分支中茶凳,會綁定到以 IBluetoothGatt 的類名為 Action Name 的服務(wù),也就是 action="android.bluetooth.IBluetoothGatt"播揪。我們在 /packages/apps/Bluetooth 這個 App 的 AndroidManifest.xml 中找到如下的聲明:
<code>
<service
android:process="@string/process"
android:name = ".gatt.GattService"
android:enabled="@bool/profile_supported_gatt">
<intent-filter>
<action android:name="android.bluetooth.IBluetoothGatt" />
</intent-filter>
</service> </code>
我們找到了贮喧,原來是綁定到了 com.android.bluetooth.gatt.GattService 上了。如果綁定成功猪狈,會回調(diào) mConnection 的 onServiceConnected()箱沦,其實現(xiàn)如下:
<code>
public void onServiceConnected(ComponentName className, IBinder service) {
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
...
} else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
msg.arg1 = SERVICE_IBLUETOOTHGATT;
}
...
msg.obj = service;
mHandler.sendMessage(msg);
}</code>
如果綁定的類名是 GattService,就會發(fā)送MESSAGE_BLUETOOTH_SERVICE_CONNECTED 消息給 mHandler雇庙,消息的第一個參數(shù)為 SERVICE_IBLUETOOTHGATT谓形,我們接下來看 mHandler 怎么處理的:
<ocde>
@Override
public void handleMessage(Message msg) {
if (DBG) Log.d (TAG, "Message: " + msg.what);
switch (msg.what) {
...
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
{
IBinder service = (IBinder) msg.obj;
synchronized(mConnection) {
if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
break;
}
...
}
}
}
}</code>
最終獲得 IBluetoothGatt 的 Binder,并賦值給 mBluetoothGatt疆前,最后通過如下接口寒跳,返回給前面的 BluetoothGatt。

至此竹椒,通過 Binder 機制童太,完成了應(yīng)用框架 API 到 Service 的綁定。別忘了我們的目標:分析BLE連接的流程胸完。通過前面的代碼分析我們知道书释,連接的時候,先調(diào)用了 'mService.registerClient()'赊窥,然后在注冊成功后爆惧,調(diào)用了 mService.clientConnect() 真正發(fā)起連接。我們知道了誓琼,這個 mService 實際上就是 com.android.bluetooth.gatt.GattService检激。我們接下來分析這個 Service,也就到了藍牙服務(wù)層了腹侣。

藍牙服務(wù)
藍牙服務(wù)的代碼在 packages/app/Bluetooth叔收,編譯以后成 Bluetooth.apk,安裝在 /system/app/ 目錄下面傲隶,GattService 運行在 com.android.bluetooth 進程中饺律。我們接著來看 Binder 的 registerClient() 接口,這個 Binder 是 GattService 的一個內(nèi)部類:
<code>
private static class BluetoothGattBinder extends IBluetoothGatt.Stub implements IProfileServiceBinder {
public void registerClient(ParcelUuid uuid, IBluetoothGattCallback callback) {
GattService service = getService();
if (service == null) return;
service.registerClient(uuid.getUuid(), callback);
}
}</code>
可以看到跺株,實際上這里還是調(diào)用了 GattService 的 registerClient 方法:
<code>
void registerClient(UUID uuid, IBluetoothGattCallback callback) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");

if (DBG) Log.d(TAG, "registerClient() - UUID=" + uuid);
mClientMap.add(uuid, callback);
// 調(diào)用 native 接口
gattClientRegisterAppNative(uuid.getLeastSignificantBits(),
        uuid.getMostSignificantBits());

}</code>
這里首先是把 uuid 以及對應(yīng)的 callback 保存到一個 mClientMap 中去复濒,這里從名字上我們就能大概清楚這里的作用脖卖,這里的 uuid 是客戶端的唯一標示, uuid 對應(yīng)了客戶端的回調(diào)函數(shù) callback巧颈。接下來畦木,調(diào)用了 gattClientRegisterAppNative() 接口向底層協(xié)議棧注冊客戶端,看一下函數(shù)定義:
<code>
private native void gattClientRegisterAppNative(long app_uuid_lsb, long app_uuid_msb); </code>
這里可以看出砸泛,實際上是客戶端的標示 -- UUID 注冊到底層去十籍,UUID 是 128 bit, 正好用兩個 long 型的參數(shù)表示。這個函數(shù)是 JNI 的申明唇礁,具體的實現(xiàn)就在對應(yīng)的 C/C++ 代碼中勾栗。

藍牙服務(wù)和 HAL 的調(diào)用:JNI
上面的 gattClientRegisterAppNative() 對應(yīng)的 JNI 的代碼在哪里呢?通過查看 AndroidManifest.xml盏筐,我們知道 Bluetooth 的自定義 Application 是 AdapterApp围俘,里面有這樣的代碼:
<code>
static {
if (DBG) Log.d(TAG,"Loading JNI Library");
System.loadLibrary("bluetooth_jni");
}</code>
這里是加載了 libbluetooth_jni.so 動態(tài)庫。我們再看 jni 目錄的 Android.mk琢融,這里正好是生成 libbluetooth_jni 的編譯腳本界牡。這樣我們就知道了對應(yīng)的 C/C++ 代碼在 com_android_bluetooth_gatt.cpp:
<code>
static JNINativeMethod sMethods[] = {
...
{"gattClientRegisterAppNative", "(JJ)V", (void ) gattClientRegisterAppNative},
...
}</code>
這是注冊 JNI 函數(shù)的標準方法,關(guān)于 JNI 的詳細語法漾抬,可以參考這里欢揖。可以找到對應(yīng)的函數(shù)實現(xiàn)如下:
<code>
static void gattClientRegisterAppNative(JNIEnv
env, jobject object,
jlong app_uuid_lsb, jlong app_uuid_msb )
{
bt_uuid_t uuid;

if (!sGattIf) return;
set_uuid(uuid.uu, app_uuid_msb, app_uuid_lsb);
sGattIf->client->register_client(&uuid);

}</code>
這里調(diào)用了 sGattIf 的 client 的 register_client() 方法奋蔚,這里還是把客戶端的標示 UUID 傳遞下去。這里的 sGattIf 是什么呢烈钞?

static const btgatt_interface_t *sGattIf = NULL;
它是一個 btgatt_interface_t 類型的變量泊碑,sGattIf 的初始化在 initializeNative() 中,這個函數(shù)在 GattService.start() 方法中被調(diào)用了毯欣,這個函數(shù)定義如下:
<code>
static void initializeNative(JNIEnv *env, jobject object) {
if(btIf)
return;
if ( (btIf = getBluetoothInterface()) == NULL) {
error("Bluetooth module is not loaded");
return;
}
...
if ( (sGattIf = (btgatt_interface_t *)
btIf->get_profile_interface(BT_PROFILE_GATT_ID)) == NULL) {
error("Failed to get Bluetooth GATT Interface");
return;
}
...
}</code>
注意這里首先通過 getBluetoothInterface() 獲得整個底層的藍牙接口馒过。我們重點來關(guān)注以下 sGattIf 是怎么來的? 看到這里調(diào)用了 btIf->get_profile_interface(BT_PROFILE_GATT_ID)) 來獲取 sGattIf 實例酗钞。

現(xiàn)在來看 btgatt_interface_t 是在哪里定義的腹忽,我們看一下這個文件 include 的頭文件中,最有可能就是在 #include "hardware/bt_gatt.h"砚作,這就是 HAL 接口定義的地方窘奏。

硬件抽象層 HAL
HAL 頭文件都放在 hardware/libhardware/include/hardware/,我們在這里找到了 bt_gatt.h葫录,并找到了 btgatt_interface_t 的定義如下:
<code>
/** Represents the standard Bluetooth GATT interface. /
typedef struct {
/
* Set to sizeof(btgatt_interface_t) */
size_t size;

/**
 * Initializes the interface and provides callback routines
 */
bt_status_t (*init)( const btgatt_callbacks_t* callbacks );

/** Closes the interface */
void (*cleanup)( void );

/** Pointer to the GATT client interface methods.*/
const btgatt_client_interface_t* client;

/** Pointer to the GATT server interface methods.*/
const btgatt_server_interface_t* server;

} btgatt_interface_t;</code>
可以看到 btgatt_interface_t 有一個成員 client着裹,類型是 btgatt_client_interface_t。 我們再來看 btgatt_client_interface_t 的定義米同,在 bt_gatt_client.h 中:
<code>
/** Represents the standard BT-GATT client interface. */

typedef struct {
/** Registers a GATT client application with the stack /
bt_status_t (
register_client)( bt_uuid_t *uuid );

/** Unregister a client application from the stack */
bt_status_t (*unregister_client)(int client_if );

/** Create a connection to a remote LE or dual-mode device */
bt_status_t (*connect)( int client_if, const bt_bdaddr_t *bd_addr,
                     bool is_direct, int transport );

/** Disconnect a remote device or cancel a pending connection */
bt_status_t (*disconnect)( int client_if, const bt_bdaddr_t *bd_addr,
                int conn_id);

    ...

} btgatt_client_interface_t;</code>
在這里我們看到了 register_client 這個函數(shù)指針骇扇。這個結(jié)構(gòu)體的定義很長摔竿,我這里只截取了本文相關(guān)的內(nèi)容。這里定義了所有 GATT 客戶端操作相關(guān)的接口少孝,例如連接继低、掃描、獲取 Gatt Service稍走、讀寫 Gatt characteristic/descriptor 等等袁翁。 但是這個結(jié)構(gòu)體的具體實現(xiàn)在什么地方呢?我們的直覺應(yīng)該就在 HAL 的實現(xiàn) BlueDroid 模塊了钱磅。

藍牙協(xié)議棧:BlueDroid
BlueDroid 的代碼在 external/bluetooth/bluedroid梦裂。我們在這個目錄下 grep 一下:
<code>
$ cd external/bluetooth/bluedroid
$ grep -nr 'btgatt_interface_t' .</code>
我們很容易找到 btgatt_interface_t 的實現(xiàn):
<code>
// btif/src/btif_gatt.c
static const btgatt_interface_t btgattInterface = {
sizeof(btgattInterface),

btif_gatt_init,
btif_gatt_cleanup,

&btgattClientInterface,
&btgattServerInterface,

};</code>
然后在 btif/src/btif_gatt_client.c,找到 btgattClientInterface 具體的實現(xiàn)如下:
<code>
const btgatt_client_interface_t btgattClientInterface = {
btif_gattc_register_app,
btif_gattc_unregister_app,
btif_gattc_open,
btif_gattc_close,
...
}</code>
看到這里定義了一個 btgatt_client_interface_t 的實例 btgattClientInterface盖淡,內(nèi)部都是函數(shù)指針年柠,所有的函數(shù)的實現(xiàn)都這這個文件中能找到。其實這個變量就是前面的代碼中提到的 sGattIf->client褪迟,我們下面來看看這是是怎么關(guān)聯(lián)上的冗恨。 在 btif/src/btif_gatt.c 中又如下定義:
<code>
extern btgatt_client_interface_t btgattClientInterface;
static const btgatt_interface_t btgattInterface = {
sizeof(btgattInterface),

btif_gatt_init,
btif_gatt_cleanup,

&btgattClientInterface,
&btgattServerInterface,

};

const btgatt_interface_t *btif_gatt_get_interface()
{
return &btgattInterface;
}</code>
從代碼中可以看出,btgattClientInterface 賦值給了 btgattInterface 的 client 成員(還記得前面的 btgatt_interface_t 的定義吧)味赃。難道這里的 btgattInterface 就是 JNI 中的 sGattIf 嗎掀抹? 確實是這樣的,還記的前面的說的 sGattIf 的初始化心俗,調(diào)用了如下接口:

sGattIf = (btgatt_interface_t )btIf->get_profile_interface(BT_PROFILE_GATT_ID)
我們來看一下 get_profile_interface() 的定義傲武,在 btif/src/bluetooth.c 中:
<code>
static const void
get_profile_interface (const char *profile_id)
{
...
if (is_profile(profile_id, BT_PROFILE_GATT_ID))
return btif_gatt_get_interface();
...
}</code>
看到這里調(diào)用了 btif_gatt_get_interface(),實際上就是把 btgattInterface 返回賦值給了 sGattIf城榛。

到這里揪利,變量之間的相互關(guān)系和初始化就都完全清楚了。還是不要忘記我們的目標:連接 BLE 設(shè)備狠持。前面我們分析到了疟位,發(fā)起 BLE 設(shè)備連接,首先是向底層協(xié)議棧注冊客戶端喘垂,也就是調(diào)用了 sGattIf->client->register_client(&uuid);甜刻。 現(xiàn)在我們知道了 register_client() 的實現(xiàn)就在 btif/src/btif_gatt_client.c,對應(yīng)的實現(xiàn)如下:
<code>
static bt_status_t btif_gattc_register_app(bt_uuid_t uuid)
{
CHECK_BTGATT_INIT();
btif_gattc_cb_t btif_cb;
memcpy(&btif_cb.uuid, uuid, sizeof(bt_uuid_t));
return btif_transfer_context(btgattc_handle_event, BTIF_GATTC_REGISTER_APP,
(char
) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
}</code>
這里的 btif_transfer_context() 是一個工具方法正勒,代碼如下:
<code>
// btif/src/btif_core.c
bt_status_t btif_transfer_context (tBTIF_CBACK p_cback, UINT16 event, char p_params, int param_len, tBTIF_COPY_CBACK *p_copy_cback)
{
tBTIF_CONTEXT_SWITCH_CBACK *p_msg;
BTIF_TRACE_VERBOSE2("btif_transfer_context event %d, len %d", event, param_len);

/* allocate and send message that will be executed in btif context */
if ((p_msg = (tBTIF_CONTEXT_SWITCH_CBACK *) GKI_getbuf(sizeof(tBTIF_CONTEXT_SWITCH_CBACK) + param_len)) != NULL)
{
    // 給 p_msg 賦值
            ...

            // 注意這里
    btif_sendmsg(p_msg);
    return BT_STATUS_SUCCESS;
}
else
{
    /* let caller deal with a failed allocation */
    return BT_STATUS_NOMEM;
}

}</code>
這里面調(diào)用了一個很重要的方法 btif_sendmsg(...)得院,實際上是調(diào)用了 GKI_send_msg(...),接著往下看代碼就會發(fā)現(xiàn)昭齐,實際上就是發(fā)送一個 Event尿招,讓對應(yīng)的 handler 函數(shù)去處理。 據(jù)我的理解,其實就是一個類似于 Android framework 中的 Handler就谜,你可以提交你的 Task 到指定的線程中去運行怪蔑。 這里 btif_transfer_context(...) 的作用就是把代碼運行的上下文(context)轉(zhuǎn)為藍牙協(xié)議棧,這部分的代碼和本文關(guān)系不大丧荐,這里就不深入討論了缆瓣。 這里的 btgattc_handle_event 相當于我們要運行的 Task, 后面就是這個 Task 運行需要的一些參數(shù)。我們接下來看 btgattc_handle_event 的實現(xiàn):
<code>
// btif/src/btif_gatt_client.c
static void btgattc_handle_event(uint16_t event, char* p_param)
{
tBTA_GATT_STATUS status;
tBT_UUID uuid;
...

btif_gattc_cb_t* p_cb = (btif_gattc_cb_t*)p_param;
if (!p_cb) return;
ALOGD("%s: Event %d", __FUNCTION__, event);

switch (event)
{
    case BTIF_GATTC_REGISTER_APP:
                    // UUID 的格式轉(zhuǎn)換
        btif_to_bta_uuid(&uuid, &p_cb->uuid);
        BTA_GATTC_AppRegister(&uuid, bta_gattc_cback);
        break;
            ...
    }

}</code>
可以看到虹统,這個函數(shù)的參數(shù)弓坞,都是我們上面那個 btif_transfer_context(...) 傳遞進來的,這里 event = BTIF_GATTC_REGISTER_APP车荔,看一下對應(yīng)的 switch 分支代碼渡冻。 btif_to_bta_uuid() 非常簡單,這是把 UUID 的轉(zhuǎn)換為 BTA 層 UUID 格式忧便。然后真正做事的是 BTA_GATTC_AppRegister(...):
<code>
// bta/gatt/bta_gattc_api.c
void BTA_GATTC_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTC_CBACK *p_client_cb)
{
tBTA_GATTC_API_REG *p_buf;

    if (bta_sys_is_register(BTA_ID_GATTC) == FALSE)
{
    GKI_sched_lock();
            // 注冊事件處理函數(shù)
    bta_sys_register(BTA_ID_GATTC, &bta_gattc_reg);
    GKI_sched_unlock();
}

if ((p_buf = (tBTA_GATTC_API_REG *) GKI_getbuf(sizeof(tBTA_GATTC_API_REG))) != NULL)
{
    p_buf->hdr.event    = BTA_GATTC_API_REG_EVT;
    if (p_app_uuid != NULL)
        memcpy(&p_buf->app_uuid, p_app_uuid, sizeof(tBT_UUID));
    p_buf->p_cback      = p_client_cb;

    bta_sys_sendmsg(p_buf);
}
return;

}</code>
這里首先判斷 BTA_ID_GATTC 事件處理器是否注冊了族吻,如果沒有注冊就注冊一個 bta_gattc_reg。這里表示 bta_gattc_reg 所有的 GATT 客戶端事件的處理器珠增。 可見超歌,這個 bta_gattc_reg 是非常重要的,我們來看它的定義:
<code>
// bta/sys/bta_sys.h
/* registration structure */
typedef struct
{
// 事件處理函數(shù)
tBTA_SYS_EVT_HDLR *evt_hdlr;
// 關(guān)閉事件處理
tBTA_SYS_DISABLE *disable;
} tBTA_SYS_REG;

// bta/gatt/bta_gattc_api.c
static const tBTA_SYS_REG bta_gattc_reg =
{
bta_gattc_hdl_event,
BTA_GATTC_Disable
};</code>
這是一個結(jié)構(gòu)體蒂教,其定義我也貼在上面了巍举,成員類型都是函數(shù)指針,其函數(shù)的定義我在后面會講凝垛。 我們先往下看懊悯,這里的代碼是不是看起來有些面熟,其實和 btif_transfer_context() 的邏輯非常類似梦皮,這里也是發(fā)送一個 event 給 handler 去處理定枷。 雖然這里調(diào)用的是 bta_sys_sendmsg(...),實際上它的實現(xiàn)就是調(diào)用 GKI_send_msg(...)届氢。 分析方法類似,我們這里的 event 是 BTA_GATTC_API_REG_EVT覆旭,這個事件的處理函數(shù)就是上面的 bta_gattc_reg 的 bta_gattc_hdl_event:
<code>
// bta/gatt/bta_gattc_main.c
BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg)
{
tBTA_GATTC_CB *p_cb = &bta_gattc_cb;
...
switch (p_msg->event)
{
case BTA_GATTC_API_REG_EVT:
bta_gattc_register(p_cb, (tBTA_GATTC_DATA *) p_msg);
break;
...
}
...
}</code>
到這里退子,我們終于看到了最終的真正執(zhí)行注冊客戶端的函數(shù) bta_gattc_register(...) 了:
<code>
// bta/gatt/bta_gattc_act.c
void bta_gattc_register(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_data)
{
tBTA_GATTC cb_data;
UINT8 i;
tBT_UUID *p_app_uuid = &p_data->api_reg.app_uuid;
tBTA_GATTC_INT_START_IF *p_buf;
tBTA_GATT_STATUS status = BTA_GATT_NO_RESOURCES;

APPL_TRACE_DEBUG1("bta_gattc_register state %d",p_cb->state);
memset(&cb_data, 0, sizeof(cb_data));
cb_data.reg_oper.status = BTA_GATT_NO_RESOURCES;

 // 檢查 GATTC 模塊是否開啟;如果沒有型将,就開啟
 if (p_cb->state == BTA_GATTC_STATE_DISABLED)
 {
     bta_gattc_enable (p_cb);
 }

    // 這里遍歷客戶端列表
for (i = 0; i < BTA_GATTC_CL_MAX; i ++)
{
            // 檢查是否被占用
    if (!p_cb->cl_rcb[i].in_use)
    {
                    // 如果沒有被占用寂祥,就向 GATT 協(xié)議棧注冊,并獲得新的 client_if
        if ((p_app_uuid == NULL) || (p_cb->cl_rcb[i].client_if = GATT_Register(p_app_uuid, &bta_gattc_cl_cback)) == 0)
        {
            APPL_TRACE_ERROR0("Register with GATT stack failed.");
            status = BTA_GATT_ERROR;
        }
        else
        {
                            // GATT協(xié)議棧注冊成功七兜,就在 BTA 層也中保存下來丸凭,完成注冊
            p_cb->cl_rcb[i].in_use = TRUE;
            p_cb->cl_rcb[i].p_cback = p_data->api_reg.p_cback;
            memcpy(&p_cb->cl_rcb[i].app_uuid, p_app_uuid, sizeof(tBT_UUID));

            /* BTA use the same client interface as BTE GATT statck */
            cb_data.reg_oper.client_if = p_cb->cl_rcb[i].client_if;

            if ((p_buf = (tBTA_GATTC_INT_START_IF *) GKI_getbuf(sizeof(tBTA_GATTC_INT_START_IF))) != NULL)
            {
                p_buf->hdr.event    = BTA_GATTC_INT_START_IF_EVT;
                p_buf->client_if    = p_cb->cl_rcb[i].client_if;
                                    // 注冊成功,發(fā)送 BTA_GATTC_INT_START_IF_EVT 事件
                bta_sys_sendmsg(p_buf);
                status = BTA_GATT_OK;
            }
            else
            {
                GATT_Deregister(p_cb->cl_rcb[i].client_if);

                status = BTA_GATT_NO_RESOURCES;
                memset( &p_cb->cl_rcb[i], 0 , sizeof(tBTA_GATTC_RCB));
            }
            break;
        }
    }
}

    /* callback with register event */
if (p_data->api_reg.p_cback)
{
    if (p_app_uuid != NULL)
        memcpy(&(cb_data.reg_oper.app_uuid),p_app_uuid,sizeof(tBT_UUID));

    cb_data.reg_oper.status = status;
            // 通過發(fā)送 BTA_GATTC_REG_EVT 事件,把注冊結(jié)果回調(diào)給上層
    (*p_data->api_reg.p_cback)(BTA_GATTC_REG_EVT,  (tBTA_GATTC *)&cb_data);
}  

}</code>
主要流程都在代碼的注釋中解釋了惜犀,遍歷客戶端列表铛碑,向 GATT statck 注冊客戶端,注冊成功后發(fā)送 BTA_GATTC_INT_START_IF_EVT 事件虽界。 因為這里和前面一樣汽烦,同樣是調(diào)用了 bta_sys_sendmsg(...),所以處理函數(shù)是 bta_gattc_hdl_event(...)莉御,我們再來看這個分支:
<code>
// bta/gatt/bta_gattc_main.c
BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg)
{
tBTA_GATTC_CB *p_cb = &bta_gattc_cb;
...
switch (p_msg->event)
{
...
case BTA_GATTC_INT_START_IF_EVT:
bta_gattc_start_if(p_cb, (tBTA_GATTC_DATA *) p_msg);
break;
...
}
}</code>
這里調(diào)用了 bta_gattc_start_if(...):
<code>
// bta/gatt/bta_gattc_act.c
void bta_gattc_start_if(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
{
if (bta_gattc_cl_get_regcb(p_msg->int_start_if.client_if) !=NULL )
{
GATT_StartIf(p_msg->int_start_if.client_if);
}
else
{
APPL_TRACE_ERROR1("Unable to start app.: Unknown interface =%d",p_msg->int_start_if.client_if );
}
}</code>
最終調(diào)用了 GATT_StartIf(...):
<code>
// stack/gatt/gatt_api.c
void GATT_StartIf (tGATT_IF gatt_if)
{
tGATT_REG *p_reg;
tGATT_TCB *p_tcb;
BD_ADDR bda;
UINT8 start_idx, found_idx;
UINT16 conn_id;

GATT_TRACE_API1 ("GATT_StartIf gatt_if=%d", gatt_if);
if ((p_reg = gatt_get_regcb(gatt_if)) != NULL)
{
    p_reg = &gatt_cb.cl_rcb[gatt_if - 1];
    start_idx = 0;
    while (gatt_find_the_connected_bda(start_idx, bda, &found_idx))
    {
        p_tcb = gatt_find_tcb_by_addr(bda);
        if (p_reg->app_cb.p_conn_cb && p_tcb)
        {
            conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if);
            (*p_reg->app_cb.p_conn_cb)(gatt_if, bda, conn_id, TRUE, 0);
        }
        start_idx = ++found_idx;
    }
}

}</code>
這個函數(shù)的主要作用是撇吞,調(diào)用剛注冊的客戶端 gatt_if 的連接回調(diào)函數(shù),上報所有的設(shè)備的連接狀態(tài)礁叔。

我們接著分析 bta_gattc_register(...) 函數(shù)牍颈,重點是這一行:

(p_data->api_reg.p_cback)(BTA_GATTC_REG_EVT, (tBTA_GATTC )&cb_data);
這里是看起來調(diào)用了一個回調(diào)函數(shù),這里的 (
p_data->api_reg.p_cback) 是誰琅关,我們這里回溯以下煮岁。 回到 bta_gattc_hdl_event(...),這個 p_data 是這里傳進來的 tBTA_GATTC_DATA 類型的數(shù)據(jù)死姚,它是一個聯(lián)合體(union):
<code>
// bta/gatt/bta_gattc_int.h
typedef union
{
BT_HDR hdr;
tBTA_GATTC_API_REG api_reg;
...
} tBTA_GATTC_DATA;</code>
其中 (p_data->api_reg) 成員是一個 tBTA_GATTC_API_REG人乓,這個類型我們在 BTA_GATTC_AppRegister(...) 函數(shù)中見過。 其實這個值就是我們在這里創(chuàng)建的都毒,我們看一下 p_cback 是誰色罚,我們在往回找到 btgattc_handle_event(...) 函數(shù),發(fā)現(xiàn)就是 bta_gattc_cback:其定義如下:
</code>
// btif/src/btif_gatt_client.c
static void bta_gattc_cback(tBTA_GATTC_EVT event, tBTA_GATTC p_data)
{
bt_status_t status = btif_transfer_context(btif_gattc_upstreams_evt,
(uint16_t) event, (void
)p_data, sizeof(tBTA_GATTC), btapp_gattc_req_data);
ASSERTC(status == BT_STATUS_SUCCESS, "Context transfer failed!", status);
}</code>
這里又是一個通過 "handler" 機制實現(xiàn)運行上下文的切換账劲,這里來看一下事件處理函數(shù) btif_gattc_upstreams_evt:
</code>
static void btif_gattc_upstreams_evt(uint16_t event, char
p_param)
{
ALOGD("%s: Event %d", FUNCTION, event);

tBTA_GATTC *p_data = (tBTA_GATTC*)p_param;
switch (event)
{
    case BTA_GATTC_REG_EVT:
    {
        bt_uuid_t app_uuid;
        bta_to_btif_uuid(&app_uuid, &p_data->reg_oper.app_uuid);
        HAL_CBACK(bt_gatt_callbacks, client->register_client_cb
            , p_data->reg_oper.status
            , p_data->reg_oper.client_if
            , &app_uuid
        );
        break;
    }
            ...
    }
    btapp_gattc_free_req_data(event, p_data);

}</code>
因為上面?zhèn)鬟f進來的 event 是 BTA_GATTC_REG_EVT戳护,我們就來看這個 event 的處理代碼。 bta_to_btif_uuid() 和上面的 btif_to_bta_uuid() 相反瀑焦,把 BTA 的 UUID 轉(zhuǎn)換為 BTIF 的格式腌且。 然后,一個宏 HAL_CBACK榛瓮,其定義如下:
<code>

define HAL_CBACK(P_CB, P_CBACK, ...)\

if (P_CB && P_CB->P_CBACK) {            \
    BTIF_TRACE_API2("HAL %s->%s", #P_CB, #P_CBACK); \
    P_CB->P_CBACK(__VA_ARGS__);         \
}                                       \
else {                                  \
    ASSERTC(0, "Callback is NULL", 0);  \
}</code>

這里展開一下铺董,并替換其中實際的變量:

bt_gatt_callbacks->client->register_client_cb(p_data->reg_oper.status, p_data->reg_oper.client_if, &app_uuid);
可以看到這里其實就是一個執(zhí)行回調(diào)函數(shù)的宏。

現(xiàn)在問題是 bt_gatt_callbacks 是從誰禀晓?我們可以看到是在這里初始化的:

static bt_status_t btif_gatt_init( const btgatt_callbacks_t* callbacks )
{
bt_gatt_callbacks = callbacks;

return BT_STATUS_SUCCESS;

}
也就是初始化的時候精续,通過傳遞進來的。在分析 BlueDroid 的最開始我們已經(jīng)看到了 btgattInterface 的初始化了粹懒,這里的 btif_gatt_init 函數(shù)就賦值給了它的 init 成員重付。 通過我們前面的分析,btgattInterface 實際上就是 JNI 層的 sGattIf 對象凫乖。我們回到 JNI 層的代碼:
<code>
// /packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
static void initializeNative(JNIEnv *env, jobject object) {
...
// 這里我們前面分析過
if ( (sGattIf = (btgatt_interface_t *)
btIf->get_profile_interface(BT_PROFILE_GATT_ID)) == NULL) {
error("Failed to get Bluetooth GATT Interface");
return;
}

    // 注意這里調(diào)用初始化方法 init(...)
bt_status_t status;
if ( (status = sGattIf->init(&sGattCallbacks)) != BT_STATUS_SUCCESS) {
    error("Failed to initialize Bluetooth GATT, status: %d", status);
    sGattIf = NULL;
    return;
}

mCallbacksObj = env->NewGlobalRef(object);

}</code>
這里調(diào)用了 sGattIf->init(...) 方法确垫,實際上就是前面的 btif_gatt_init(...) 函數(shù)弓颈。看到這里傳入的 sGattCallbacks 就是我們在 BlueDroid 層尋找的 bt_gatt_callbacks删掀。 我們來看 sGattCallbacks 的定義:

// /packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
static const btgatt_callbacks_t sGattCallbacks = {
sizeof(btgatt_callbacks_t),
&sGattClientCallbacks,
&sGattServerCallbacks
};
通過前面的分析翔冀,我們應(yīng)該能很快知道 btgatt_callbacks_t 應(yīng)該在 HAL 中定義的,然后 sGattClientCallbacks 就是對應(yīng)的 client 成員爬迟。 在來看 sGattClientCallbacks 的定義:

// /packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
static const btgatt_client_callbacks_t sGattClientCallbacks = {
btgattc_register_app_cb,
...
};
這里的 btgattc_register_app_cb 對應(yīng)的就是 btgatt_client_callbacks_t 的 register_client_cb 成員橘蜜。所以我們再來看一下那個展開的宏,這里再貼一下:

bt_gatt_callbacks->client->register_client_cb(p_data->reg_oper.status, p_data->reg_oper.client_if, &app_uuid);
實際上就是調(diào)用了 JNI 中定義的 btgattc_register_app_cb 函數(shù):

// /packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
void btgattc_register_app_cb(int status, int clientIf, bt_uuid_t *app_uuid)
{
CHECK_CALLBACK_ENV
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientRegistered, status,
clientIf, UUID_PARAMS(app_uuid));
checkAndClearExceptionFromCallback(sCallbackEnv, FUNCTION);
}
這里通過 JNI 調(diào)用了函數(shù) id 為 method_onClientRegistered 函數(shù)付呕,

// /packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
static void classInitNative(JNIEnv* env, jclass clazz) {
// Client callbacks
method_onClientRegistered = env->GetMethodID(clazz, "onClientRegistered", "(IIJJ)V");
...
}
這里就對應(yīng)了 Java class 中的 onClientRegistered(...) 方法计福,這里 clazz 是誰呢?

// /packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void *) classInitNative},
...
}
我們在 GattService.java 中看到:

// /packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java
public class GattService extends ProfileService {
static {
classInitNative();
}
}
可見徽职,上面的 clazz 就是 GattService象颖,onClientRegistered 就是 GattService 的成員方法。我們在 GattService 類中找到其實現(xiàn)如下:

// /packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java
void onClientRegistered(int status, int clientIf, long uuidLsb, long uuidMsb)
throws RemoteException {
UUID uuid = new UUID(uuidMsb, uuidLsb);
if (DBG) Log.d(TAG, "onClientRegistered() - UUID=" + uuid + ", clientIf=" + clientIf);
ClientMap.App app = mClientMap.getByUuid(uuid);
if (app != null) {
app.id = clientIf;
app.linkToDeath(new ClientDeathRecipient(clientIf));
app.callback.onClientRegistered(status, clientIf);
}
}
哈哈姆钉,終于回到我們熟悉的 Java 層代碼说订。還記得我們前面的在分析調(diào)用 registerClient(...) 的時候,把上層客戶端與 uuid 對應(yīng)起來潮瓶,保存在 mClientMap 中陶冷。 這里通過 uuid 找到對應(yīng)的客戶端App,然后調(diào)用對應(yīng)的回調(diào)函數(shù) app.callback.onClientRegistered(status, clientIf);毯辅。 如果你還記得前面的分析的話埂伦,應(yīng)該知道這個 callback 就是 BluetoothGatt 在調(diào)用 registerApp(...) 的時候傳遞的 mBluetoothGattCallback,所以這里就調(diào)用了 mBluetoothGattCallback.onClientRegistered(...) 方法思恐。 這個 onClientRegistered() 回調(diào)我們在之前就提到過沾谜,如果注冊客戶端完成,就會回調(diào)這里胀莹。如果成功基跑,就會發(fā)起真正的連接請求(見第 1 節(jié))。到這里描焰,其實就回調(diào)了 Android framework 層了媳否。

如果這里注冊客戶端成功了,回調(diào)的 status 就是 GATT_SUCCESS荆秦,在 BluetoothGatt 中就會發(fā)起連接請求 mService.clientConnect(mClientIf, mDevice.getAddress(), !mAutoConnect);逆日。具體的流程和上面整個分析類似。 有了上面的整個分析經(jīng)驗萄凤,這次應(yīng)該就駕輕就熟了。所以我這里也不再往下繼續(xù)說了搪哪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末靡努,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惑朦,老刑警劉巖兽泄,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異漾月,居然都是意外死亡病梢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門梁肿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜓陌,“玉大人,你說我怎么就攤上這事吩蔑∨ト龋” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵烛芬,是天一觀的道長隧期。 經(jīng)常有香客問我,道長赘娄,這世上最難降的妖魔是什么仆潮? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮遣臼,結(jié)果婚禮上性置,老公的妹妹穿的比我還像新娘。我一直安慰自己暑诸,他們只是感情好蚌讼,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著个榕,像睡著了一般篡石。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上西采,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天凰萨,我揣著相機與錄音,去河邊找鬼械馆。 笑死胖眷,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的霹崎。 我是一名探鬼主播珊搀,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼尾菇!你這毒婦竟也來了境析?” 一聲冷哼從身側(cè)響起囚枪,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎劳淆,沒想到半個月后链沼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡沛鸵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年括勺,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曲掰。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡疾捍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜈缤,到底是詐尸還是另有隱情拾氓,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布底哥,位于F島的核電站咙鞍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏趾徽。R本人自食惡果不足惜续滋,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望孵奶。 院中可真熱鬧疲酌,春花似錦、人聲如沸了袁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽载绿。三九已至粥诫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崭庸,已是汗流浹背怀浆。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留怕享,地道東北人执赡。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像函筋,于是被迫代替她去往敵國和親沙合。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容