2 ** Copyright 2008, The Android Open Source Project
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
8 ** http://www.apache.org/licenses/LICENSE-2.0
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
17 #define LOG_TAG "BluetoothEventLoop.cpp"
19 #include "android_bluetooth_common.h"
20 #include "android_runtime/AndroidRuntime.h"
21 #include "cutils/sockets.h"
24 #include "utils/Log.h"
25 #include "utils/misc.h"
34 #include <dbus/dbus.h>
39 #define CREATE_DEVICE_ALREADY_EXISTS 1
40 #define CREATE_DEVICE_SUCCESS 0
41 #define CREATE_DEVICE_FAILED -1
44 static jfieldID field_mNativeData;
46 static jmethodID method_onPropertyChanged;
47 static jmethodID method_onDevicePropertyChanged;
48 static jmethodID method_onDeviceFound;
49 static jmethodID method_onDeviceDisappeared;
50 static jmethodID method_onDeviceCreated;
51 static jmethodID method_onDeviceRemoved;
52 static jmethodID method_onDeviceDisconnectRequested;
54 static jmethodID method_onCreatePairedDeviceResult;
55 static jmethodID method_onCreateDeviceResult;
56 static jmethodID method_onDiscoverServicesResult;
57 static jmethodID method_onGetDeviceServiceChannelResult;
59 static jmethodID method_onRequestPinCode;
60 static jmethodID method_onRequestPasskey;
61 static jmethodID method_onRequestPasskeyConfirmation;
62 static jmethodID method_onRequestPairingConsent;
63 static jmethodID method_onDisplayPasskey;
64 static jmethodID method_onRequestOobData;
65 static jmethodID method_onAgentOutOfBandDataAvailable;
66 static jmethodID method_onAgentAuthorize;
67 static jmethodID method_onAgentCancel;
69 typedef event_loop_native_data_t native_data_t;
71 #define EVENT_LOOP_REFS 10
73 static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
74 return (native_data_t *)(env->GetIntField(object,
78 native_data_t *get_EventLoop_native_data(JNIEnv *env, jobject object) {
79 return get_native_data(env, object);
83 static void classInitNative(JNIEnv* env, jclass clazz) {
87 method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged",
88 "([Ljava/lang/String;)V");
89 method_onDevicePropertyChanged = env->GetMethodID(clazz,
90 "onDevicePropertyChanged",
91 "(Ljava/lang/String;[Ljava/lang/String;)V");
92 method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound",
93 "(Ljava/lang/String;[Ljava/lang/String;)V");
94 method_onDeviceDisappeared = env->GetMethodID(clazz, "onDeviceDisappeared",
95 "(Ljava/lang/String;)V");
96 method_onDeviceCreated = env->GetMethodID(clazz, "onDeviceCreated", "(Ljava/lang/String;)V");
97 method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V");
98 method_onDeviceDisconnectRequested = env->GetMethodID(clazz, "onDeviceDisconnectRequested",
99 "(Ljava/lang/String;)V");
101 method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
102 "(Ljava/lang/String;I)V");
103 method_onCreateDeviceResult = env->GetMethodID(clazz, "onCreateDeviceResult",
104 "(Ljava/lang/String;I)V");
105 method_onDiscoverServicesResult = env->GetMethodID(clazz, "onDiscoverServicesResult",
106 "(Ljava/lang/String;Z)V");
108 method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
109 "(Ljava/lang/String;Ljava/lang/String;)Z");
110 method_onAgentOutOfBandDataAvailable = env->GetMethodID(clazz, "onAgentOutOfBandDataAvailable",
111 "(Ljava/lang/String;)Z");
112 method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
113 method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
114 "(Ljava/lang/String;I)V");
115 method_onRequestPasskey = env->GetMethodID(clazz, "onRequestPasskey",
116 "(Ljava/lang/String;I)V");
117 method_onRequestPasskeyConfirmation = env->GetMethodID(clazz, "onRequestPasskeyConfirmation",
118 "(Ljava/lang/String;II)V");
119 method_onRequestPairingConsent = env->GetMethodID(clazz, "onRequestPairingConsent",
120 "(Ljava/lang/String;I)V");
121 method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey",
122 "(Ljava/lang/String;II)V");
123 method_onRequestOobData = env->GetMethodID(clazz, "onRequestOobData",
124 "(Ljava/lang/String;I)V");
126 field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
130 static void initializeNativeDataNative(JNIEnv* env, jobject object) {
132 #ifdef HAVE_BLUETOOTH
133 native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
135 LOGE("%s: out of memory!", __FUNCTION__);
138 memset(nat, 0, sizeof(native_data_t));
140 pthread_mutex_init(&(nat->thread_mutex), NULL);
142 env->SetIntField(object, field_mNativeData, (jint)nat);
146 dbus_error_init(&err);
147 dbus_threads_init_default();
148 nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
149 if (dbus_error_is_set(&err)) {
150 LOGE("%s: Could not get onto the system bus!", __FUNCTION__);
151 dbus_error_free(&err);
153 dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
158 static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
160 #ifdef HAVE_BLUETOOTH
162 (native_data_t *)env->GetIntField(object, field_mNativeData);
164 pthread_mutex_destroy(&(nat->thread_mutex));
172 #ifdef HAVE_BLUETOOTH
173 static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
175 DBusHandlerResult agent_event_filter(DBusConnection *conn,
178 static int register_agent(native_data_t *nat,
179 const char *agent_path, const char *capabilities);
181 static const DBusObjectPathVTable agent_vtable = {
182 NULL, agent_event_filter, NULL, NULL, NULL, NULL
185 static unsigned int unix_events_to_dbus_flags(short events) {
186 return (events & DBUS_WATCH_READABLE ? POLLIN : 0) |
187 (events & DBUS_WATCH_WRITABLE ? POLLOUT : 0) |
188 (events & DBUS_WATCH_ERROR ? POLLERR : 0) |
189 (events & DBUS_WATCH_HANGUP ? POLLHUP : 0);
192 static short dbus_flags_to_unix_events(unsigned int flags) {
193 return (flags & POLLIN ? DBUS_WATCH_READABLE : 0) |
194 (flags & POLLOUT ? DBUS_WATCH_WRITABLE : 0) |
195 (flags & POLLERR ? DBUS_WATCH_ERROR : 0) |
196 (flags & POLLHUP ? DBUS_WATCH_HANGUP : 0);
199 static jboolean setUpEventLoop(native_data_t *nat) {
202 if (nat != NULL && nat->conn != NULL) {
203 dbus_threads_init_default();
205 dbus_error_init(&err);
207 // Add a filter for all incoming messages
208 if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){
212 // Set which messages will be processed by this dbus connection
213 dbus_bus_add_match(nat->conn,
214 "type='signal',interface='org.freedesktop.DBus'",
216 if (dbus_error_is_set(&err)) {
217 LOG_AND_FREE_DBUS_ERROR(&err);
220 dbus_bus_add_match(nat->conn,
221 "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",
223 if (dbus_error_is_set(&err)) {
224 LOG_AND_FREE_DBUS_ERROR(&err);
227 dbus_bus_add_match(nat->conn,
228 "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'",
230 if (dbus_error_is_set(&err)) {
231 LOG_AND_FREE_DBUS_ERROR(&err);
234 dbus_bus_add_match(nat->conn,
235 "type='signal',interface='org.bluez.AudioSink'",
237 if (dbus_error_is_set(&err)) {
238 LOG_AND_FREE_DBUS_ERROR(&err);
242 const char *agent_path = "/android/bluetooth/agent";
243 const char *capabilities = "DisplayYesNo";
244 if (register_agent(nat, agent_path, capabilities) < 0) {
245 dbus_connection_unregister_object_path (nat->conn, agent_path);
254 const char * get_adapter_path(DBusConnection *conn) {
255 DBusMessage *msg = NULL, *reply = NULL;
257 const char *device_path = NULL;
260 for (attempt = 0; attempt < 1000 && reply == NULL; attempt ++) {
261 msg = dbus_message_new_method_call("org.bluez", "/",
262 "org.bluez.Manager", "DefaultAdapter");
264 LOGE("%s: Can't allocate new method call for get_adapter_path!",
268 dbus_message_append_args(msg, DBUS_TYPE_INVALID);
269 dbus_error_init(&err);
270 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
273 if (dbus_error_is_set(&err)) {
274 if (dbus_error_has_name(&err,
275 "org.freedesktop.DBus.Error.ServiceUnknown")) {
276 // bluetoothd is still down, retry
277 LOG_AND_FREE_DBUS_ERROR(&err);
278 usleep(10000); // 10 ms
281 // Some other error we weren't expecting
282 LOG_AND_FREE_DBUS_ERROR(&err);
288 if (attempt == 1000) {
289 LOGE("Time out while trying to get Adapter path, is bluetoothd up ?");
293 if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
294 &device_path, DBUS_TYPE_INVALID)
296 if (dbus_error_is_set(&err)) {
297 LOG_AND_FREE_DBUS_ERROR(&err);
301 dbus_message_unref(msg);
305 dbus_message_unref(msg);
309 static int register_agent(native_data_t *nat,
310 const char * agent_path, const char * capabilities)
312 DBusMessage *msg, *reply;
316 if (!dbus_connection_register_object_path(nat->conn, agent_path,
317 &agent_vtable, nat)) {
318 LOGE("%s: Can't register object path %s for agent!",
319 __FUNCTION__, agent_path);
323 nat->adapter = get_adapter_path(nat->conn);
324 if (nat->adapter == NULL) {
327 msg = dbus_message_new_method_call("org.bluez", nat->adapter,
328 "org.bluez.Adapter", "RegisterAgent");
330 LOGE("%s: Can't allocate new method call for agent!",
334 dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
335 DBUS_TYPE_STRING, &capabilities,
336 DBUS_TYPE_BOOLEAN, &oob,
339 dbus_error_init(&err);
340 reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
341 dbus_message_unref(msg);
344 LOGE("%s: Can't register agent!", __FUNCTION__);
345 if (dbus_error_is_set(&err)) {
346 LOG_AND_FREE_DBUS_ERROR(&err);
351 dbus_message_unref(reply);
352 dbus_connection_flush(nat->conn);
357 static void tearDownEventLoop(native_data_t *nat) {
359 if (nat != NULL && nat->conn != NULL) {
361 DBusMessage *msg, *reply;
363 dbus_error_init(&err);
364 const char * agent_path = "/android/bluetooth/agent";
366 msg = dbus_message_new_method_call("org.bluez",
371 dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
373 reply = dbus_connection_send_with_reply_and_block(nat->conn,
377 if (dbus_error_is_set(&err)) {
378 LOG_AND_FREE_DBUS_ERROR(&err);
379 dbus_error_free(&err);
382 dbus_message_unref(reply);
384 dbus_message_unref(msg);
386 LOGE("%s: Can't create new method call!", __FUNCTION__);
389 dbus_connection_flush(nat->conn);
390 dbus_connection_unregister_object_path(nat->conn, agent_path);
392 dbus_bus_remove_match(nat->conn,
393 "type='signal',interface='org.bluez.AudioSink'",
395 if (dbus_error_is_set(&err)) {
396 LOG_AND_FREE_DBUS_ERROR(&err);
398 dbus_bus_remove_match(nat->conn,
399 "type='signal',interface='org.bluez.Device'",
401 if (dbus_error_is_set(&err)) {
402 LOG_AND_FREE_DBUS_ERROR(&err);
404 dbus_bus_remove_match(nat->conn,
405 "type='signal',interface='org.bluez.audio.Manager'",
407 if (dbus_error_is_set(&err)) {
408 LOG_AND_FREE_DBUS_ERROR(&err);
410 dbus_bus_remove_match(nat->conn,
411 "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",
413 if (dbus_error_is_set(&err)) {
414 LOG_AND_FREE_DBUS_ERROR(&err);
416 dbus_bus_remove_match(nat->conn,
417 "type='signal',interface='org.freedesktop.DBus'",
419 if (dbus_error_is_set(&err)) {
420 LOG_AND_FREE_DBUS_ERROR(&err);
423 dbus_connection_remove_filter(nat->conn, event_filter, nat);
428 #define EVENT_LOOP_EXIT 1
429 #define EVENT_LOOP_ADD 2
430 #define EVENT_LOOP_REMOVE 3
432 dbus_bool_t dbusAddWatch(DBusWatch *watch, void *data) {
433 native_data_t *nat = (native_data_t *)data;
435 if (dbus_watch_get_enabled(watch)) {
436 // note that we can't just send the watch and inspect it later
437 // because we may get a removeWatch call before this data is reacted
438 // to by our eventloop and remove this watch.. reading the add first
439 // and then inspecting the recently deceased watch would be bad.
440 char control = EVENT_LOOP_ADD;
441 write(nat->controlFdW, &control, sizeof(char));
443 int fd = dbus_watch_get_fd(watch);
444 write(nat->controlFdW, &fd, sizeof(int));
446 unsigned int flags = dbus_watch_get_flags(watch);
447 write(nat->controlFdW, &flags, sizeof(unsigned int));
449 write(nat->controlFdW, &watch, sizeof(DBusWatch*));
454 void dbusRemoveWatch(DBusWatch *watch, void *data) {
455 native_data_t *nat = (native_data_t *)data;
457 char control = EVENT_LOOP_REMOVE;
458 write(nat->controlFdW, &control, sizeof(char));
460 int fd = dbus_watch_get_fd(watch);
461 write(nat->controlFdW, &fd, sizeof(int));
463 unsigned int flags = dbus_watch_get_flags(watch);
464 write(nat->controlFdW, &flags, sizeof(unsigned int));
467 void dbusToggleWatch(DBusWatch *watch, void *data) {
468 if (dbus_watch_get_enabled(watch)) {
469 dbusAddWatch(watch, data);
471 dbusRemoveWatch(watch, data);
475 static void handleWatchAdd(native_data_t *nat) {
480 read(nat->controlFdR, &newFD, sizeof(int));
481 read(nat->controlFdR, &flags, sizeof(unsigned int));
482 read(nat->controlFdR, &watch, sizeof(DBusWatch *));
483 short events = dbus_flags_to_unix_events(flags);
485 for (int y = 0; y<nat->pollMemberCount; y++) {
486 if ((nat->pollData[y].fd == newFD) &&
487 (nat->pollData[y].events == events)) {
488 LOGV("DBusWatch duplicate add");
492 if (nat->pollMemberCount == nat->pollDataSize) {
493 LOGV("Bluetooth EventLoop poll struct growing");
494 struct pollfd *temp = (struct pollfd *)malloc(
495 sizeof(struct pollfd) * (nat->pollMemberCount+1));
499 memcpy(temp, nat->pollData, sizeof(struct pollfd) *
500 nat->pollMemberCount);
502 nat->pollData = temp;
503 DBusWatch **temp2 = (DBusWatch **)malloc(sizeof(DBusWatch *) *
504 (nat->pollMemberCount+1));
508 memcpy(temp2, nat->watchData, sizeof(DBusWatch *) *
509 nat->pollMemberCount);
510 free(nat->watchData);
511 nat->watchData = temp2;
514 nat->pollData[nat->pollMemberCount].fd = newFD;
515 nat->pollData[nat->pollMemberCount].revents = 0;
516 nat->pollData[nat->pollMemberCount].events = events;
517 nat->watchData[nat->pollMemberCount] = watch;
518 nat->pollMemberCount++;
521 static void handleWatchRemove(native_data_t *nat) {
525 read(nat->controlFdR, &removeFD, sizeof(int));
526 read(nat->controlFdR, &flags, sizeof(unsigned int));
527 short events = dbus_flags_to_unix_events(flags);
529 for (int y = 0; y < nat->pollMemberCount; y++) {
530 if ((nat->pollData[y].fd == removeFD) &&
531 (nat->pollData[y].events == events)) {
532 int newCount = --nat->pollMemberCount;
533 // copy the last live member over this one
534 nat->pollData[y].fd = nat->pollData[newCount].fd;
535 nat->pollData[y].events = nat->pollData[newCount].events;
536 nat->pollData[y].revents = nat->pollData[newCount].revents;
537 nat->watchData[y] = nat->watchData[newCount];
541 LOGW("WatchRemove given with unknown watch");
544 static void *eventLoopMain(void *ptr) {
545 native_data_t *nat = (native_data_t *)ptr;
548 JavaVMAttachArgs args;
549 char name[] = "BT EventLoop";
550 args.version = nat->envVer;
554 nat->vm->AttachCurrentThread(&env, &args);
556 dbus_connection_set_watch_functions(nat->conn, dbusAddWatch,
557 dbusRemoveWatch, dbusToggleWatch, ptr, NULL);
562 for (int i = 0; i < nat->pollMemberCount; i++) {
563 if (!nat->pollData[i].revents) {
566 if (nat->pollData[i].fd == nat->controlFdR) {
568 while (recv(nat->controlFdR, &data, sizeof(char), MSG_DONTWAIT)
571 case EVENT_LOOP_EXIT:
573 dbus_connection_set_watch_functions(nat->conn,
574 NULL, NULL, NULL, NULL, NULL);
575 tearDownEventLoop(nat);
576 nat->vm->DetachCurrentThread();
578 int fd = nat->controlFdR;
588 case EVENT_LOOP_REMOVE:
590 handleWatchRemove(nat);
596 short events = nat->pollData[i].revents;
597 unsigned int flags = unix_events_to_dbus_flags(events);
598 dbus_watch_handle(nat->watchData[i], flags);
599 nat->pollData[i].revents = 0;
600 // can only do one - it may have caused a 'remove'
604 while (dbus_connection_dispatch(nat->conn) ==
605 DBUS_DISPATCH_DATA_REMAINS) {
608 poll(nat->pollData, nat->pollMemberCount, -1);
611 #endif // HAVE_BLUETOOTH
613 static jboolean startEventLoopNative(JNIEnv *env, jobject object) {
614 jboolean result = JNI_FALSE;
615 #ifdef HAVE_BLUETOOTH
616 event_loop_native_data_t *nat = get_native_data(env, object);
618 pthread_mutex_lock(&(nat->thread_mutex));
620 nat->running = false;
623 LOGW("trying to start EventLoop a second time!");
624 pthread_mutex_unlock( &(nat->thread_mutex) );
628 nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) *
629 DEFAULT_INITIAL_POLLFD_COUNT);
630 if (!nat->pollData) {
631 LOGE("out of memory error starting EventLoop!");
635 nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) *
636 DEFAULT_INITIAL_POLLFD_COUNT);
637 if (!nat->watchData) {
638 LOGE("out of memory error starting EventLoop!");
642 memset(nat->pollData, 0, sizeof(struct pollfd) *
643 DEFAULT_INITIAL_POLLFD_COUNT);
644 memset(nat->watchData, 0, sizeof(DBusWatch *) *
645 DEFAULT_INITIAL_POLLFD_COUNT);
646 nat->pollDataSize = DEFAULT_INITIAL_POLLFD_COUNT;
647 nat->pollMemberCount = 1;
649 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &(nat->controlFdR))) {
650 LOGE("Error getting BT control socket");
653 nat->pollData[0].fd = nat->controlFdR;
654 nat->pollData[0].events = POLLIN;
656 env->GetJavaVM( &(nat->vm) );
657 nat->envVer = env->GetVersion();
659 nat->me = env->NewGlobalRef(object);
661 if (setUpEventLoop(nat) != JNI_TRUE) {
662 LOGE("failure setting up Event Loop!");
666 pthread_create(&(nat->thread), NULL, eventLoopMain, nat);
670 if (JNI_FALSE == result) {
671 if (nat->controlFdW) {
672 close(nat->controlFdW);
675 if (nat->controlFdR) {
676 close(nat->controlFdR);
679 if (nat->me) env->DeleteGlobalRef(nat->me);
681 if (nat->pollData) free(nat->pollData);
682 nat->pollData = NULL;
683 if (nat->watchData) free(nat->watchData);
684 nat->watchData = NULL;
685 nat->pollDataSize = 0;
686 nat->pollMemberCount = 0;
689 pthread_mutex_unlock(&(nat->thread_mutex));
690 #endif // HAVE_BLUETOOTH
694 static void stopEventLoopNative(JNIEnv *env, jobject object) {
695 #ifdef HAVE_BLUETOOTH
696 native_data_t *nat = get_native_data(env, object);
698 pthread_mutex_lock(&(nat->thread_mutex));
700 char data = EVENT_LOOP_EXIT;
701 ssize_t t = write(nat->controlFdW, &data, sizeof(char));
703 pthread_join(nat->thread, &ret);
705 env->DeleteGlobalRef(nat->me);
708 nat->pollData = NULL;
709 free(nat->watchData);
710 nat->watchData = NULL;
711 nat->pollDataSize = 0;
712 nat->pollMemberCount = 0;
714 int fd = nat->controlFdW;
718 nat->running = false;
719 pthread_mutex_unlock(&(nat->thread_mutex));
720 #endif // HAVE_BLUETOOTH
723 static jboolean isEventLoopRunningNative(JNIEnv *env, jobject object) {
724 jboolean result = JNI_FALSE;
725 #ifdef HAVE_BLUETOOTH
726 native_data_t *nat = get_native_data(env, object);
728 pthread_mutex_lock(&(nat->thread_mutex));
732 pthread_mutex_unlock(&(nat->thread_mutex));
734 #endif // HAVE_BLUETOOTH
738 #ifdef HAVE_BLUETOOTH
739 extern DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env);
741 // Called by dbus during WaitForAndDispatchEventNative()
742 static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
747 DBusHandlerResult ret;
749 dbus_error_init(&err);
751 nat = (native_data_t *)data;
752 nat->vm->GetEnv((void**)&env, nat->envVer);
753 if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) {
754 LOGV("%s: not interested (not a signal).", __FUNCTION__);
755 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
758 LOGE("%s: Received signal %s:%s from %s", __FUNCTION__,
759 dbus_message_get_interface(msg), dbus_message_get_member(msg),
760 dbus_message_get_path(msg));
762 env->PushLocalFrame(EVENT_LOOP_REFS);
763 if (dbus_message_is_signal(msg,
767 DBusMessageIter iter;
768 jobjectArray str_array = NULL;
769 if (dbus_message_iter_init(msg, &iter)) {
770 dbus_message_iter_get_basic(&iter, &c_address);
771 if (dbus_message_iter_next(&iter))
773 parse_remote_device_properties(env, &iter);
775 if (str_array != NULL) {
776 env->CallVoidMethod(nat->me,
777 method_onDeviceFound,
778 env->NewStringUTF(c_address),
781 LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
783 } else if (dbus_message_is_signal(msg,
785 "DeviceDisappeared")) {
787 if (dbus_message_get_args(msg, &err,
788 DBUS_TYPE_STRING, &c_address,
789 DBUS_TYPE_INVALID)) {
790 LOGV("... address = %s", c_address);
791 env->CallVoidMethod(nat->me, method_onDeviceDisappeared,
792 env->NewStringUTF(c_address));
793 } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
795 } else if (dbus_message_is_signal(msg,
799 if (dbus_message_get_args(msg, &err,
800 DBUS_TYPE_OBJECT_PATH, &c_object_path,
801 DBUS_TYPE_INVALID)) {
802 LOGV("... address = %s", c_object_path);
803 env->CallVoidMethod(nat->me,
804 method_onDeviceCreated,
805 env->NewStringUTF(c_object_path));
806 } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
808 } else if (dbus_message_is_signal(msg,
812 if (dbus_message_get_args(msg, &err,
813 DBUS_TYPE_OBJECT_PATH, &c_object_path,
814 DBUS_TYPE_INVALID)) {
815 LOGV("... Object Path = %s", c_object_path);
816 env->CallVoidMethod(nat->me,
817 method_onDeviceRemoved,
818 env->NewStringUTF(c_object_path));
819 } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
821 } else if (dbus_message_is_signal(msg,
823 "PropertyChanged")) {
824 jobjectArray str_array = parse_adapter_property_change(env, msg);
825 if (str_array != NULL) {
826 /* Check if bluetoothd has (re)started, if so update the path. */
827 jstring property =(jstring) env->GetObjectArrayElement(str_array, 0);
828 const char *c_property = env->GetStringUTFChars(property, NULL);
829 if (!strncmp(c_property, "Powered", strlen("Powered"))) {
831 (jstring) env->GetObjectArrayElement(str_array, 1);
832 const char *c_value = env->GetStringUTFChars(value, NULL);
833 if (!strncmp(c_value, "true", strlen("true")))
834 nat->adapter = get_adapter_path(nat->conn);
835 env->ReleaseStringUTFChars(value, c_value);
837 env->ReleaseStringUTFChars(property, c_property);
839 env->CallVoidMethod(nat->me,
840 method_onPropertyChanged,
842 } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
844 } else if (dbus_message_is_signal(msg,
846 "PropertyChanged")) {
847 jobjectArray str_array = parse_remote_device_property_change(env, msg);
848 if (str_array != NULL) {
849 const char *remote_device_path = dbus_message_get_path(msg);
850 env->CallVoidMethod(nat->me,
851 method_onDevicePropertyChanged,
852 env->NewStringUTF(remote_device_path),
854 } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
856 } else if (dbus_message_is_signal(msg,
858 "DisconnectRequested")) {
859 const char *remote_device_path = dbus_message_get_path(msg);
860 env->CallVoidMethod(nat->me,
861 method_onDeviceDisconnectRequested,
862 env->NewStringUTF(remote_device_path));
866 ret = a2dp_event_filter(msg, env);
867 env->PopLocalFrame(NULL);
871 env->PopLocalFrame(NULL);
872 return DBUS_HANDLER_RESULT_HANDLED;
875 // Called by dbus during WaitForAndDispatchEventNative()
876 DBusHandlerResult agent_event_filter(DBusConnection *conn,
877 DBusMessage *msg, void *data) {
878 native_data_t *nat = (native_data_t *)data;
880 if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
881 LOGV("%s: not interested (not a method call).", __FUNCTION__);
882 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
884 LOGI("%s: Received method %s:%s", __FUNCTION__,
885 dbus_message_get_interface(msg), dbus_message_get_member(msg));
887 if (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED;
889 nat->vm->GetEnv((void**)&env, nat->envVer);
890 env->PushLocalFrame(EVENT_LOOP_REFS);
892 if (dbus_message_is_method_call(msg,
893 "org.bluez.Agent", "Cancel")) {
894 env->CallVoidMethod(nat->me, method_onAgentCancel);
896 DBusMessage *reply = dbus_message_new_method_return(msg);
898 LOGE("%s: Cannot create message reply\n", __FUNCTION__);
901 dbus_connection_send(nat->conn, reply, NULL);
902 dbus_message_unref(reply);
905 } else if (dbus_message_is_method_call(msg,
906 "org.bluez.Agent", "Authorize")) {
909 if (!dbus_message_get_args(msg, NULL,
910 DBUS_TYPE_OBJECT_PATH, &object_path,
911 DBUS_TYPE_STRING, &uuid,
912 DBUS_TYPE_INVALID)) {
913 LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__);
917 LOGV("... object_path = %s", object_path);
918 LOGV("... uuid = %s", uuid);
921 env->CallBooleanMethod(nat->me, method_onAgentAuthorize,
922 env->NewStringUTF(object_path), env->NewStringUTF(uuid));
926 DBusMessage *reply = dbus_message_new_method_return(msg);
928 LOGE("%s: Cannot create message reply\n", __FUNCTION__);
931 dbus_connection_send(nat->conn, reply, NULL);
932 dbus_message_unref(reply);
934 DBusMessage *reply = dbus_message_new_error(msg,
935 "org.bluez.Error.Rejected", "Authorization rejected");
937 LOGE("%s: Cannot create message reply\n", __FUNCTION__);
940 dbus_connection_send(nat->conn, reply, NULL);
941 dbus_message_unref(reply);
944 } else if (dbus_message_is_method_call(msg,
945 "org.bluez.Agent", "OutOfBandAvailable")) {
947 if (!dbus_message_get_args(msg, NULL,
948 DBUS_TYPE_OBJECT_PATH, &object_path,
949 DBUS_TYPE_INVALID)) {
950 LOGE("%s: Invalid arguments for OutOfBandData available() method", __FUNCTION__);
954 LOGV("... object_path = %s", object_path);
957 env->CallBooleanMethod(nat->me, method_onAgentOutOfBandDataAvailable,
958 env->NewStringUTF(object_path));
963 DBusMessage *reply = dbus_message_new_method_return(msg);
965 LOGE("%s: Cannot create message reply\n", __FUNCTION__);
968 dbus_connection_send(nat->conn, reply, NULL);
969 dbus_message_unref(reply);
971 DBusMessage *reply = dbus_message_new_error(msg,
972 "org.bluez.Error.DoesNotExist", "OutofBand data not available");
974 LOGE("%s: Cannot create message reply\n", __FUNCTION__);
977 dbus_connection_send(nat->conn, reply, NULL);
978 dbus_message_unref(reply);
981 } else if (dbus_message_is_method_call(msg,
982 "org.bluez.Agent", "RequestPinCode")) {
984 if (!dbus_message_get_args(msg, NULL,
985 DBUS_TYPE_OBJECT_PATH, &object_path,
986 DBUS_TYPE_INVALID)) {
987 LOGE("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__);
991 dbus_message_ref(msg); // increment refcount because we pass to java
992 env->CallVoidMethod(nat->me, method_onRequestPinCode,
993 env->NewStringUTF(object_path),
996 } else if (dbus_message_is_method_call(msg,
997 "org.bluez.Agent", "RequestPasskey")) {
999 if (!dbus_message_get_args(msg, NULL,
1000 DBUS_TYPE_OBJECT_PATH, &object_path,
1001 DBUS_TYPE_INVALID)) {
1002 LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
1006 dbus_message_ref(msg); // increment refcount because we pass to java
1007 env->CallVoidMethod(nat->me, method_onRequestPasskey,
1008 env->NewStringUTF(object_path),
1011 } else if (dbus_message_is_method_call(msg,
1012 "org.bluez.Agent", "RequestOobData")) {
1014 if (!dbus_message_get_args(msg, NULL,
1015 DBUS_TYPE_OBJECT_PATH, &object_path,
1016 DBUS_TYPE_INVALID)) {
1017 LOGE("%s: Invalid arguments for RequestOobData() method", __FUNCTION__);
1021 dbus_message_ref(msg); // increment refcount because we pass to java
1022 env->CallVoidMethod(nat->me, method_onRequestOobData,
1023 env->NewStringUTF(object_path),
1026 } else if (dbus_message_is_method_call(msg,
1027 "org.bluez.Agent", "DisplayPasskey")) {
1030 if (!dbus_message_get_args(msg, NULL,
1031 DBUS_TYPE_OBJECT_PATH, &object_path,
1032 DBUS_TYPE_UINT32, &passkey,
1033 DBUS_TYPE_INVALID)) {
1034 LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
1038 dbus_message_ref(msg); // increment refcount because we pass to java
1039 env->CallVoidMethod(nat->me, method_onDisplayPasskey,
1040 env->NewStringUTF(object_path),
1044 } else if (dbus_message_is_method_call(msg,
1045 "org.bluez.Agent", "RequestConfirmation")) {
1048 if (!dbus_message_get_args(msg, NULL,
1049 DBUS_TYPE_OBJECT_PATH, &object_path,
1050 DBUS_TYPE_UINT32, &passkey,
1051 DBUS_TYPE_INVALID)) {
1052 LOGE("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__);
1056 dbus_message_ref(msg); // increment refcount because we pass to java
1057 env->CallVoidMethod(nat->me, method_onRequestPasskeyConfirmation,
1058 env->NewStringUTF(object_path),
1062 } else if (dbus_message_is_method_call(msg,
1063 "org.bluez.Agent", "RequestPairingConsent")) {
1065 if (!dbus_message_get_args(msg, NULL,
1066 DBUS_TYPE_OBJECT_PATH, &object_path,
1067 DBUS_TYPE_INVALID)) {
1068 LOGE("%s: Invalid arguments for RequestPairingConsent() method", __FUNCTION__);
1072 dbus_message_ref(msg); // increment refcount because we pass to java
1073 env->CallVoidMethod(nat->me, method_onRequestPairingConsent,
1074 env->NewStringUTF(object_path),
1077 } else if (dbus_message_is_method_call(msg,
1078 "org.bluez.Agent", "Release")) {
1080 DBusMessage *reply = dbus_message_new_method_return(msg);
1082 LOGE("%s: Cannot create message reply\n", __FUNCTION__);
1085 dbus_connection_send(nat->conn, reply, NULL);
1086 dbus_message_unref(reply);
1089 LOGV("%s:%s is ignored", dbus_message_get_interface(msg), dbus_message_get_member(msg));
1093 env->PopLocalFrame(NULL);
1094 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1097 env->PopLocalFrame(NULL);
1098 return DBUS_HANDLER_RESULT_HANDLED;
1104 #ifdef HAVE_BLUETOOTH
1105 //TODO: Unify result codes in a header
1106 #define BOND_RESULT_ERROR -1000
1107 #define BOND_RESULT_SUCCESS 0
1108 #define BOND_RESULT_AUTH_FAILED 1
1109 #define BOND_RESULT_AUTH_REJECTED 2
1110 #define BOND_RESULT_AUTH_CANCELED 3
1111 #define BOND_RESULT_REMOTE_DEVICE_DOWN 4
1112 #define BOND_RESULT_DISCOVERY_IN_PROGRESS 5
1113 #define BOND_RESULT_AUTH_TIMEOUT 6
1114 #define BOND_RESULT_REPEATED_ATTEMPTS 7
1116 void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) {
1119 native_data_t *nat = (native_data_t *)n;
1120 const char *address = (const char *)user;
1122 dbus_error_init(&err);
1126 nat->vm->GetEnv((void**)&env, nat->envVer);
1128 LOGV("... address = %s", address);
1130 jint result = BOND_RESULT_SUCCESS;
1131 if (dbus_set_error_from_message(&err, msg)) {
1132 if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) {
1133 // Pins did not match, or remote device did not respond to pin
1135 LOGV("... error = %s (%s)\n", err.name, err.message);
1136 result = BOND_RESULT_AUTH_FAILED;
1137 } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationRejected")) {
1138 // We rejected pairing, or the remote side rejected pairing. This
1139 // happens if either side presses 'cancel' at the pairing dialog.
1140 LOGV("... error = %s (%s)\n", err.name, err.message);
1141 result = BOND_RESULT_AUTH_REJECTED;
1142 } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationCanceled")) {
1143 // Not sure if this happens
1144 LOGV("... error = %s (%s)\n", err.name, err.message);
1145 result = BOND_RESULT_AUTH_CANCELED;
1146 } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.ConnectionAttemptFailed")) {
1147 // Other device is not responding at all
1148 LOGV("... error = %s (%s)\n", err.name, err.message);
1149 result = BOND_RESULT_REMOTE_DEVICE_DOWN;
1150 } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AlreadyExists")) {
1152 LOGV("... error = %s (%s)\n", err.name, err.message);
1153 result = BOND_RESULT_SUCCESS;
1154 } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") &&
1155 !strcmp(err.message, "Bonding in progress")) {
1156 LOGV("... error = %s (%s)\n", err.name, err.message);
1158 } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") &&
1159 !strcmp(err.message, "Discover in progress")) {
1160 LOGV("... error = %s (%s)\n", err.name, err.message);
1161 result = BOND_RESULT_DISCOVERY_IN_PROGRESS;
1162 } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.RepeatedAttempts")) {
1163 LOGV("... error = %s (%s)\n", err.name, err.message);
1164 result = BOND_RESULT_REPEATED_ATTEMPTS;
1165 } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationTimeout")) {
1166 LOGV("... error = %s (%s)\n", err.name, err.message);
1167 result = BOND_RESULT_AUTH_TIMEOUT;
1169 LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
1170 result = BOND_RESULT_ERROR;
1174 addr = env->NewStringUTF(address);
1175 env->CallVoidMethod(nat->me,
1176 method_onCreatePairedDeviceResult,
1179 env->DeleteLocalRef(addr);
1181 dbus_error_free(&err);
1185 void onCreateDeviceResult(DBusMessage *msg, void *user, void *n) {
1188 native_data_t *nat = (native_data_t *)n;
1189 const char *address= (const char *)user;
1191 dbus_error_init(&err);
1193 nat->vm->GetEnv((void**)&env, nat->envVer);
1195 LOGV("... Address = %s", address);
1197 jint result = CREATE_DEVICE_SUCCESS;
1198 if (dbus_set_error_from_message(&err, msg)) {
1199 if (dbus_error_has_name(&err, "org.bluez.Error.AlreadyExists")) {
1200 result = CREATE_DEVICE_ALREADY_EXISTS;
1202 result = CREATE_DEVICE_FAILED;
1204 LOG_AND_FREE_DBUS_ERROR(&err);
1206 jstring addr = env->NewStringUTF(address);
1207 env->CallVoidMethod(nat->me,
1208 method_onCreateDeviceResult,
1211 env->DeleteLocalRef(addr);
1215 void onDiscoverServicesResult(DBusMessage *msg, void *user, void *n) {
1218 native_data_t *nat = (native_data_t *)n;
1219 const char *path = (const char *)user;
1221 dbus_error_init(&err);
1223 nat->vm->GetEnv((void**)&env, nat->envVer);
1225 LOGV("... Device Path = %s", path);
1227 bool result = JNI_TRUE;
1228 if (dbus_set_error_from_message(&err, msg)) {
1229 LOG_AND_FREE_DBUS_ERROR(&err);
1232 jstring jPath = env->NewStringUTF(path);
1233 env->CallVoidMethod(nat->me,
1234 method_onDiscoverServicesResult,
1237 env->DeleteLocalRef(jPath);
1241 void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) {
1244 const char *address = (const char *) user;
1245 native_data_t *nat = (native_data_t *) n;
1248 dbus_error_init(&err);
1250 nat->vm->GetEnv((void**)&env, nat->envVer);
1254 LOGV("... address = %s", address);
1256 if (dbus_set_error_from_message(&err, msg) ||
1257 !dbus_message_get_args(msg, &err,
1258 DBUS_TYPE_INT32, &channel,
1259 DBUS_TYPE_INVALID)) {
1260 LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
1261 dbus_error_free(&err);
1265 jstring addr = env->NewStringUTF(address);
1266 env->CallVoidMethod(nat->me,
1267 method_onGetDeviceServiceChannelResult,
1270 env->DeleteLocalRef(addr);
1275 static JNINativeMethod sMethods[] = {
1276 /* name, signature, funcPtr */
1277 {"classInitNative", "()V", (void *)classInitNative},
1278 {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
1279 {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
1280 {"startEventLoopNative", "()V", (void *)startEventLoopNative},
1281 {"stopEventLoopNative", "()V", (void *)stopEventLoopNative},
1282 {"isEventLoopRunningNative", "()Z", (void *)isEventLoopRunningNative}
1285 int register_android_server_BluetoothEventLoop(JNIEnv *env) {
1286 return AndroidRuntime::registerNativeMethods(env,
1287 "android/server/BluetoothEventLoop", sMethods, NELEM(sMethods));
1290 } /* namespace android */