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_onAgentAuthorize;
65static jmethodID method_onAgentCancel;
66
67typedef event_loop_native_data_t native_data_t;
68
69#define EVENT_LOOP_REFS 10
70
71static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
72    return (native_data_t *)(env->GetIntField(object,
73                                                 field_mNativeData));
74}
75
76native_data_t *get_EventLoop_native_data(JNIEnv *env, jobject object) {
77    return get_native_data(env, object);
78}
79
80#endif
81static void classInitNative(JNIEnv* env, jclass clazz) {
82    LOGV(__FUNCTION__);
83
84#ifdef HAVE_BLUETOOTH
85    method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged",
86                                                "([Ljava/lang/String;)V");
87    method_onDevicePropertyChanged = env->GetMethodID(clazz,
88                                                      "onDevicePropertyChanged",
89                                                      "(Ljava/lang/String;[Ljava/lang/String;)V");
90    method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound",
91                                            "(Ljava/lang/String;[Ljava/lang/String;)V");
92    method_onDeviceDisappeared = env->GetMethodID(clazz, "onDeviceDisappeared",
93                                                  "(Ljava/lang/String;)V");
94    method_onDeviceCreated = env->GetMethodID(clazz, "onDeviceCreated", "(Ljava/lang/String;)V");
95    method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V");
96    method_onDeviceDisconnectRequested = env->GetMethodID(clazz, "onDeviceDisconnectRequested",
97                                                        "(Ljava/lang/String;)V");
98
99    method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
100                                                         "(Ljava/lang/String;I)V");
101    method_onCreateDeviceResult = env->GetMethodID(clazz, "onCreateDeviceResult",
102                                                         "(Ljava/lang/String;I)V");
103    method_onDiscoverServicesResult = env->GetMethodID(clazz, "onDiscoverServicesResult",
104                                                         "(Ljava/lang/String;Z)V");
105
106    method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
107                                               "(Ljava/lang/String;Ljava/lang/String;)Z");
108    method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
109    method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
110                                               "(Ljava/lang/String;I)V");
111    method_onRequestPasskey = env->GetMethodID(clazz, "onRequestPasskey",
112                                               "(Ljava/lang/String;I)V");
113    method_onRequestPasskeyConfirmation = env->GetMethodID(clazz, "onRequestPasskeyConfirmation",
114                                               "(Ljava/lang/String;II)V");
115    method_onRequestPairingConsent = env->GetMethodID(clazz, "onRequestPairingConsent",
116                                               "(Ljava/lang/String;I)V");
117    method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey",
118                                               "(Ljava/lang/String;II)V");
119
120    field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
121#endif
122}
123
124static void initializeNativeDataNative(JNIEnv* env, jobject object) {
125    LOGV(__FUNCTION__);
126#ifdef HAVE_BLUETOOTH
127    native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
128    if (NULL == nat) {
129        LOGE("%s: out of memory!", __FUNCTION__);
130        return;
131    }
132    memset(nat, 0, sizeof(native_data_t));
133
134    pthread_mutex_init(&(nat->thread_mutex), NULL);
135
136    env->SetIntField(object, field_mNativeData, (jint)nat);
137
138    {
139        DBusError err;
140        dbus_error_init(&err);
141        dbus_threads_init_default();
142        nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
143        if (dbus_error_is_set(&err)) {
144            LOGE("%s: Could not get onto the system bus!", __FUNCTION__);
145            dbus_error_free(&err);
146        }
147        dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
148    }
149#endif
150}
151
152static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
153    LOGV(__FUNCTION__);
154#ifdef HAVE_BLUETOOTH
155    native_data_t *nat =
156            (native_data_t *)env->GetIntField(object, field_mNativeData);
157
158    pthread_mutex_destroy(&(nat->thread_mutex));
159
160    if (nat) {
161        free(nat);
162    }
163#endif
164}
165
166#ifdef HAVE_BLUETOOTH
167static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
168                                      void *data);
169DBusHandlerResult agent_event_filter(DBusConnection *conn,
170                                     DBusMessage *msg,
171                                     void *data);
172static int register_agent(native_data_t *nat,
173                          const char *agent_path, const char *capabilities);
174
175static const DBusObjectPathVTable agent_vtable = {
176    NULL, agent_event_filter, NULL, NULL, NULL, NULL
177};
178
179static unsigned int unix_events_to_dbus_flags(short events) {
180    return (events & DBUS_WATCH_READABLE ? POLLIN : 0) |
181           (events & DBUS_WATCH_WRITABLE ? POLLOUT : 0) |
182           (events & DBUS_WATCH_ERROR ? POLLERR : 0) |
183           (events & DBUS_WATCH_HANGUP ? POLLHUP : 0);
184}
185
186static short dbus_flags_to_unix_events(unsigned int flags) {
187    return (flags & POLLIN ? DBUS_WATCH_READABLE : 0) |
188           (flags & POLLOUT ? DBUS_WATCH_WRITABLE : 0) |
189           (flags & POLLERR ? DBUS_WATCH_ERROR : 0) |
190           (flags & POLLHUP ? DBUS_WATCH_HANGUP : 0);
191}
192
193static jboolean setUpEventLoop(native_data_t *nat) {
194    LOGV(__FUNCTION__);
195
196    if (nat != NULL && nat->conn != NULL) {
197        dbus_threads_init_default();
198        DBusError err;
199        dbus_error_init(&err);
200
201        // Add a filter for all incoming messages
202        if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){
203            return JNI_FALSE;
204        }
205
206        // Set which messages will be processed by this dbus connection
207        dbus_bus_add_match(nat->conn,
208                "type='signal',interface='org.freedesktop.DBus'",
209                &err);
210        if (dbus_error_is_set(&err)) {
211            LOG_AND_FREE_DBUS_ERROR(&err);
212            return JNI_FALSE;
213        }
214        dbus_bus_add_match(nat->conn,
215                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",
216                &err);
217        if (dbus_error_is_set(&err)) {
218            LOG_AND_FREE_DBUS_ERROR(&err);
219            return JNI_FALSE;
220        }
221        dbus_bus_add_match(nat->conn,
222                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'",
223                &err);
224        if (dbus_error_is_set(&err)) {
225            LOG_AND_FREE_DBUS_ERROR(&err);
226            return JNI_FALSE;
227        }
228        dbus_bus_add_match(nat->conn,
229                "type='signal',interface='org.bluez.AudioSink'",
230                &err);
231        if (dbus_error_is_set(&err)) {
232            LOG_AND_FREE_DBUS_ERROR(&err);
233            return JNI_FALSE;
234        }
235
236        const char *agent_path = "/android/bluetooth/agent";
237        const char *capabilities = "DisplayYesNo";
238        if (register_agent(nat, agent_path, capabilities) < 0) {
239            dbus_connection_unregister_object_path (nat->conn, agent_path);
240            return JNI_FALSE;
241        }
242        return JNI_TRUE;
243    }
244    return JNI_FALSE;
245}
246
247
248const char * get_adapter_path(DBusConnection *conn) {
249    DBusMessage *msg = NULL, *reply = NULL;
250    DBusError err;
251    const char *device_path = NULL;
252    int attempt = 0;
253
254    for (attempt = 0; attempt < 1000 && reply == NULL; attempt ++) {
255        msg = dbus_message_new_method_call("org.bluez", "/",
256              "org.bluez.Manager", "DefaultAdapter");
257        if (!msg) {
258            LOGE("%s: Can't allocate new method call for get_adapter_path!",
259                  __FUNCTION__);
260            return NULL;
261        }
262        dbus_message_append_args(msg, DBUS_TYPE_INVALID);
263        dbus_error_init(&err);
264        reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
265
266        if (!reply) {
267            if (dbus_error_is_set(&err)) {
268                if (dbus_error_has_name(&err,
269                    "org.freedesktop.DBus.Error.ServiceUnknown")) {
270                    // bluetoothd is still down, retry
271                    LOG_AND_FREE_DBUS_ERROR(&err);
272                    usleep(10000);  // 10 ms
273                    continue;
274                } else {
275                    // Some other error we weren't expecting
276                    LOG_AND_FREE_DBUS_ERROR(&err);
277                }
278            }
279            goto failed;
280        }
281    }
282    if (attempt == 1000) {
283        LOGE("Time out while trying to get Adapter path, is bluetoothd up ?");
284        goto failed;
285    }
286
287    if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
288                               &device_path, DBUS_TYPE_INVALID)
289                               || !device_path){
290        if (dbus_error_is_set(&err)) {
291            LOG_AND_FREE_DBUS_ERROR(&err);
292        }
293        goto failed;
294    }
295    dbus_message_unref(msg);
296    return device_path;
297
298failed:
299    dbus_message_unref(msg);
300    return NULL;
301}
302
303static int register_agent(native_data_t *nat,
304                          const char * agent_path, const char * capabilities)
305{
306    DBusMessage *msg, *reply;
307    DBusError err;
308
309    if (!dbus_connection_register_object_path(nat->conn, agent_path,
310            &agent_vtable, nat)) {
311        LOGE("%s: Can't register object path %s for agent!",
312              __FUNCTION__, agent_path);
313        return -1;
314    }
315
316    nat->adapter = get_adapter_path(nat->conn);
317    if (nat->adapter == NULL) {
318        return -1;
319    }
320    msg = dbus_message_new_method_call("org.bluez", nat->adapter,
321          "org.bluez.Adapter", "RegisterAgent");
322    if (!msg) {
323        LOGE("%s: Can't allocate new method call for agent!",
324              __FUNCTION__);
325        return -1;
326    }
327    dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
328                             DBUS_TYPE_STRING, &capabilities,
329                             DBUS_TYPE_INVALID);
330
331    dbus_error_init(&err);
332    reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
333    dbus_message_unref(msg);
334
335    if (!reply) {
336        LOGE("%s: Can't register agent!", __FUNCTION__);
337        if (dbus_error_is_set(&err)) {
338            LOG_AND_FREE_DBUS_ERROR(&err);
339        }
340        return -1;
341    }
342
343    dbus_message_unref(reply);
344    dbus_connection_flush(nat->conn);
345
346    return 0;
347}
348
349static void tearDownEventLoop(native_data_t *nat) {
350    LOGV(__FUNCTION__);
351    if (nat != NULL && nat->conn != NULL) {
352
353        DBusMessage *msg, *reply;
354        DBusError err;
355        dbus_error_init(&err);
356        const char * agent_path = "/android/bluetooth/agent";
357
358        msg = dbus_message_new_method_call("org.bluez",
359                                           nat->adapter,
360                                           "org.bluez.Adapter",
361                                           "UnregisterAgent");
362        if (msg != NULL) {
363            dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
364                                     DBUS_TYPE_INVALID);
365            reply = dbus_connection_send_with_reply_and_block(nat->conn,
366                                                              msg, -1, &err);
367
368            if (!reply) {
369                if (dbus_error_is_set(&err)) {
370                    LOG_AND_FREE_DBUS_ERROR(&err);
371                    dbus_error_free(&err);
372                }
373            } else {
374                dbus_message_unref(reply);
375            }
376            dbus_message_unref(msg);
377        } else {
378             LOGE("%s: Can't create new method call!", __FUNCTION__);
379        }
380
381        dbus_connection_flush(nat->conn);
382        dbus_connection_unregister_object_path(nat->conn, agent_path);
383
384        dbus_bus_remove_match(nat->conn,
385                "type='signal',interface='org.bluez.AudioSink'",
386                &err);
387        if (dbus_error_is_set(&err)) {
388            LOG_AND_FREE_DBUS_ERROR(&err);
389        }
390        dbus_bus_remove_match(nat->conn,
391                "type='signal',interface='org.bluez.audio.Device'",
392                &err);
393        if (dbus_error_is_set(&err)) {
394            LOG_AND_FREE_DBUS_ERROR(&err);
395        }
396        dbus_bus_remove_match(nat->conn,
397                "type='signal',interface='org.bluez.audio.Manager'",
398                &err);
399        if (dbus_error_is_set(&err)) {
400            LOG_AND_FREE_DBUS_ERROR(&err);
401        }
402        dbus_bus_remove_match(nat->conn,
403                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",
404                &err);
405        if (dbus_error_is_set(&err)) {
406            LOG_AND_FREE_DBUS_ERROR(&err);
407        }
408        dbus_bus_remove_match(nat->conn,
409                "type='signal',interface='org.freedesktop.DBus'",
410                &err);
411        if (dbus_error_is_set(&err)) {
412            LOG_AND_FREE_DBUS_ERROR(&err);
413        }
414
415        dbus_connection_remove_filter(nat->conn, event_filter, nat);
416    }
417}
418
419
420#define EVENT_LOOP_EXIT 1
421#define EVENT_LOOP_ADD  2
422#define EVENT_LOOP_REMOVE 3
423
424dbus_bool_t dbusAddWatch(DBusWatch *watch, void *data) {
425    native_data_t *nat = (native_data_t *)data;
426
427    if (dbus_watch_get_enabled(watch)) {
428        // note that we can't just send the watch and inspect it later
429        // because we may get a removeWatch call before this data is reacted
430        // to by our eventloop and remove this watch..  reading the add first
431        // and then inspecting the recently deceased watch would be bad.
432        char control = EVENT_LOOP_ADD;
433        write(nat->controlFdW, &control, sizeof(char));
434
435        int fd = dbus_watch_get_fd(watch);
436        write(nat->controlFdW, &fd, sizeof(int));
437
438        unsigned int flags = dbus_watch_get_flags(watch);
439        write(nat->controlFdW, &flags, sizeof(unsigned int));
440
441        write(nat->controlFdW, &watch, sizeof(DBusWatch*));
442    }
443    return true;
444}
445
446void dbusRemoveWatch(DBusWatch *watch, void *data) {
447    native_data_t *nat = (native_data_t *)data;
448
449    char control = EVENT_LOOP_REMOVE;
450    write(nat->controlFdW, &control, sizeof(char));
451
452    int fd = dbus_watch_get_fd(watch);
453    write(nat->controlFdW, &fd, sizeof(int));
454
455    unsigned int flags = dbus_watch_get_flags(watch);
456    write(nat->controlFdW, &flags, sizeof(unsigned int));
457}
458
459void dbusToggleWatch(DBusWatch *watch, void *data) {
460    if (dbus_watch_get_enabled(watch)) {
461        dbusAddWatch(watch, data);
462    } else {
463        dbusRemoveWatch(watch, data);
464    }
465}
466
467static void handleWatchAdd(native_data_t *nat) {
468    DBusWatch *watch;
469    int newFD;
470    unsigned int flags;
471
472    read(nat->controlFdR, &newFD, sizeof(int));
473    read(nat->controlFdR, &flags, sizeof(unsigned int));
474    read(nat->controlFdR, &watch, sizeof(DBusWatch *));
475    short events = dbus_flags_to_unix_events(flags);
476
477    for (int y = 0; y<nat->pollMemberCount; y++) {
478        if ((nat->pollData[y].fd == newFD) &&
479                (nat->pollData[y].events == events)) {
480            LOGV("DBusWatch duplicate add");
481            return;
482        }
483    }
484    if (nat->pollMemberCount == nat->pollDataSize) {
485        LOGV("Bluetooth EventLoop poll struct growing");
486        struct pollfd *temp = (struct pollfd *)malloc(
487                sizeof(struct pollfd) * (nat->pollMemberCount+1));
488        if (!temp) {
489            return;
490        }
491        memcpy(temp, nat->pollData, sizeof(struct pollfd) *
492                nat->pollMemberCount);
493        free(nat->pollData);
494        nat->pollData = temp;
495        DBusWatch **temp2 = (DBusWatch **)malloc(sizeof(DBusWatch *) *
496                (nat->pollMemberCount+1));
497        if (!temp2) {
498            return;
499        }
500        memcpy(temp2, nat->watchData, sizeof(DBusWatch *) *
501                nat->pollMemberCount);
502        free(nat->watchData);
503        nat->watchData = temp2;
504        nat->pollDataSize++;
505    }
506    nat->pollData[nat->pollMemberCount].fd = newFD;
507    nat->pollData[nat->pollMemberCount].revents = 0;
508    nat->pollData[nat->pollMemberCount].events = events;
509    nat->watchData[nat->pollMemberCount] = watch;
510    nat->pollMemberCount++;
511}
512
513static void handleWatchRemove(native_data_t *nat) {
514    int removeFD;
515    unsigned int flags;
516
517    read(nat->controlFdR, &removeFD, sizeof(int));
518    read(nat->controlFdR, &flags, sizeof(unsigned int));
519    short events = dbus_flags_to_unix_events(flags);
520
521    for (int y = 0; y < nat->pollMemberCount; y++) {
522        if ((nat->pollData[y].fd == removeFD) &&
523                (nat->pollData[y].events == events)) {
524            int newCount = --nat->pollMemberCount;
525            // copy the last live member over this one
526            nat->pollData[y].fd = nat->pollData[newCount].fd;
527            nat->pollData[y].events = nat->pollData[newCount].events;
528            nat->pollData[y].revents = nat->pollData[newCount].revents;
529            nat->watchData[y] = nat->watchData[newCount];
530            return;
531        }
532    }
533    LOGW("WatchRemove given with unknown watch");
534}
535
536static void *eventLoopMain(void *ptr) {
537    native_data_t *nat = (native_data_t *)ptr;
538    JNIEnv *env;
539
540    JavaVMAttachArgs args;
541    char name[] = "BT EventLoop";
542    args.version = nat->envVer;
543    args.name = name;
544    args.group = NULL;
545
546    nat->vm->AttachCurrentThread(&env, &args);
547
548    dbus_connection_set_watch_functions(nat->conn, dbusAddWatch,
549            dbusRemoveWatch, dbusToggleWatch, ptr, NULL);
550
551    nat->running = true;
552
553    while (1) {
554        for (int i = 0; i < nat->pollMemberCount; i++) {
555            if (!nat->pollData[i].revents) {
556                continue;
557            }
558            if (nat->pollData[i].fd == nat->controlFdR) {
559                char data;
560                while (recv(nat->controlFdR, &data, sizeof(char), MSG_DONTWAIT)
561                        != -1) {
562                    switch (data) {
563                    case EVENT_LOOP_EXIT:
564                    {
565                        dbus_connection_set_watch_functions(nat->conn,
566                                NULL, NULL, NULL, NULL, NULL);
567                        tearDownEventLoop(nat);
568                        nat->vm->DetachCurrentThread();
569
570                        int fd = nat->controlFdR;
571                        nat->controlFdR = 0;
572                        close(fd);
573                        return NULL;
574                    }
575                    case EVENT_LOOP_ADD:
576                    {
577                        handleWatchAdd(nat);
578                        break;
579                    }
580                    case EVENT_LOOP_REMOVE:
581                    {
582                        handleWatchRemove(nat);
583                        break;
584                    }
585                    }
586                }
587            } else {
588                short events = nat->pollData[i].revents;
589                unsigned int flags = unix_events_to_dbus_flags(events);
590                dbus_watch_handle(nat->watchData[i], flags);
591                nat->pollData[i].revents = 0;
592                // can only do one - it may have caused a 'remove'
593                break;
594            }
595        }
596        while (dbus_connection_dispatch(nat->conn) ==
597                DBUS_DISPATCH_DATA_REMAINS) {
598        }
599
600        poll(nat->pollData, nat->pollMemberCount, -1);
601    }
602}
603#endif // HAVE_BLUETOOTH
604
605static jboolean startEventLoopNative(JNIEnv *env, jobject object) {
606    jboolean result = JNI_FALSE;
607#ifdef HAVE_BLUETOOTH
608    event_loop_native_data_t *nat = get_native_data(env, object);
609
610    pthread_mutex_lock(&(nat->thread_mutex));
611
612    nat->running = false;
613
614    if (nat->pollData) {
615        LOGW("trying to start EventLoop a second time!");
616        pthread_mutex_unlock( &(nat->thread_mutex) );
617        return JNI_FALSE;
618    }
619
620    nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) *
621            DEFAULT_INITIAL_POLLFD_COUNT);
622    if (!nat->pollData) {
623        LOGE("out of memory error starting EventLoop!");
624        goto done;
625    }
626
627    nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) *
628            DEFAULT_INITIAL_POLLFD_COUNT);
629    if (!nat->watchData) {
630        LOGE("out of memory error starting EventLoop!");
631        goto done;
632    }
633
634    memset(nat->pollData, 0, sizeof(struct pollfd) *
635            DEFAULT_INITIAL_POLLFD_COUNT);
636    memset(nat->watchData, 0, sizeof(DBusWatch *) *
637            DEFAULT_INITIAL_POLLFD_COUNT);
638    nat->pollDataSize = DEFAULT_INITIAL_POLLFD_COUNT;
639    nat->pollMemberCount = 1;
640
641    if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &(nat->controlFdR))) {
642        LOGE("Error getting BT control socket");
643        goto done;
644    }
645    nat->pollData[0].fd = nat->controlFdR;
646    nat->pollData[0].events = POLLIN;
647
648    env->GetJavaVM( &(nat->vm) );
649    nat->envVer = env->GetVersion();
650
651    nat->me = env->NewGlobalRef(object);
652
653    if (setUpEventLoop(nat) != JNI_TRUE) {
654        LOGE("failure setting up Event Loop!");
655        goto done;
656    }
657
658    pthread_create(&(nat->thread), NULL, eventLoopMain, nat);
659    result = JNI_TRUE;
660
661done:
662    if (JNI_FALSE == result) {
663        if (nat->controlFdW) {
664            close(nat->controlFdW);
665            nat->controlFdW = 0;
666        }
667        if (nat->controlFdR) {
668            close(nat->controlFdR);
669            nat->controlFdR = 0;
670        }
671        if (nat->me) env->DeleteGlobalRef(nat->me);
672        nat->me = NULL;
673        if (nat->pollData) free(nat->pollData);
674        nat->pollData = NULL;
675        if (nat->watchData) free(nat->watchData);
676        nat->watchData = NULL;
677        nat->pollDataSize = 0;
678        nat->pollMemberCount = 0;
679    }
680
681    pthread_mutex_unlock(&(nat->thread_mutex));
682#endif // HAVE_BLUETOOTH
683    return result;
684}
685
686static void stopEventLoopNative(JNIEnv *env, jobject object) {
687#ifdef HAVE_BLUETOOTH
688    native_data_t *nat = get_native_data(env, object);
689
690    pthread_mutex_lock(&(nat->thread_mutex));
691    if (nat->pollData) {
692        char data = EVENT_LOOP_EXIT;
693        ssize_t t = write(nat->controlFdW, &data, sizeof(char));
694        void *ret;
695        pthread_join(nat->thread, &ret);
696
697        env->DeleteGlobalRef(nat->me);
698        nat->me = NULL;
699        free(nat->pollData);
700        nat->pollData = NULL;
701        free(nat->watchData);
702        nat->watchData = NULL;
703        nat->pollDataSize = 0;
704        nat->pollMemberCount = 0;
705
706        int fd = nat->controlFdW;
707        nat->controlFdW = 0;
708        close(fd);
709    }
710    nat->running = false;
711    pthread_mutex_unlock(&(nat->thread_mutex));
712#endif // HAVE_BLUETOOTH
713}
714
715static jboolean isEventLoopRunningNative(JNIEnv *env, jobject object) {
716    jboolean result = JNI_FALSE;
717#ifdef HAVE_BLUETOOTH
718    native_data_t *nat = get_native_data(env, object);
719
720    pthread_mutex_lock(&(nat->thread_mutex));
721    if (nat->running) {
722        result = JNI_TRUE;
723    }
724    pthread_mutex_unlock(&(nat->thread_mutex));
725
726#endif // HAVE_BLUETOOTH
727    return result;
728}
729
730#ifdef HAVE_BLUETOOTH
731extern DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env);
732
733// Called by dbus during WaitForAndDispatchEventNative()
734static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
735                                      void *data) {
736    native_data_t *nat;
737    JNIEnv *env;
738    DBusError err;
739    DBusHandlerResult ret;
740
741    dbus_error_init(&err);
742
743    nat = (native_data_t *)data;
744    nat->vm->GetEnv((void**)&env, nat->envVer);
745    if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) {
746        LOGV("%s: not interested (not a signal).", __FUNCTION__);
747        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
748    }
749
750    LOGE("%s: Received signal %s:%s from %s", __FUNCTION__,
751        dbus_message_get_interface(msg), dbus_message_get_member(msg),
752        dbus_message_get_path(msg));
753
754    env->PushLocalFrame(EVENT_LOOP_REFS);
755    if (dbus_message_is_signal(msg,
756                               "org.bluez.Adapter",
757                               "DeviceFound")) {
758        char *c_address;
759        DBusMessageIter iter;
760        jobjectArray str_array = NULL;
761        if (dbus_message_iter_init(msg, &iter)) {
762            dbus_message_iter_get_basic(&iter, &c_address);
763            if (dbus_message_iter_next(&iter))
764                str_array =
765                    parse_remote_device_properties(env, &iter);
766        }
767        if (str_array != NULL) {
768            env->CallVoidMethod(nat->me,
769                                method_onDeviceFound,
770                                env->NewStringUTF(c_address),
771                                str_array);
772        } else
773            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
774        goto success;
775    } else if (dbus_message_is_signal(msg,
776                                     "org.bluez.Adapter",
777                                     "DeviceDisappeared")) {
778        char *c_address;
779        if (dbus_message_get_args(msg, &err,
780                                  DBUS_TYPE_STRING, &c_address,
781                                  DBUS_TYPE_INVALID)) {
782            LOGV("... address = %s", c_address);
783            env->CallVoidMethod(nat->me, method_onDeviceDisappeared,
784                                env->NewStringUTF(c_address));
785        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
786        goto success;
787    } else if (dbus_message_is_signal(msg,
788                                     "org.bluez.Adapter",
789                                     "DeviceCreated")) {
790        char *c_object_path;
791        if (dbus_message_get_args(msg, &err,
792                                  DBUS_TYPE_OBJECT_PATH, &c_object_path,
793                                  DBUS_TYPE_INVALID)) {
794            LOGV("... address = %s", c_object_path);
795            env->CallVoidMethod(nat->me,
796                                method_onDeviceCreated,
797                                env->NewStringUTF(c_object_path));
798        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
799        goto success;
800    } else if (dbus_message_is_signal(msg,
801                                     "org.bluez.Adapter",
802                                     "DeviceRemoved")) {
803        char *c_object_path;
804        if (dbus_message_get_args(msg, &err,
805                                 DBUS_TYPE_OBJECT_PATH, &c_object_path,
806                                 DBUS_TYPE_INVALID)) {
807           LOGV("... Object Path = %s", c_object_path);
808           env->CallVoidMethod(nat->me,
809                               method_onDeviceRemoved,
810                               env->NewStringUTF(c_object_path));
811        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
812        goto success;
813    } else if (dbus_message_is_signal(msg,
814                                      "org.bluez.Adapter",
815                                      "PropertyChanged")) {
816        jobjectArray str_array = parse_adapter_property_change(env, msg);
817        if (str_array != NULL) {
818            /* Check if bluetoothd has (re)started, if so update the path. */
819            jstring property =(jstring) env->GetObjectArrayElement(str_array, 0);
820            const char *c_property = env->GetStringUTFChars(property, NULL);
821            if (!strncmp(c_property, "Powered", strlen("Powered"))) {
822                jstring value =
823                    (jstring) env->GetObjectArrayElement(str_array, 1);
824                const char *c_value = env->GetStringUTFChars(value, NULL);
825                if (!strncmp(c_value, "true", strlen("true")))
826                    nat->adapter = get_adapter_path(nat->conn);
827                env->ReleaseStringUTFChars(value, c_value);
828            }
829            env->ReleaseStringUTFChars(property, c_property);
830
831            env->CallVoidMethod(nat->me,
832                              method_onPropertyChanged,
833                              str_array);
834        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
835        goto success;
836    } else if (dbus_message_is_signal(msg,
837                                      "org.bluez.Device",
838                                      "PropertyChanged")) {
839        jobjectArray str_array = parse_remote_device_property_change(env, msg);
840        if (str_array != NULL) {
841            const char *remote_device_path = dbus_message_get_path(msg);
842            env->CallVoidMethod(nat->me,
843                            method_onDevicePropertyChanged,
844                            env->NewStringUTF(remote_device_path),
845                            str_array);
846        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
847        goto success;
848    } else if (dbus_message_is_signal(msg,
849                                      "org.bluez.Device",
850                                      "DisconnectRequested")) {
851        const char *remote_device_path = dbus_message_get_path(msg);
852        env->CallVoidMethod(nat->me,
853                            method_onDeviceDisconnectRequested,
854                            env->NewStringUTF(remote_device_path));
855        goto success;
856    }
857
858    ret = a2dp_event_filter(msg, env);
859    env->PopLocalFrame(NULL);
860    return ret;
861
862success:
863    env->PopLocalFrame(NULL);
864    return DBUS_HANDLER_RESULT_HANDLED;
865}
866
867// Called by dbus during WaitForAndDispatchEventNative()
868DBusHandlerResult agent_event_filter(DBusConnection *conn,
869                                     DBusMessage *msg, void *data) {
870    native_data_t *nat = (native_data_t *)data;
871    JNIEnv *env;
872    if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
873        LOGV("%s: not interested (not a method call).", __FUNCTION__);
874        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
875    }
876    LOGI("%s: Received method %s:%s", __FUNCTION__,
877         dbus_message_get_interface(msg), dbus_message_get_member(msg));
878
879    if (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED;
880
881    nat->vm->GetEnv((void**)&env, nat->envVer);
882    env->PushLocalFrame(EVENT_LOOP_REFS);
883
884    if (dbus_message_is_method_call(msg,
885            "org.bluez.Agent", "Cancel")) {
886        env->CallVoidMethod(nat->me, method_onAgentCancel);
887        // reply
888        DBusMessage *reply = dbus_message_new_method_return(msg);
889        if (!reply) {
890            LOGE("%s: Cannot create message reply\n", __FUNCTION__);
891            goto failure;
892        }
893        dbus_connection_send(nat->conn, reply, NULL);
894        dbus_message_unref(reply);
895        goto success;
896
897    } else if (dbus_message_is_method_call(msg,
898            "org.bluez.Agent", "Authorize")) {
899        char *object_path;
900        const char *uuid;
901        if (!dbus_message_get_args(msg, NULL,
902                                   DBUS_TYPE_OBJECT_PATH, &object_path,
903                                   DBUS_TYPE_STRING, &uuid,
904                                   DBUS_TYPE_INVALID)) {
905            LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__);
906            goto failure;
907        }
908
909        LOGV("... object_path = %s", object_path);
910        LOGV("... uuid = %s", uuid);
911
912        bool auth_granted =
913            env->CallBooleanMethod(nat->me, method_onAgentAuthorize,
914                env->NewStringUTF(object_path), env->NewStringUTF(uuid));
915
916        // reply
917        if (auth_granted) {
918            DBusMessage *reply = dbus_message_new_method_return(msg);
919            if (!reply) {
920                LOGE("%s: Cannot create message reply\n", __FUNCTION__);
921                goto failure;
922            }
923            dbus_connection_send(nat->conn, reply, NULL);
924            dbus_message_unref(reply);
925        } else {
926            DBusMessage *reply = dbus_message_new_error(msg,
927                    "org.bluez.Error.Rejected", "Authorization rejected");
928            if (!reply) {
929                LOGE("%s: Cannot create message reply\n", __FUNCTION__);
930                goto failure;
931            }
932            dbus_connection_send(nat->conn, reply, NULL);
933            dbus_message_unref(reply);
934        }
935        goto success;
936    } else if (dbus_message_is_method_call(msg,
937            "org.bluez.Agent", "RequestPinCode")) {
938        char *object_path;
939        if (!dbus_message_get_args(msg, NULL,
940                                   DBUS_TYPE_OBJECT_PATH, &object_path,
941                                   DBUS_TYPE_INVALID)) {
942            LOGE("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__);
943            goto failure;
944        }
945
946        dbus_message_ref(msg);  // increment refcount because we pass to java
947        env->CallVoidMethod(nat->me, method_onRequestPinCode,
948                                       env->NewStringUTF(object_path),
949                                       int(msg));
950        goto success;
951    } else if (dbus_message_is_method_call(msg,
952            "org.bluez.Agent", "RequestPasskey")) {
953        char *object_path;
954        if (!dbus_message_get_args(msg, NULL,
955                                   DBUS_TYPE_OBJECT_PATH, &object_path,
956                                   DBUS_TYPE_INVALID)) {
957            LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
958            goto failure;
959        }
960
961        dbus_message_ref(msg);  // increment refcount because we pass to java
962        env->CallVoidMethod(nat->me, method_onRequestPasskey,
963                                       env->NewStringUTF(object_path),
964                                       int(msg));
965        goto success;
966    } else if (dbus_message_is_method_call(msg,
967            "org.bluez.Agent", "DisplayPasskey")) {
968        char *object_path;
969        uint32_t passkey;
970        if (!dbus_message_get_args(msg, NULL,
971                                   DBUS_TYPE_OBJECT_PATH, &object_path,
972                                   DBUS_TYPE_UINT32, &passkey,
973                                   DBUS_TYPE_INVALID)) {
974            LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
975            goto failure;
976        }
977
978        dbus_message_ref(msg);  // increment refcount because we pass to java
979        env->CallVoidMethod(nat->me, method_onDisplayPasskey,
980                                       env->NewStringUTF(object_path),
981                                       passkey,
982                                       int(msg));
983        goto success;
984    } else if (dbus_message_is_method_call(msg,
985            "org.bluez.Agent", "RequestConfirmation")) {
986        char *object_path;
987        uint32_t passkey;
988        if (!dbus_message_get_args(msg, NULL,
989                                   DBUS_TYPE_OBJECT_PATH, &object_path,
990                                   DBUS_TYPE_UINT32, &passkey,
991                                   DBUS_TYPE_INVALID)) {
992            LOGE("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__);
993            goto failure;
994        }
995
996        dbus_message_ref(msg);  // increment refcount because we pass to java
997        env->CallVoidMethod(nat->me, method_onRequestPasskeyConfirmation,
998                                       env->NewStringUTF(object_path),
999                                       passkey,
1000                                       int(msg));
1001        goto success;
1002    } else if (dbus_message_is_method_call(msg,
1003            "org.bluez.Agent", "RequestPairingConsent")) {
1004        char *object_path;
1005        if (!dbus_message_get_args(msg, NULL,
1006                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1007                                   DBUS_TYPE_INVALID)) {
1008            LOGE("%s: Invalid arguments for RequestPairingConsent() method", __FUNCTION__);
1009            goto failure;
1010        }
1011
1012        dbus_message_ref(msg);  // increment refcount because we pass to java
1013        env->CallVoidMethod(nat->me, method_onRequestPairingConsent,
1014                                       env->NewStringUTF(object_path),
1015                                       int(msg));
1016        goto success;
1017    } else if (dbus_message_is_method_call(msg,
1018                  "org.bluez.Agent", "Release")) {
1019        // reply
1020        DBusMessage *reply = dbus_message_new_method_return(msg);
1021        if (!reply) {
1022            LOGE("%s: Cannot create message reply\n", __FUNCTION__);
1023            goto failure;
1024        }
1025        dbus_connection_send(nat->conn, reply, NULL);
1026        dbus_message_unref(reply);
1027        goto success;
1028    } else {
1029        LOGV("%s:%s is ignored", dbus_message_get_interface(msg), dbus_message_get_member(msg));
1030    }
1031
1032failure:
1033    env->PopLocalFrame(NULL);
1034    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1035
1036success:
1037    env->PopLocalFrame(NULL);
1038    return DBUS_HANDLER_RESULT_HANDLED;
1039
1040}
1041#endif
1042
1043
1044#ifdef HAVE_BLUETOOTH
1045//TODO: Unify result codes in a header
1046#define BOND_RESULT_ERROR -1000
1047#define BOND_RESULT_SUCCESS 0
1048#define BOND_RESULT_AUTH_FAILED 1
1049#define BOND_RESULT_AUTH_REJECTED 2
1050#define BOND_RESULT_AUTH_CANCELED 3
1051#define BOND_RESULT_REMOTE_DEVICE_DOWN 4
1052#define BOND_RESULT_DISCOVERY_IN_PROGRESS 5
1053#define BOND_RESULT_AUTH_TIMEOUT 6
1054#define BOND_RESULT_REPEATED_ATTEMPTS 7
1055
1056void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) {
1057    LOGV(__FUNCTION__);
1058
1059    native_data_t *nat = (native_data_t *)n;
1060    const char *address = (const char *)user;
1061    DBusError err;
1062    dbus_error_init(&err);
1063    JNIEnv *env;
1064    nat->vm->GetEnv((void**)&env, nat->envVer);
1065
1066    LOGV("... address = %s", address);
1067
1068    jint result = BOND_RESULT_SUCCESS;
1069    if (dbus_set_error_from_message(&err, msg)) {
1070        if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) {
1071            // Pins did not match, or remote device did not respond to pin
1072            // request in time
1073            LOGV("... error = %s (%s)\n", err.name, err.message);
1074            result = BOND_RESULT_AUTH_FAILED;
1075        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationRejected")) {
1076            // We rejected pairing, or the remote side rejected pairing. This
1077            // happens if either side presses 'cancel' at the pairing dialog.
1078            LOGV("... error = %s (%s)\n", err.name, err.message);
1079            result = BOND_RESULT_AUTH_REJECTED;
1080        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationCanceled")) {
1081            // Not sure if this happens
1082            LOGV("... error = %s (%s)\n", err.name, err.message);
1083            result = BOND_RESULT_AUTH_CANCELED;
1084        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.ConnectionAttemptFailed")) {
1085            // Other device is not responding at all
1086            LOGV("... error = %s (%s)\n", err.name, err.message);
1087            result = BOND_RESULT_REMOTE_DEVICE_DOWN;
1088        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AlreadyExists")) {
1089            // already bonded
1090            LOGV("... error = %s (%s)\n", err.name, err.message);
1091            result = BOND_RESULT_SUCCESS;
1092        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") &&
1093                   !strcmp(err.message, "Bonding in progress")) {
1094            LOGV("... error = %s (%s)\n", err.name, err.message);
1095            goto done;
1096        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") &&
1097                   !strcmp(err.message, "Discover in progress")) {
1098            LOGV("... error = %s (%s)\n", err.name, err.message);
1099            result = BOND_RESULT_DISCOVERY_IN_PROGRESS;
1100        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.RepeatedAttempts")) {
1101            LOGV("... error = %s (%s)\n", err.name, err.message);
1102            result = BOND_RESULT_REPEATED_ATTEMPTS;
1103        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationTimeout")) {
1104            LOGV("... error = %s (%s)\n", err.name, err.message);
1105            result = BOND_RESULT_AUTH_TIMEOUT;
1106        } else {
1107            LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
1108            result = BOND_RESULT_ERROR;
1109        }
1110    }
1111
1112    env->CallVoidMethod(nat->me,
1113                        method_onCreatePairedDeviceResult,
1114                        env->NewStringUTF(address),
1115                        result);
1116done:
1117    dbus_error_free(&err);
1118    free(user);
1119}
1120
1121void onCreateDeviceResult(DBusMessage *msg, void *user, void *n) {
1122    LOGV(__FUNCTION__);
1123
1124    native_data_t *nat = (native_data_t *)n;
1125    const char *address= (const char *)user;
1126    DBusError err;
1127    dbus_error_init(&err);
1128    JNIEnv *env;
1129    nat->vm->GetEnv((void**)&env, nat->envVer);
1130
1131    LOGV("... Address = %s", address);
1132
1133    jint result = CREATE_DEVICE_SUCCESS;
1134    if (dbus_set_error_from_message(&err, msg)) {
1135        if (dbus_error_has_name(&err, "org.bluez.Error.AlreadyExists")) {
1136            result = CREATE_DEVICE_ALREADY_EXISTS;
1137        } else {
1138            result = CREATE_DEVICE_FAILED;
1139        }
1140        LOG_AND_FREE_DBUS_ERROR(&err);
1141    }
1142    env->CallVoidMethod(nat->me,
1143                        method_onCreateDeviceResult,
1144                        env->NewStringUTF(address),
1145                        result);
1146    free(user);
1147}
1148
1149void onDiscoverServicesResult(DBusMessage *msg, void *user, void *n) {
1150    LOGV(__FUNCTION__);
1151
1152    native_data_t *nat = (native_data_t *)n;
1153    const char *path = (const char *)user;
1154    DBusError err;
1155    dbus_error_init(&err);
1156    JNIEnv *env;
1157    nat->vm->GetEnv((void**)&env, nat->envVer);
1158
1159    LOGV("... Device Path = %s", path);
1160
1161    bool result = JNI_TRUE;
1162    if (dbus_set_error_from_message(&err, msg)) {
1163        LOG_AND_FREE_DBUS_ERROR(&err);
1164        result = JNI_FALSE;
1165    }
1166    env->CallVoidMethod(nat->me,
1167                        method_onDiscoverServicesResult,
1168                        env->NewStringUTF(path),
1169                        result);
1170    free(user);
1171}
1172
1173void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) {
1174    LOGV(__FUNCTION__);
1175
1176    const char *address = (const char *) user;
1177    native_data_t *nat = (native_data_t *) n;
1178
1179    DBusError err;
1180    dbus_error_init(&err);
1181    JNIEnv *env;
1182    nat->vm->GetEnv((void**)&env, nat->envVer);
1183
1184    jint channel = -2;
1185
1186    LOGV("... address = %s", address);
1187
1188    if (dbus_set_error_from_message(&err, msg) ||
1189        !dbus_message_get_args(msg, &err,
1190                               DBUS_TYPE_INT32, &channel,
1191                               DBUS_TYPE_INVALID)) {
1192        LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
1193        dbus_error_free(&err);
1194    }
1195
1196done:
1197    env->CallVoidMethod(nat->me,
1198                        method_onGetDeviceServiceChannelResult,
1199                        env->NewStringUTF(address),
1200                        channel);
1201    free(user);
1202}
1203#endif
1204
1205static JNINativeMethod sMethods[] = {
1206     /* name, signature, funcPtr */
1207    {"classInitNative", "()V", (void *)classInitNative},
1208    {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
1209    {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
1210    {"startEventLoopNative", "()V", (void *)startEventLoopNative},
1211    {"stopEventLoopNative", "()V", (void *)stopEventLoopNative},
1212    {"isEventLoopRunningNative", "()Z", (void *)isEventLoopRunningNative}
1213};
1214
1215int register_android_server_BluetoothEventLoop(JNIEnv *env) {
1216    return AndroidRuntime::registerNativeMethods(env,
1217            "android/server/BluetoothEventLoop", sMethods, NELEM(sMethods));
1218}
1219
1220} /* namespace android */
1221