1/*
2** Copyright 2008, The Android Open Source Project
3**
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
7**
8**     http://www.apache.org/licenses/LICENSE-2.0
9**
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.
15*/
16
17#define LOG_TAG "BluetoothEventLoop.cpp"
18
19#include "android_bluetooth_common.h"
20#include "android_runtime/AndroidRuntime.h"
21#include "cutils/sockets.h"
22#include "JNIHelp.h"
23#include "jni.h"
24#include "utils/Log.h"
25#include "utils/misc.h"
26
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <errno.h>
31#include <unistd.h>
32
33#ifdef HAVE_BLUETOOTH
34#include <dbus/dbus.h>
35#endif
36
37namespace android {
38
39#define CREATE_DEVICE_ALREADY_EXISTS 1
40#define CREATE_DEVICE_SUCCESS 0
41#define CREATE_DEVICE_FAILED -1
42
43#ifdef HAVE_BLUETOOTH
44static jfieldID field_mNativeData;
45
46static jmethodID method_onPropertyChanged;
47static jmethodID method_onDevicePropertyChanged;
48static jmethodID method_onDeviceFound;
49static jmethodID method_onDeviceDisappeared;
50static jmethodID method_onDeviceCreated;
51static jmethodID method_onDeviceRemoved;
52static jmethodID method_onDeviceDisconnectRequested;
53
54static jmethodID method_onCreatePairedDeviceResult;
55static jmethodID method_onCreateDeviceResult;
56static jmethodID method_onDiscoverServicesResult;
57static jmethodID method_onGetDeviceServiceChannelResult;
58
59static jmethodID method_onRequestPinCode;
60static jmethodID method_onRequestPasskey;
61static jmethodID method_onRequestPasskeyConfirmation;
62static jmethodID method_onRequestPairingConsent;
63static jmethodID method_onDisplayPasskey;
64static jmethodID method_onRequestOobData;
65static jmethodID method_onAgentOutOfBandDataAvailable;
66static jmethodID method_onAgentAuthorize;
67static jmethodID method_onAgentCancel;
68
69typedef event_loop_native_data_t native_data_t;
70
71#define EVENT_LOOP_REFS 10
72
73static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
74    return (native_data_t *)(env->GetIntField(object,
75                                                 field_mNativeData));
76}
77
78native_data_t *get_EventLoop_native_data(JNIEnv *env, jobject object) {
79    return get_native_data(env, object);
80}
81
82#endif
83static void classInitNative(JNIEnv* env, jclass clazz) {
84    LOGV(__FUNCTION__);
85
86#ifdef HAVE_BLUETOOTH
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");
100
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");
107
108    method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
109                                               "(Ljava/lang/String;Ljava/lang/String;I)V");
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");
125
126    field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
127#endif
128}
129
130static void initializeNativeDataNative(JNIEnv* env, jobject object) {
131    LOGV(__FUNCTION__);
132#ifdef HAVE_BLUETOOTH
133    native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
134    if (NULL == nat) {
135        LOGE("%s: out of memory!", __FUNCTION__);
136        return;
137    }
138    memset(nat, 0, sizeof(native_data_t));
139
140    pthread_mutex_init(&(nat->thread_mutex), NULL);
141
142    env->SetIntField(object, field_mNativeData, (jint)nat);
143
144    {
145        DBusError err;
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);
152        }
153        dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
154    }
155#endif
156}
157
158static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
159    LOGV(__FUNCTION__);
160#ifdef HAVE_BLUETOOTH
161    native_data_t *nat =
162            (native_data_t *)env->GetIntField(object, field_mNativeData);
163
164    pthread_mutex_destroy(&(nat->thread_mutex));
165
166    if (nat) {
167        free(nat);
168    }
169#endif
170}
171
172#ifdef HAVE_BLUETOOTH
173static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
174                                      void *data);
175DBusHandlerResult agent_event_filter(DBusConnection *conn,
176                                     DBusMessage *msg,
177                                     void *data);
178static int register_agent(native_data_t *nat,
179                          const char *agent_path, const char *capabilities);
180
181static const DBusObjectPathVTable agent_vtable = {
182    NULL, agent_event_filter, NULL, NULL, NULL, NULL
183};
184
185static 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);
190}
191
192static 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);
197}
198
199static jboolean setUpEventLoop(native_data_t *nat) {
200    LOGV(__FUNCTION__);
201
202    if (nat != NULL && nat->conn != NULL) {
203        dbus_threads_init_default();
204        DBusError err;
205        dbus_error_init(&err);
206
207        // Add a filter for all incoming messages
208        if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){
209            return JNI_FALSE;
210        }
211
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'",
215                &err);
216        if (dbus_error_is_set(&err)) {
217            LOG_AND_FREE_DBUS_ERROR(&err);
218            return JNI_FALSE;
219        }
220        dbus_bus_add_match(nat->conn,
221                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",
222                &err);
223        if (dbus_error_is_set(&err)) {
224            LOG_AND_FREE_DBUS_ERROR(&err);
225            return JNI_FALSE;
226        }
227        dbus_bus_add_match(nat->conn,
228                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'",
229                &err);
230        if (dbus_error_is_set(&err)) {
231            LOG_AND_FREE_DBUS_ERROR(&err);
232            return JNI_FALSE;
233        }
234        dbus_bus_add_match(nat->conn,
235                "type='signal',interface='org.bluez.AudioSink'",
236                &err);
237        if (dbus_error_is_set(&err)) {
238            LOG_AND_FREE_DBUS_ERROR(&err);
239            return JNI_FALSE;
240        }
241
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);
246            return JNI_FALSE;
247        }
248        return JNI_TRUE;
249    }
250    return JNI_FALSE;
251}
252
253
254const char * get_adapter_path(DBusConnection *conn) {
255    DBusMessage *msg = NULL, *reply = NULL;
256    DBusError err;
257    const char *device_path = NULL;
258    int attempt = 0;
259
260    for (attempt = 0; attempt < 1000 && reply == NULL; attempt ++) {
261        msg = dbus_message_new_method_call("org.bluez", "/",
262              "org.bluez.Manager", "DefaultAdapter");
263        if (!msg) {
264            LOGE("%s: Can't allocate new method call for get_adapter_path!",
265                  __FUNCTION__);
266            return NULL;
267        }
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);
271
272        if (!reply) {
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
279                    continue;
280                } else {
281                    // Some other error we weren't expecting
282                    LOG_AND_FREE_DBUS_ERROR(&err);
283                }
284            }
285            goto failed;
286        }
287    }
288    if (attempt == 1000) {
289        LOGE("Time out while trying to get Adapter path, is bluetoothd up ?");
290        goto failed;
291    }
292
293    if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
294                               &device_path, DBUS_TYPE_INVALID)
295                               || !device_path){
296        if (dbus_error_is_set(&err)) {
297            LOG_AND_FREE_DBUS_ERROR(&err);
298        }
299        goto failed;
300    }
301    dbus_message_unref(msg);
302    return device_path;
303
304failed:
305    dbus_message_unref(msg);
306    return NULL;
307}
308
309static int register_agent(native_data_t *nat,
310                          const char * agent_path, const char * capabilities)
311{
312    DBusMessage *msg, *reply;
313    DBusError err;
314    bool oob = TRUE;
315
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);
320        return -1;
321    }
322
323    nat->adapter = get_adapter_path(nat->conn);
324    if (nat->adapter == NULL) {
325        return -1;
326    }
327    msg = dbus_message_new_method_call("org.bluez", nat->adapter,
328          "org.bluez.Adapter", "RegisterAgent");
329    if (!msg) {
330        LOGE("%s: Can't allocate new method call for agent!",
331              __FUNCTION__);
332        return -1;
333    }
334    dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
335                             DBUS_TYPE_STRING, &capabilities,
336                             DBUS_TYPE_BOOLEAN, &oob,
337                             DBUS_TYPE_INVALID);
338
339    dbus_error_init(&err);
340    reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
341    dbus_message_unref(msg);
342
343    if (!reply) {
344        LOGE("%s: Can't register agent!", __FUNCTION__);
345        if (dbus_error_is_set(&err)) {
346            LOG_AND_FREE_DBUS_ERROR(&err);
347        }
348        return -1;
349    }
350
351    dbus_message_unref(reply);
352    dbus_connection_flush(nat->conn);
353
354    return 0;
355}
356
357static void tearDownEventLoop(native_data_t *nat) {
358    LOGV(__FUNCTION__);
359    if (nat != NULL && nat->conn != NULL) {
360
361        DBusMessage *msg, *reply;
362        DBusError err;
363        dbus_error_init(&err);
364        const char * agent_path = "/android/bluetooth/agent";
365
366        msg = dbus_message_new_method_call("org.bluez",
367                                           nat->adapter,
368                                           "org.bluez.Adapter",
369                                           "UnregisterAgent");
370        if (msg != NULL) {
371            dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
372                                     DBUS_TYPE_INVALID);
373            reply = dbus_connection_send_with_reply_and_block(nat->conn,
374                                                              msg, -1, &err);
375
376            if (!reply) {
377                if (dbus_error_is_set(&err)) {
378                    LOG_AND_FREE_DBUS_ERROR(&err);
379                    dbus_error_free(&err);
380                }
381            } else {
382                dbus_message_unref(reply);
383            }
384            dbus_message_unref(msg);
385        } else {
386             LOGE("%s: Can't create new method call!", __FUNCTION__);
387        }
388
389        dbus_connection_flush(nat->conn);
390        dbus_connection_unregister_object_path(nat->conn, agent_path);
391
392        dbus_bus_remove_match(nat->conn,
393                "type='signal',interface='org.bluez.AudioSink'",
394                &err);
395        if (dbus_error_is_set(&err)) {
396            LOG_AND_FREE_DBUS_ERROR(&err);
397        }
398        dbus_bus_remove_match(nat->conn,
399                "type='signal',interface='org.bluez.Device'",
400                &err);
401        if (dbus_error_is_set(&err)) {
402            LOG_AND_FREE_DBUS_ERROR(&err);
403        }
404        dbus_bus_remove_match(nat->conn,
405                "type='signal',interface='org.bluez.audio.Manager'",
406                &err);
407        if (dbus_error_is_set(&err)) {
408            LOG_AND_FREE_DBUS_ERROR(&err);
409        }
410        dbus_bus_remove_match(nat->conn,
411                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",
412                &err);
413        if (dbus_error_is_set(&err)) {
414            LOG_AND_FREE_DBUS_ERROR(&err);
415        }
416        dbus_bus_remove_match(nat->conn,
417                "type='signal',interface='org.freedesktop.DBus'",
418                &err);
419        if (dbus_error_is_set(&err)) {
420            LOG_AND_FREE_DBUS_ERROR(&err);
421        }
422
423        dbus_connection_remove_filter(nat->conn, event_filter, nat);
424    }
425}
426
427
428#define EVENT_LOOP_EXIT 1
429#define EVENT_LOOP_ADD  2
430#define EVENT_LOOP_REMOVE 3
431
432dbus_bool_t dbusAddWatch(DBusWatch *watch, void *data) {
433    native_data_t *nat = (native_data_t *)data;
434
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));
442
443        int fd = dbus_watch_get_fd(watch);
444        write(nat->controlFdW, &fd, sizeof(int));
445
446        unsigned int flags = dbus_watch_get_flags(watch);
447        write(nat->controlFdW, &flags, sizeof(unsigned int));
448
449        write(nat->controlFdW, &watch, sizeof(DBusWatch*));
450    }
451    return true;
452}
453
454void dbusRemoveWatch(DBusWatch *watch, void *data) {
455    native_data_t *nat = (native_data_t *)data;
456
457    char control = EVENT_LOOP_REMOVE;
458    write(nat->controlFdW, &control, sizeof(char));
459
460    int fd = dbus_watch_get_fd(watch);
461    write(nat->controlFdW, &fd, sizeof(int));
462
463    unsigned int flags = dbus_watch_get_flags(watch);
464    write(nat->controlFdW, &flags, sizeof(unsigned int));
465}
466
467void dbusToggleWatch(DBusWatch *watch, void *data) {
468    if (dbus_watch_get_enabled(watch)) {
469        dbusAddWatch(watch, data);
470    } else {
471        dbusRemoveWatch(watch, data);
472    }
473}
474
475static void handleWatchAdd(native_data_t *nat) {
476    DBusWatch *watch;
477    int newFD;
478    unsigned int flags;
479
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);
484
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");
489            return;
490        }
491    }
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));
496        if (!temp) {
497            return;
498        }
499        memcpy(temp, nat->pollData, sizeof(struct pollfd) *
500                nat->pollMemberCount);
501        free(nat->pollData);
502        nat->pollData = temp;
503        DBusWatch **temp2 = (DBusWatch **)malloc(sizeof(DBusWatch *) *
504                (nat->pollMemberCount+1));
505        if (!temp2) {
506            return;
507        }
508        memcpy(temp2, nat->watchData, sizeof(DBusWatch *) *
509                nat->pollMemberCount);
510        free(nat->watchData);
511        nat->watchData = temp2;
512        nat->pollDataSize++;
513    }
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++;
519}
520
521static void handleWatchRemove(native_data_t *nat) {
522    int removeFD;
523    unsigned int flags;
524
525    read(nat->controlFdR, &removeFD, sizeof(int));
526    read(nat->controlFdR, &flags, sizeof(unsigned int));
527    short events = dbus_flags_to_unix_events(flags);
528
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];
538            return;
539        }
540    }
541    LOGW("WatchRemove given with unknown watch");
542}
543
544static void *eventLoopMain(void *ptr) {
545    native_data_t *nat = (native_data_t *)ptr;
546    JNIEnv *env;
547
548    JavaVMAttachArgs args;
549    char name[] = "BT EventLoop";
550    args.version = nat->envVer;
551    args.name = name;
552    args.group = NULL;
553
554    nat->vm->AttachCurrentThread(&env, &args);
555
556    dbus_connection_set_watch_functions(nat->conn, dbusAddWatch,
557            dbusRemoveWatch, dbusToggleWatch, ptr, NULL);
558
559    nat->running = true;
560
561    while (1) {
562        for (int i = 0; i < nat->pollMemberCount; i++) {
563            if (!nat->pollData[i].revents) {
564                continue;
565            }
566            if (nat->pollData[i].fd == nat->controlFdR) {
567                char data;
568                while (recv(nat->controlFdR, &data, sizeof(char), MSG_DONTWAIT)
569                        != -1) {
570                    switch (data) {
571                    case EVENT_LOOP_EXIT:
572                    {
573                        dbus_connection_set_watch_functions(nat->conn,
574                                NULL, NULL, NULL, NULL, NULL);
575                        tearDownEventLoop(nat);
576                        nat->vm->DetachCurrentThread();
577
578                        int fd = nat->controlFdR;
579                        nat->controlFdR = 0;
580                        close(fd);
581                        return NULL;
582                    }
583                    case EVENT_LOOP_ADD:
584                    {
585                        handleWatchAdd(nat);
586                        break;
587                    }
588                    case EVENT_LOOP_REMOVE:
589                    {
590                        handleWatchRemove(nat);
591                        break;
592                    }
593                    }
594                }
595            } else {
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'
601                break;
602            }
603        }
604        while (dbus_connection_dispatch(nat->conn) ==
605                DBUS_DISPATCH_DATA_REMAINS) {
606        }
607
608        poll(nat->pollData, nat->pollMemberCount, -1);
609    }
610}
611#endif // HAVE_BLUETOOTH
612
613static 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);
617
618    pthread_mutex_lock(&(nat->thread_mutex));
619
620    nat->running = false;
621
622    if (nat->pollData) {
623        LOGW("trying to start EventLoop a second time!");
624        pthread_mutex_unlock( &(nat->thread_mutex) );
625        return JNI_FALSE;
626    }
627
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!");
632        goto done;
633    }
634
635    nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) *
636            DEFAULT_INITIAL_POLLFD_COUNT);
637    if (!nat->watchData) {
638        LOGE("out of memory error starting EventLoop!");
639        goto done;
640    }
641
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;
648
649    if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &(nat->controlFdR))) {
650        LOGE("Error getting BT control socket");
651        goto done;
652    }
653    nat->pollData[0].fd = nat->controlFdR;
654    nat->pollData[0].events = POLLIN;
655
656    env->GetJavaVM( &(nat->vm) );
657    nat->envVer = env->GetVersion();
658
659    nat->me = env->NewGlobalRef(object);
660
661    if (setUpEventLoop(nat) != JNI_TRUE) {
662        LOGE("failure setting up Event Loop!");
663        goto done;
664    }
665
666    pthread_create(&(nat->thread), NULL, eventLoopMain, nat);
667    result = JNI_TRUE;
668
669done:
670    if (JNI_FALSE == result) {
671        if (nat->controlFdW) {
672            close(nat->controlFdW);
673            nat->controlFdW = 0;
674        }
675        if (nat->controlFdR) {
676            close(nat->controlFdR);
677            nat->controlFdR = 0;
678        }
679        if (nat->me) env->DeleteGlobalRef(nat->me);
680        nat->me = NULL;
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;
687    }
688
689    pthread_mutex_unlock(&(nat->thread_mutex));
690#endif // HAVE_BLUETOOTH
691    return result;
692}
693
694static void stopEventLoopNative(JNIEnv *env, jobject object) {
695#ifdef HAVE_BLUETOOTH
696    native_data_t *nat = get_native_data(env, object);
697
698    pthread_mutex_lock(&(nat->thread_mutex));
699    if (nat->pollData) {
700        char data = EVENT_LOOP_EXIT;
701        ssize_t t = write(nat->controlFdW, &data, sizeof(char));
702        void *ret;
703        pthread_join(nat->thread, &ret);
704
705        env->DeleteGlobalRef(nat->me);
706        nat->me = NULL;
707        free(nat->pollData);
708        nat->pollData = NULL;
709        free(nat->watchData);
710        nat->watchData = NULL;
711        nat->pollDataSize = 0;
712        nat->pollMemberCount = 0;
713
714        int fd = nat->controlFdW;
715        nat->controlFdW = 0;
716        close(fd);
717    }
718    nat->running = false;
719    pthread_mutex_unlock(&(nat->thread_mutex));
720#endif // HAVE_BLUETOOTH
721}
722
723static 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);
727
728    pthread_mutex_lock(&(nat->thread_mutex));
729    if (nat->running) {
730        result = JNI_TRUE;
731    }
732    pthread_mutex_unlock(&(nat->thread_mutex));
733
734#endif // HAVE_BLUETOOTH
735    return result;
736}
737
738#ifdef HAVE_BLUETOOTH
739extern DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env);
740
741// Called by dbus during WaitForAndDispatchEventNative()
742static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
743                                      void *data) {
744    native_data_t *nat;
745    JNIEnv *env;
746    DBusError err;
747    DBusHandlerResult ret;
748
749    dbus_error_init(&err);
750
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;
756    }
757
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));
761
762    env->PushLocalFrame(EVENT_LOOP_REFS);
763    if (dbus_message_is_signal(msg,
764                               "org.bluez.Adapter",
765                               "DeviceFound")) {
766        char *c_address;
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))
772                str_array =
773                    parse_remote_device_properties(env, &iter);
774        }
775        if (str_array != NULL) {
776            env->CallVoidMethod(nat->me,
777                                method_onDeviceFound,
778                                env->NewStringUTF(c_address),
779                                str_array);
780        } else
781            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
782        goto success;
783    } else if (dbus_message_is_signal(msg,
784                                     "org.bluez.Adapter",
785                                     "DeviceDisappeared")) {
786        char *c_address;
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);
794        goto success;
795    } else if (dbus_message_is_signal(msg,
796                                     "org.bluez.Adapter",
797                                     "DeviceCreated")) {
798        char *c_object_path;
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);
807        goto success;
808    } else if (dbus_message_is_signal(msg,
809                                     "org.bluez.Adapter",
810                                     "DeviceRemoved")) {
811        char *c_object_path;
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);
820        goto success;
821    } else if (dbus_message_is_signal(msg,
822                                      "org.bluez.Adapter",
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"))) {
830                jstring value =
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);
836            }
837            env->ReleaseStringUTFChars(property, c_property);
838
839            env->CallVoidMethod(nat->me,
840                              method_onPropertyChanged,
841                              str_array);
842        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
843        goto success;
844    } else if (dbus_message_is_signal(msg,
845                                      "org.bluez.Device",
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),
853                            str_array);
854        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
855        goto success;
856    } else if (dbus_message_is_signal(msg,
857                                      "org.bluez.Device",
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));
863        goto success;
864    }
865
866    ret = a2dp_event_filter(msg, env);
867    env->PopLocalFrame(NULL);
868    return ret;
869
870success:
871    env->PopLocalFrame(NULL);
872    return DBUS_HANDLER_RESULT_HANDLED;
873}
874
875// Called by dbus during WaitForAndDispatchEventNative()
876DBusHandlerResult agent_event_filter(DBusConnection *conn,
877                                     DBusMessage *msg, void *data) {
878    native_data_t *nat = (native_data_t *)data;
879    JNIEnv *env;
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;
883    }
884    LOGI("%s: Received method %s:%s", __FUNCTION__,
885         dbus_message_get_interface(msg), dbus_message_get_member(msg));
886
887    if (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED;
888
889    nat->vm->GetEnv((void**)&env, nat->envVer);
890    env->PushLocalFrame(EVENT_LOOP_REFS);
891
892    if (dbus_message_is_method_call(msg,
893            "org.bluez.Agent", "Cancel")) {
894        env->CallVoidMethod(nat->me, method_onAgentCancel);
895        // reply
896        DBusMessage *reply = dbus_message_new_method_return(msg);
897        if (!reply) {
898            LOGE("%s: Cannot create message reply\n", __FUNCTION__);
899            goto failure;
900        }
901        dbus_connection_send(nat->conn, reply, NULL);
902        dbus_message_unref(reply);
903        goto success;
904
905    } else if (dbus_message_is_method_call(msg,
906            "org.bluez.Agent", "Authorize")) {
907        char *object_path;
908        const char *uuid;
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__);
914            goto failure;
915        }
916
917        LOGV("... object_path = %s", object_path);
918        LOGV("... uuid = %s", uuid);
919
920        dbus_message_ref(msg);  // increment refcount because we pass to java
921        env->CallBooleanMethod(nat->me, method_onAgentAuthorize,
922                env->NewStringUTF(object_path), env->NewStringUTF(uuid),
923                int(msg));
924
925        goto success;
926    } else if (dbus_message_is_method_call(msg,
927            "org.bluez.Agent", "OutOfBandAvailable")) {
928        char *object_path;
929        if (!dbus_message_get_args(msg, NULL,
930                                   DBUS_TYPE_OBJECT_PATH, &object_path,
931                                   DBUS_TYPE_INVALID)) {
932            LOGE("%s: Invalid arguments for OutOfBandData available() method", __FUNCTION__);
933            goto failure;
934        }
935
936        LOGV("... object_path = %s", object_path);
937
938        bool available =
939            env->CallBooleanMethod(nat->me, method_onAgentOutOfBandDataAvailable,
940                env->NewStringUTF(object_path));
941
942
943        // reply
944        if (available) {
945            DBusMessage *reply = dbus_message_new_method_return(msg);
946            if (!reply) {
947                LOGE("%s: Cannot create message reply\n", __FUNCTION__);
948                goto failure;
949            }
950            dbus_connection_send(nat->conn, reply, NULL);
951            dbus_message_unref(reply);
952        } else {
953            DBusMessage *reply = dbus_message_new_error(msg,
954                    "org.bluez.Error.DoesNotExist", "OutofBand data not available");
955            if (!reply) {
956                LOGE("%s: Cannot create message reply\n", __FUNCTION__);
957                goto failure;
958            }
959            dbus_connection_send(nat->conn, reply, NULL);
960            dbus_message_unref(reply);
961        }
962        goto success;
963    } else if (dbus_message_is_method_call(msg,
964            "org.bluez.Agent", "RequestPinCode")) {
965        char *object_path;
966        if (!dbus_message_get_args(msg, NULL,
967                                   DBUS_TYPE_OBJECT_PATH, &object_path,
968                                   DBUS_TYPE_INVALID)) {
969            LOGE("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__);
970            goto failure;
971        }
972
973        dbus_message_ref(msg);  // increment refcount because we pass to java
974        env->CallVoidMethod(nat->me, method_onRequestPinCode,
975                                       env->NewStringUTF(object_path),
976                                       int(msg));
977        goto success;
978    } else if (dbus_message_is_method_call(msg,
979            "org.bluez.Agent", "RequestPasskey")) {
980        char *object_path;
981        if (!dbus_message_get_args(msg, NULL,
982                                   DBUS_TYPE_OBJECT_PATH, &object_path,
983                                   DBUS_TYPE_INVALID)) {
984            LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
985            goto failure;
986        }
987
988        dbus_message_ref(msg);  // increment refcount because we pass to java
989        env->CallVoidMethod(nat->me, method_onRequestPasskey,
990                                       env->NewStringUTF(object_path),
991                                       int(msg));
992        goto success;
993    } else if (dbus_message_is_method_call(msg,
994            "org.bluez.Agent", "RequestOobData")) {
995        char *object_path;
996        if (!dbus_message_get_args(msg, NULL,
997                                   DBUS_TYPE_OBJECT_PATH, &object_path,
998                                   DBUS_TYPE_INVALID)) {
999            LOGE("%s: Invalid arguments for RequestOobData() method", __FUNCTION__);
1000            goto failure;
1001        }
1002
1003        dbus_message_ref(msg);  // increment refcount because we pass to java
1004        env->CallVoidMethod(nat->me, method_onRequestOobData,
1005                                       env->NewStringUTF(object_path),
1006                                       int(msg));
1007        goto success;
1008    } else if (dbus_message_is_method_call(msg,
1009            "org.bluez.Agent", "DisplayPasskey")) {
1010        char *object_path;
1011        uint32_t passkey;
1012        if (!dbus_message_get_args(msg, NULL,
1013                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1014                                   DBUS_TYPE_UINT32, &passkey,
1015                                   DBUS_TYPE_INVALID)) {
1016            LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
1017            goto failure;
1018        }
1019
1020        dbus_message_ref(msg);  // increment refcount because we pass to java
1021        env->CallVoidMethod(nat->me, method_onDisplayPasskey,
1022                                       env->NewStringUTF(object_path),
1023                                       passkey,
1024                                       int(msg));
1025        goto success;
1026    } else if (dbus_message_is_method_call(msg,
1027            "org.bluez.Agent", "RequestConfirmation")) {
1028        char *object_path;
1029        uint32_t passkey;
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 RequestConfirmation() method", __FUNCTION__);
1035            goto failure;
1036        }
1037
1038        dbus_message_ref(msg);  // increment refcount because we pass to java
1039        env->CallVoidMethod(nat->me, method_onRequestPasskeyConfirmation,
1040                                       env->NewStringUTF(object_path),
1041                                       passkey,
1042                                       int(msg));
1043        goto success;
1044    } else if (dbus_message_is_method_call(msg,
1045            "org.bluez.Agent", "RequestPairingConsent")) {
1046        char *object_path;
1047        if (!dbus_message_get_args(msg, NULL,
1048                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1049                                   DBUS_TYPE_INVALID)) {
1050            LOGE("%s: Invalid arguments for RequestPairingConsent() method", __FUNCTION__);
1051            goto failure;
1052        }
1053
1054        dbus_message_ref(msg);  // increment refcount because we pass to java
1055        env->CallVoidMethod(nat->me, method_onRequestPairingConsent,
1056                                       env->NewStringUTF(object_path),
1057                                       int(msg));
1058        goto success;
1059    } else if (dbus_message_is_method_call(msg,
1060                  "org.bluez.Agent", "Release")) {
1061        // reply
1062        DBusMessage *reply = dbus_message_new_method_return(msg);
1063        if (!reply) {
1064            LOGE("%s: Cannot create message reply\n", __FUNCTION__);
1065            goto failure;
1066        }
1067        dbus_connection_send(nat->conn, reply, NULL);
1068        dbus_message_unref(reply);
1069        goto success;
1070    } else {
1071        LOGV("%s:%s is ignored", dbus_message_get_interface(msg), dbus_message_get_member(msg));
1072    }
1073
1074failure:
1075    env->PopLocalFrame(NULL);
1076    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1077
1078success:
1079    env->PopLocalFrame(NULL);
1080    return DBUS_HANDLER_RESULT_HANDLED;
1081
1082}
1083#endif
1084
1085
1086#ifdef HAVE_BLUETOOTH
1087//TODO: Unify result codes in a header
1088#define BOND_RESULT_ERROR -1000
1089#define BOND_RESULT_SUCCESS 0
1090#define BOND_RESULT_AUTH_FAILED 1
1091#define BOND_RESULT_AUTH_REJECTED 2
1092#define BOND_RESULT_AUTH_CANCELED 3
1093#define BOND_RESULT_REMOTE_DEVICE_DOWN 4
1094#define BOND_RESULT_DISCOVERY_IN_PROGRESS 5
1095#define BOND_RESULT_AUTH_TIMEOUT 6
1096#define BOND_RESULT_REPEATED_ATTEMPTS 7
1097
1098void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) {
1099    LOGV(__FUNCTION__);
1100
1101    native_data_t *nat = (native_data_t *)n;
1102    const char *address = (const char *)user;
1103    DBusError err;
1104    dbus_error_init(&err);
1105    JNIEnv *env;
1106    jstring addr;
1107
1108    nat->vm->GetEnv((void**)&env, nat->envVer);
1109
1110    LOGV("... address = %s", address);
1111
1112    jint result = BOND_RESULT_SUCCESS;
1113    if (dbus_set_error_from_message(&err, msg)) {
1114        if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) {
1115            // Pins did not match, or remote device did not respond to pin
1116            // request in time
1117            LOGV("... error = %s (%s)\n", err.name, err.message);
1118            result = BOND_RESULT_AUTH_FAILED;
1119        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationRejected")) {
1120            // We rejected pairing, or the remote side rejected pairing. This
1121            // happens if either side presses 'cancel' at the pairing dialog.
1122            LOGV("... error = %s (%s)\n", err.name, err.message);
1123            result = BOND_RESULT_AUTH_REJECTED;
1124        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationCanceled")) {
1125            // Not sure if this happens
1126            LOGV("... error = %s (%s)\n", err.name, err.message);
1127            result = BOND_RESULT_AUTH_CANCELED;
1128        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.ConnectionAttemptFailed")) {
1129            // Other device is not responding at all
1130            LOGV("... error = %s (%s)\n", err.name, err.message);
1131            result = BOND_RESULT_REMOTE_DEVICE_DOWN;
1132        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AlreadyExists")) {
1133            // already bonded
1134            LOGV("... error = %s (%s)\n", err.name, err.message);
1135            result = BOND_RESULT_SUCCESS;
1136        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") &&
1137                   !strcmp(err.message, "Bonding in progress")) {
1138            LOGV("... error = %s (%s)\n", err.name, err.message);
1139            goto done;
1140        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") &&
1141                   !strcmp(err.message, "Discover in progress")) {
1142            LOGV("... error = %s (%s)\n", err.name, err.message);
1143            result = BOND_RESULT_DISCOVERY_IN_PROGRESS;
1144        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.RepeatedAttempts")) {
1145            LOGV("... error = %s (%s)\n", err.name, err.message);
1146            result = BOND_RESULT_REPEATED_ATTEMPTS;
1147        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationTimeout")) {
1148            LOGV("... error = %s (%s)\n", err.name, err.message);
1149            result = BOND_RESULT_AUTH_TIMEOUT;
1150        } else {
1151            LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
1152            result = BOND_RESULT_ERROR;
1153        }
1154    }
1155
1156    addr = env->NewStringUTF(address);
1157    env->CallVoidMethod(nat->me,
1158                        method_onCreatePairedDeviceResult,
1159                        addr,
1160                        result);
1161    env->DeleteLocalRef(addr);
1162done:
1163    dbus_error_free(&err);
1164    free(user);
1165}
1166
1167void onCreateDeviceResult(DBusMessage *msg, void *user, void *n) {
1168    LOGV(__FUNCTION__);
1169
1170    native_data_t *nat = (native_data_t *)n;
1171    const char *address= (const char *)user;
1172    DBusError err;
1173    dbus_error_init(&err);
1174    JNIEnv *env;
1175    nat->vm->GetEnv((void**)&env, nat->envVer);
1176
1177    LOGV("... Address = %s", address);
1178
1179    jint result = CREATE_DEVICE_SUCCESS;
1180    if (dbus_set_error_from_message(&err, msg)) {
1181        if (dbus_error_has_name(&err, "org.bluez.Error.AlreadyExists")) {
1182            result = CREATE_DEVICE_ALREADY_EXISTS;
1183        } else {
1184            result = CREATE_DEVICE_FAILED;
1185        }
1186        LOG_AND_FREE_DBUS_ERROR(&err);
1187    }
1188    jstring addr = env->NewStringUTF(address);
1189    env->CallVoidMethod(nat->me,
1190                        method_onCreateDeviceResult,
1191                        addr,
1192                        result);
1193    env->DeleteLocalRef(addr);
1194    free(user);
1195}
1196
1197void onDiscoverServicesResult(DBusMessage *msg, void *user, void *n) {
1198    LOGV(__FUNCTION__);
1199
1200    native_data_t *nat = (native_data_t *)n;
1201    const char *path = (const char *)user;
1202    DBusError err;
1203    dbus_error_init(&err);
1204    JNIEnv *env;
1205    nat->vm->GetEnv((void**)&env, nat->envVer);
1206
1207    LOGV("... Device Path = %s", path);
1208
1209    bool result = JNI_TRUE;
1210    if (dbus_set_error_from_message(&err, msg)) {
1211        LOG_AND_FREE_DBUS_ERROR(&err);
1212        result = JNI_FALSE;
1213    }
1214    jstring jPath = env->NewStringUTF(path);
1215    env->CallVoidMethod(nat->me,
1216                        method_onDiscoverServicesResult,
1217                        jPath,
1218                        result);
1219    env->DeleteLocalRef(jPath);
1220    free(user);
1221}
1222
1223void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) {
1224    LOGV(__FUNCTION__);
1225
1226    const char *address = (const char *) user;
1227    native_data_t *nat = (native_data_t *) n;
1228
1229    DBusError err;
1230    dbus_error_init(&err);
1231    JNIEnv *env;
1232    nat->vm->GetEnv((void**)&env, nat->envVer);
1233
1234    jint channel = -2;
1235
1236    LOGV("... address = %s", address);
1237
1238    if (dbus_set_error_from_message(&err, msg) ||
1239        !dbus_message_get_args(msg, &err,
1240                               DBUS_TYPE_INT32, &channel,
1241                               DBUS_TYPE_INVALID)) {
1242        LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
1243        dbus_error_free(&err);
1244    }
1245
1246done:
1247    jstring addr = env->NewStringUTF(address);
1248    env->CallVoidMethod(nat->me,
1249                        method_onGetDeviceServiceChannelResult,
1250                        addr,
1251                        channel);
1252    env->DeleteLocalRef(addr);
1253    free(user);
1254}
1255#endif
1256
1257static JNINativeMethod sMethods[] = {
1258     /* name, signature, funcPtr */
1259    {"classInitNative", "()V", (void *)classInitNative},
1260    {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
1261    {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
1262    {"startEventLoopNative", "()V", (void *)startEventLoopNative},
1263    {"stopEventLoopNative", "()V", (void *)stopEventLoopNative},
1264    {"isEventLoopRunningNative", "()Z", (void *)isEventLoopRunningNative}
1265};
1266
1267int register_android_server_BluetoothEventLoop(JNIEnv *env) {
1268    return AndroidRuntime::registerNativeMethods(env,
1269            "android/server/BluetoothEventLoop", sMethods, NELEM(sMethods));
1270}
1271
1272} /* namespace android */
1273