[Android源碼解析]Eventloop在jni層的分析

3 eventloopjni層的詳細分析

         Eventloop在整個bluetoothjni層和bluez之間的交互,以及jniframework層之間的交互過程中有着舉足輕重的作用。所以,本文仍然需要花費一定的筆墨來分析它,當然由於它更像是一個橋樑,我們的分析有可能就不是那麼的深入了,會從比較宏觀地角度來看待這個東西的作用。

         當然,一切的一切還是要從源碼來說:

static jboolean startEventLoopNative(JNIEnv *env, jobject object) {
    jboolean result = JNI_FALSE;
#ifdef HAVE_BLUETOOTH
    event_loop_native_data_t *nat = get_native_data(env, object);

    pthread_mutex_lock(&(nat->thread_mutex));
	//這個是用來判斷eventloop是否啓動的
    nat->running = false;

    if (nat->pollData) {
        LOGW("trying to start EventLoop a second time!");
        pthread_mutex_unlock( &(nat->thread_mutex) );
        return JNI_FALSE;
    }
	//申請pollfa
    nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) *
            DEFAULT_INITIAL_POLLFD_COUNT);
    if (!nat->pollData) {
        LOGE("out of memory error starting EventLoop!");
        goto done;
    }
	//申請dbus的watch data
    nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) *
            DEFAULT_INITIAL_POLLFD_COUNT);
    if (!nat->watchData) {
        LOGE("out of memory error starting EventLoop!");
        goto done;
    }
	//初始化爲0
    memset(nat->pollData, 0, sizeof(struct pollfd) *
            DEFAULT_INITIAL_POLLFD_COUNT);
    memset(nat->watchData, 0, sizeof(DBusWatch *) *
            DEFAULT_INITIAL_POLLFD_COUNT);
	//datasize和member count的初始化
    nat->pollDataSize = DEFAULT_INITIAL_POLLFD_COUNT;
    nat->pollMemberCount = 1;
	//申請socket 對,保存到controlFdR
    if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &(nat->controlFdR))) {
        LOGE("Error getting BT control socket");
        goto done;
    }
	//data 0是其中一個,可以猜到了是通過socket在內部通信的
    nat->pollData[0].fd = nat->controlFdR;
    nat->pollData[0].events = POLLIN;

    env->GetJavaVM( &(nat->vm) );
    nat->envVer = env->GetVersion();

    nat->me = env->NewGlobalRef(object);
	//建立eventloop,詳細分析見3.1
    if (setUpEventLoop(nat) != JNI_TRUE) {
        LOGE("failure setting up Event Loop!");
        goto done;
    }
	//建立eventloopmain的thread,他肯定就是在不停地運行了,詳細分析見3.2
    pthread_create(&(nat->thread), NULL, eventLoopMain, nat);
    result = JNI_TRUE;

done:
    if (JNI_FALSE == result) {
        if (nat->controlFdW) {
            close(nat->controlFdW);
            nat->controlFdW = 0;
        }
        if (nat->controlFdR) {
            close(nat->controlFdR);
            nat->controlFdR = 0;
        }
        if (nat->me) env->DeleteGlobalRef(nat->me);
        nat->me = NULL;
        if (nat->pollData) free(nat->pollData);
        nat->pollData = NULL;
        if (nat->watchData) free(nat->watchData);
        nat->watchData = NULL;
        nat->pollDataSize = 0;
        nat->pollMemberCount = 0;
    }

    pthread_mutex_unlock(&(nat->thread_mutex));
#endif // HAVE_BLUETOOTH
    return result;
}


 

3.1 eventloop的建立

static jboolean setUpEventLoop(native_data_t *nat) {

    LOGV("%s", __FUNCTION__);

 

    if (nat != NULL && nat->conn != NULL) {

        dbus_threads_init_default();

        DBusError err;

        dbus_error_init(&err);

 

        const char *agent_path = "/android/bluetooth/agent";

        const char *capabilities = "DisplayYesNo";

                   //主要就是調用bluez的registeragent函數,見3.1.1

        if (register_agent(nat, agent_path, capabilities) < 0) {

            dbus_connection_unregister_object_path (nat->conn, agent_path);

            return JNI_FALSE;

        }

                   //這裏是加入一個對event的過濾,所以我們上文中的propertychang之類的都是在這裏面處理的,我們在後面對對應的event分析處理的時候再分析

        // Add a filter for all incoming messages

        if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){

            return JNI_FALSE;

        }

                   //下面就是我們會watch這些interface。

        // Set which messages will be processed by this dbus connection

        dbus_bus_add_match(nat->conn,

                "type='signal',interface='org.freedesktop.DBus'",

                &err);

        if (dbus_error_is_set(&err)) {

            LOG_AND_FREE_DBUS_ERROR(&err);

            return JNI_FALSE;

……

}

 


 

3.1.1註冊agent

         這個又回到了bluez,在adapter.c

static DBusMessage *register_agent(DBusConnection *conn, DBusMessage *msg,
								void *data)
{
	const char *path, *name, *capability;
	struct agent *agent;
	struct btd_adapter *adapter = data;
	uint8_t cap;
	//得到capability的值
	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
			DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID))
		return NULL;
	//看是否有了agent
	if (adapter->agent)
		return btd_error_already_exists(msg);
	//解析capability,我們的輸入時display yes no,所以就是有顯示,有輸入輸出了
	cap = parse_io_capability(capability);
	if (cap == IO_CAPABILITY_INVALID)
		return btd_error_invalid_args(msg);

	name = dbus_message_get_sender(msg);
	//建agent結構體
	agent = agent_create(adapter, name, path, cap,
				(agent_remove_cb) agent_removed, adapter);
	if (!agent)
		return btd_error_failed(msg, "Failed to create a new agent");
	//agent和對應的adapter關聯起來
	adapter->agent = agent;

	DBG("Agent registered for hci%d at %s:%s", adapter->dev_id, name,
			path);
	//設置io capability,就是設置dev的io_capability
	adapter_ops->set_io_capability(adapter->dev_id, cap);

	return dbus_message_new_method_return(msg);
}


 

所以,總的來說,還是蠻簡單的,就是建了一個agent的結構體,然後把它和對應的adapter關聯起來,同時設置了devio capability

 

3.2 eventLoopMain分析

這個函數就是eventloop的主函數了,我們可以猜到,他會一直在運行。

static void *eventLoopMain(void *ptr) {
    native_data_t *nat = (native_data_t *)ptr;
    JNIEnv *env;

    JavaVMAttachArgs args;
    char name[] = "BT EventLoop";
    args.version = nat->envVer;
    args.name = name;
    args.group = NULL;

    nat->vm->AttachCurrentThread(&env, &args);
	//設置dbus的watch函數,就是監聽了和wakeup,這裏其實是dbus的一些通信機制,
	//我們可以不瞭解,只要知道最終我們是使用event_filter對event進行處理的就可以了
    dbus_connection_set_watch_functions(nat->conn, dbusAddWatch,
            dbusRemoveWatch, dbusToggleWatch, ptr, NULL);
    dbus_connection_set_wakeup_main_function(nat->conn, dbusWakeup, ptr, NULL);
	//這裏就表示eventloop ok了
    nat->running = true;
	//下面這個while大概的意思就是通過一個本地的socket進行讀寫,然後到最後進行處理
while (1) {
……
}


 

這個函數更多的是涉及機制的問題,所以我們並沒有詳細的解析。

 

至此,eventloop的分析就全部完成了,他只是一個工具,下面的章節中,我們會詳細分析這個工具給我們帶來的便利。

4、其它的一些操作

除了上面涉及到的一系列的操作,在藍牙打開的過程中還有一些jni層的操作,首先一個函數就是setBluetoothTetheringNative,他的主要作用就是使能或者說註冊pan相關的操作,具體的分析如下:

static jboolean setBluetoothTetheringNative(JNIEnv *env, jobject object, jboolean value,

                                            jstring src_role, jstring bridge) {

    LOGV("%s", __FUNCTION__);

#ifdef HAVE_BLUETOOTH

    native_data_t *nat = get_native_data(env, object);

    if (nat) {

        DBusMessage *reply;

        const char *c_role = env->GetStringUTFChars(src_role, NULL);

        const char *c_bridge = env->GetStringUTFChars(bridge, NULL);

        if (value) {

            LOGE("setBluetoothTetheringNative true");

//設置爲true,所以,就是register了,是networkserver的interface

            reply = dbus_func_args(env, nat->conn,

                                  get_adapter_path(env, object),

                                  DBUS_NETWORKSERVER_IFACE,

                                  "Register",

                                  DBUS_TYPE_STRING, &c_role,

                                  DBUS_TYPE_STRING, &c_bridge,

                                  DBUS_TYPE_INVALID);

        } else {

            LOGE("setBluetoothTetheringNative false");

            reply = dbus_func_args(env, nat->conn,

                                  get_adapter_path(env, object),

                                  DBUS_NETWORKSERVER_IFACE,

                                  "Unregister",

                                  DBUS_TYPE_STRING, &c_role,

                                  DBUS_TYPE_INVALID);

        }

        env->ReleaseStringUTFChars(src_role, c_role);

        env->ReleaseStringUTFChars(bridge, c_bridge);

        return reply ? JNI_TRUE : JNI_FALSE;

    }

#endif

    return JNI_FALSE;

}

在jni中,他的接口是:

static GDBusMethodTable server_methods[] = {

          { "Register",    "ss",         "",    register_server                 },

……}

所以,最終會調用register_server函數來實現:

static DBusMessage *register_server(DBusConnection *conn,

                                      DBusMessage *msg, void *data)

{

          struct network_server *ns = data;

          DBusMessage *reply;

          const char *uuid, *bridge;

          //得到參數

          if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &uuid,

                                      DBUS_TYPE_STRING, &bridge, DBUS_TYPE_INVALID))

                   return NULL;

          //看uuid是否是nap

          if (g_strcmp0(uuid, "nap"))

                   return btd_error_failed(msg, "Invalid UUID");

          //看recored_id是否已經存在

          if (ns->record_id)

                   return btd_error_already_exists(msg);

 

          reply = dbus_message_new_method_return(msg);

          if (!reply)

                   return NULL;

          //若是沒有,就註冊server record

          ns->record_id = register_server_record(ns);

          if (!ns->record_id)

                   return btd_error_failed(msg, "SDP record registration failed");

 

          g_free(ns->bridge);

          ns->bridge = g_strdup(bridge);

          //dbus disconnect的時候有的一個監聽

          ns->watch_id = g_dbus_add_disconnect_watch(conn,

                                               dbus_message_get_sender(msg),

                                               server_disconnect, ns, NULL);

 

          return reply;

}


 

所以,總的來說還是蠻簡單,就不深入詳細分析了。

至此,jni中所涉及的所有部分就全部分析完成了哦。

 

附1:關於2L提問的回答


所謂的藍牙上網共享,藍牙設備會扮演三個角色,他們分別是:

1、NAP:這個是用來向連結的藍牙設備提供網絡數據包的,它需要有額外的上網能力。也就是2L提到的設備1(通過wifi上網)

2、GN:這個是用來轉發從連接的藍牙設備收到的網絡數據包。它不需要額外的上網能力。

3、PANU:就是專門用來接收藍牙設備發送過來的網絡數據包,也就是可以通過連接別的設備來進行上網。


目前android的代碼只支持其中的兩個角色就是NAP和PANU,代碼如下:

    /**
     * The local device is acting as a Network Access Point.
     */
    public static final int LOCAL_NAP_ROLE = 1;

    /**
     * The local device is acting as a PAN User.
     */
    public static final int LOCAL_PANU_ROLE = 2;

沒有支持GN的角色,所以再你第三個設備連接之後會出現問題。


若您覺得該文章對您有幫助,請在下面用鼠標輕輕按一下“頂”,哈哈~~·

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章