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;
53static jmethodID method_onNetworkDeviceDisconnected;
54static jmethodID method_onNetworkDeviceConnected;
55
56static jmethodID method_onCreatePairedDeviceResult;
57static jmethodID method_onCreateDeviceResult;
58static jmethodID method_onDiscoverServicesResult;
59static jmethodID method_onGetDeviceServiceChannelResult;
60
61static jmethodID method_onRequestPinCode;
62static jmethodID method_onRequestPasskey;
63static jmethodID method_onRequestPasskeyConfirmation;
64static jmethodID method_onRequestPairingConsent;
65static jmethodID method_onDisplayPasskey;
66static jmethodID method_onRequestOobData;
67static jmethodID method_onAgentOutOfBandDataAvailable;
68static jmethodID method_onAgentAuthorize;
69static jmethodID method_onAgentCancel;
70
71static jmethodID method_onInputDevicePropertyChanged;
72static jmethodID method_onInputDeviceConnectionResult;
73static jmethodID method_onPanDevicePropertyChanged;
74static jmethodID method_onPanDeviceConnectionResult;
75static jmethodID method_onHealthDevicePropertyChanged;
76static jmethodID method_onHealthDeviceChannelChanged;
77static jmethodID method_onHealthDeviceConnectionResult;
78
79typedef event_loop_native_data_t native_data_t;
80
81#define EVENT_LOOP_REFS 10
82
83static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
84    return (native_data_t *)(env->GetIntField(object,
85                                                 field_mNativeData));
86}
87
88native_data_t *get_EventLoop_native_data(JNIEnv *env, jobject object) {
89    return get_native_data(env, object);
90}
91
92#endif
93static void classInitNative(JNIEnv* env, jclass clazz) {
94    LOGV("%s", __FUNCTION__);
95
96#ifdef HAVE_BLUETOOTH
97    method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged",
98                                                "([Ljava/lang/String;)V");
99    method_onDevicePropertyChanged = env->GetMethodID(clazz,
100                                                      "onDevicePropertyChanged",
101                                                      "(Ljava/lang/String;[Ljava/lang/String;)V");
102    method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound",
103                                            "(Ljava/lang/String;[Ljava/lang/String;)V");
104    method_onDeviceDisappeared = env->GetMethodID(clazz, "onDeviceDisappeared",
105                                                  "(Ljava/lang/String;)V");
106    method_onDeviceCreated = env->GetMethodID(clazz, "onDeviceCreated", "(Ljava/lang/String;)V");
107    method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V");
108    method_onDeviceDisconnectRequested = env->GetMethodID(clazz, "onDeviceDisconnectRequested",
109                                                        "(Ljava/lang/String;)V");
110    method_onNetworkDeviceConnected = env->GetMethodID(clazz, "onNetworkDeviceConnected",
111                                                     "(Ljava/lang/String;Ljava/lang/String;I)V");
112    method_onNetworkDeviceDisconnected = env->GetMethodID(clazz, "onNetworkDeviceDisconnected",
113                                                              "(Ljava/lang/String;)V");
114
115    method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
116                                                         "(Ljava/lang/String;I)V");
117    method_onCreateDeviceResult = env->GetMethodID(clazz, "onCreateDeviceResult",
118                                                         "(Ljava/lang/String;I)V");
119    method_onDiscoverServicesResult = env->GetMethodID(clazz, "onDiscoverServicesResult",
120                                                         "(Ljava/lang/String;Z)V");
121
122    method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
123                                               "(Ljava/lang/String;Ljava/lang/String;I)V");
124    method_onAgentOutOfBandDataAvailable = env->GetMethodID(clazz, "onAgentOutOfBandDataAvailable",
125                                               "(Ljava/lang/String;)Z");
126    method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
127    method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
128                                               "(Ljava/lang/String;I)V");
129    method_onRequestPasskey = env->GetMethodID(clazz, "onRequestPasskey",
130                                               "(Ljava/lang/String;I)V");
131    method_onRequestPasskeyConfirmation = env->GetMethodID(clazz, "onRequestPasskeyConfirmation",
132                                               "(Ljava/lang/String;II)V");
133    method_onRequestPairingConsent = env->GetMethodID(clazz, "onRequestPairingConsent",
134                                               "(Ljava/lang/String;I)V");
135    method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey",
136                                               "(Ljava/lang/String;II)V");
137    method_onInputDevicePropertyChanged = env->GetMethodID(clazz, "onInputDevicePropertyChanged",
138                                               "(Ljava/lang/String;[Ljava/lang/String;)V");
139    method_onInputDeviceConnectionResult = env->GetMethodID(clazz, "onInputDeviceConnectionResult",
140                                               "(Ljava/lang/String;I)V");
141    method_onPanDevicePropertyChanged = env->GetMethodID(clazz, "onPanDevicePropertyChanged",
142                                               "(Ljava/lang/String;[Ljava/lang/String;)V");
143    method_onPanDeviceConnectionResult = env->GetMethodID(clazz, "onPanDeviceConnectionResult",
144                                               "(Ljava/lang/String;I)V");
145    method_onHealthDeviceConnectionResult = env->GetMethodID(clazz,
146                                                             "onHealthDeviceConnectionResult",
147                                                             "(II)V");
148    method_onHealthDevicePropertyChanged = env->GetMethodID(clazz, "onHealthDevicePropertyChanged",
149                                               "(Ljava/lang/String;[Ljava/lang/String;)V");
150    method_onHealthDeviceChannelChanged = env->GetMethodID(clazz, "onHealthDeviceChannelChanged",
151                                               "(Ljava/lang/String;Ljava/lang/String;Z)V");
152    method_onRequestOobData = env->GetMethodID(clazz, "onRequestOobData",
153                                               "(Ljava/lang/String;I)V");
154
155    field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
156#endif
157}
158
159static void initializeNativeDataNative(JNIEnv* env, jobject object) {
160    LOGV("%s", __FUNCTION__);
161#ifdef HAVE_BLUETOOTH
162    native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
163    if (NULL == nat) {
164        LOGE("%s: out of memory!", __FUNCTION__);
165        return;
166    }
167    memset(nat, 0, sizeof(native_data_t));
168
169    pthread_mutex_init(&(nat->thread_mutex), NULL);
170
171    env->SetIntField(object, field_mNativeData, (jint)nat);
172
173    {
174        DBusError err;
175        dbus_error_init(&err);
176        dbus_threads_init_default();
177        nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
178        if (dbus_error_is_set(&err)) {
179            LOGE("%s: Could not get onto the system bus!", __FUNCTION__);
180            dbus_error_free(&err);
181        }
182        dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
183    }
184#endif
185}
186
187static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
188    LOGV("%s", __FUNCTION__);
189#ifdef HAVE_BLUETOOTH
190    native_data_t *nat =
191            (native_data_t *)env->GetIntField(object, field_mNativeData);
192
193    pthread_mutex_destroy(&(nat->thread_mutex));
194
195    if (nat) {
196        free(nat);
197    }
198#endif
199}
200
201#ifdef HAVE_BLUETOOTH
202static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
203                                      void *data);
204DBusHandlerResult agent_event_filter(DBusConnection *conn,
205                                     DBusMessage *msg,
206                                     void *data);
207static int register_agent(native_data_t *nat,
208                          const char *agent_path, const char *capabilities);
209
210static const DBusObjectPathVTable agent_vtable = {
211    NULL, agent_event_filter, NULL, NULL, NULL, NULL
212};
213
214static unsigned int unix_events_to_dbus_flags(short events) {
215    return (events & DBUS_WATCH_READABLE ? POLLIN : 0) |
216           (events & DBUS_WATCH_WRITABLE ? POLLOUT : 0) |
217           (events & DBUS_WATCH_ERROR ? POLLERR : 0) |
218           (events & DBUS_WATCH_HANGUP ? POLLHUP : 0);
219}
220
221static short dbus_flags_to_unix_events(unsigned int flags) {
222    return (flags & POLLIN ? DBUS_WATCH_READABLE : 0) |
223           (flags & POLLOUT ? DBUS_WATCH_WRITABLE : 0) |
224           (flags & POLLERR ? DBUS_WATCH_ERROR : 0) |
225           (flags & POLLHUP ? DBUS_WATCH_HANGUP : 0);
226}
227
228static jboolean setUpEventLoop(native_data_t *nat) {
229    LOGV("%s", __FUNCTION__);
230
231    if (nat != NULL && nat->conn != NULL) {
232        dbus_threads_init_default();
233        DBusError err;
234        dbus_error_init(&err);
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
243        // Add a filter for all incoming messages
244        if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){
245            return JNI_FALSE;
246        }
247
248        // Set which messages will be processed by this dbus connection
249        dbus_bus_add_match(nat->conn,
250                "type='signal',interface='org.freedesktop.DBus'",
251                &err);
252        if (dbus_error_is_set(&err)) {
253            LOG_AND_FREE_DBUS_ERROR(&err);
254            return JNI_FALSE;
255        }
256        dbus_bus_add_match(nat->conn,
257                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",
258                &err);
259        if (dbus_error_is_set(&err)) {
260            LOG_AND_FREE_DBUS_ERROR(&err);
261            return JNI_FALSE;
262        }
263        dbus_bus_add_match(nat->conn,
264                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'",
265                &err);
266        if (dbus_error_is_set(&err)) {
267            LOG_AND_FREE_DBUS_ERROR(&err);
268            return JNI_FALSE;
269        }
270        dbus_bus_add_match(nat->conn,
271                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Input'",
272                &err);
273        if (dbus_error_is_set(&err)) {
274            LOG_AND_FREE_DBUS_ERROR(&err);
275            return JNI_FALSE;
276        }
277        dbus_bus_add_match(nat->conn,
278                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Network'",
279                &err);
280        if (dbus_error_is_set(&err)) {
281            LOG_AND_FREE_DBUS_ERROR(&err);
282            return JNI_FALSE;
283        }
284        dbus_bus_add_match(nat->conn,
285                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".NetworkServer'",
286                &err);
287        if (dbus_error_is_set(&err)) {
288            LOG_AND_FREE_DBUS_ERROR(&err);
289            return JNI_FALSE;
290        }
291
292        dbus_bus_add_match(nat->conn,
293                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".HealthDevice'",
294                &err);
295        if (dbus_error_is_set(&err)) {
296            LOG_AND_FREE_DBUS_ERROR(&err);
297            return JNI_FALSE;
298        }
299
300        dbus_bus_add_match(nat->conn,
301                "type='signal',interface='org.bluez.AudioSink'",
302                &err);
303        if (dbus_error_is_set(&err)) {
304            LOG_AND_FREE_DBUS_ERROR(&err);
305            return JNI_FALSE;
306        }
307
308        return JNI_TRUE;
309    }
310    return JNI_FALSE;
311}
312
313
314const char * get_adapter_path(DBusConnection *conn) {
315    DBusMessage *msg = NULL, *reply = NULL;
316    DBusError err;
317    const char *device_path = NULL;
318    int attempt = 0;
319
320    for (attempt = 0; attempt < 1000 && reply == NULL; attempt ++) {
321        msg = dbus_message_new_method_call("org.bluez", "/",
322              "org.bluez.Manager", "DefaultAdapter");
323        if (!msg) {
324            LOGE("%s: Can't allocate new method call for get_adapter_path!",
325                  __FUNCTION__);
326            return NULL;
327        }
328        dbus_message_append_args(msg, DBUS_TYPE_INVALID);
329        dbus_error_init(&err);
330        reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
331
332        if (!reply) {
333            if (dbus_error_is_set(&err)) {
334                if (dbus_error_has_name(&err,
335                    "org.freedesktop.DBus.Error.ServiceUnknown")) {
336                    // bluetoothd is still down, retry
337                    LOG_AND_FREE_DBUS_ERROR(&err);
338                    usleep(10000);  // 10 ms
339                    continue;
340                } else {
341                    // Some other error we weren't expecting
342                    LOG_AND_FREE_DBUS_ERROR(&err);
343                }
344            }
345            goto failed;
346        }
347    }
348    if (attempt == 1000) {
349        LOGE("Time out while trying to get Adapter path, is bluetoothd up ?");
350        goto failed;
351    }
352
353    if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
354                               &device_path, DBUS_TYPE_INVALID)
355                               || !device_path){
356        if (dbus_error_is_set(&err)) {
357            LOG_AND_FREE_DBUS_ERROR(&err);
358        }
359        goto failed;
360    }
361    dbus_message_unref(msg);
362    return device_path;
363
364failed:
365    dbus_message_unref(msg);
366    return NULL;
367}
368
369static int register_agent(native_data_t *nat,
370                          const char * agent_path, const char * capabilities)
371{
372    DBusMessage *msg, *reply;
373    DBusError err;
374    dbus_bool_t oob = TRUE;
375
376    if (!dbus_connection_register_object_path(nat->conn, agent_path,
377            &agent_vtable, nat)) {
378        LOGE("%s: Can't register object path %s for agent!",
379              __FUNCTION__, agent_path);
380        return -1;
381    }
382
383    nat->adapter = get_adapter_path(nat->conn);
384    if (nat->adapter == NULL) {
385        return -1;
386    }
387    msg = dbus_message_new_method_call("org.bluez", nat->adapter,
388          "org.bluez.Adapter", "RegisterAgent");
389    if (!msg) {
390        LOGE("%s: Can't allocate new method call for agent!",
391              __FUNCTION__);
392        return -1;
393    }
394    dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
395                             DBUS_TYPE_STRING, &capabilities,
396                             DBUS_TYPE_INVALID);
397
398    dbus_error_init(&err);
399    reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
400    dbus_message_unref(msg);
401
402    if (!reply) {
403        LOGE("%s: Can't register agent!", __FUNCTION__);
404        if (dbus_error_is_set(&err)) {
405            LOG_AND_FREE_DBUS_ERROR(&err);
406        }
407        return -1;
408    }
409
410    dbus_message_unref(reply);
411    dbus_connection_flush(nat->conn);
412
413    return 0;
414}
415
416static void tearDownEventLoop(native_data_t *nat) {
417    LOGV("%s", __FUNCTION__);
418    if (nat != NULL && nat->conn != NULL) {
419
420        DBusMessage *msg, *reply;
421        DBusError err;
422        dbus_error_init(&err);
423        const char * agent_path = "/android/bluetooth/agent";
424
425        msg = dbus_message_new_method_call("org.bluez",
426                                           nat->adapter,
427                                           "org.bluez.Adapter",
428                                           "UnregisterAgent");
429        if (msg != NULL) {
430            dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
431                                     DBUS_TYPE_INVALID);
432            reply = dbus_connection_send_with_reply_and_block(nat->conn,
433                                                              msg, -1, &err);
434
435            if (!reply) {
436                if (dbus_error_is_set(&err)) {
437                    LOG_AND_FREE_DBUS_ERROR(&err);
438                    dbus_error_free(&err);
439                }
440            } else {
441                dbus_message_unref(reply);
442            }
443            dbus_message_unref(msg);
444        } else {
445             LOGE("%s: Can't create new method call!", __FUNCTION__);
446        }
447
448        dbus_connection_flush(nat->conn);
449        dbus_connection_unregister_object_path(nat->conn, agent_path);
450
451        dbus_bus_remove_match(nat->conn,
452                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".AudioSink'",
453                &err);
454        if (dbus_error_is_set(&err)) {
455            LOG_AND_FREE_DBUS_ERROR(&err);
456        }
457        dbus_bus_remove_match(nat->conn,
458                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'",
459                &err);
460        if (dbus_error_is_set(&err)) {
461            LOG_AND_FREE_DBUS_ERROR(&err);
462        }
463        dbus_bus_remove_match(nat->conn,
464                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Input'",
465                &err);
466        if (dbus_error_is_set(&err)) {
467            LOG_AND_FREE_DBUS_ERROR(&err);
468        }
469        dbus_bus_remove_match(nat->conn,
470                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Network'",
471                &err);
472        if (dbus_error_is_set(&err)) {
473            LOG_AND_FREE_DBUS_ERROR(&err);
474        }
475        dbus_bus_remove_match(nat->conn,
476                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".NetworkServer'",
477                &err);
478        if (dbus_error_is_set(&err)) {
479            LOG_AND_FREE_DBUS_ERROR(&err);
480        }
481        dbus_bus_remove_match(nat->conn,
482                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".HealthDevice'",
483                &err);
484        if (dbus_error_is_set(&err)) {
485            LOG_AND_FREE_DBUS_ERROR(&err);
486        }
487        dbus_bus_remove_match(nat->conn,
488                "type='signal',interface='org.bluez.audio.Manager'",
489                &err);
490        if (dbus_error_is_set(&err)) {
491            LOG_AND_FREE_DBUS_ERROR(&err);
492        }
493        dbus_bus_remove_match(nat->conn,
494                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",
495                &err);
496        if (dbus_error_is_set(&err)) {
497            LOG_AND_FREE_DBUS_ERROR(&err);
498        }
499        dbus_bus_remove_match(nat->conn,
500                "type='signal',interface='org.freedesktop.DBus'",
501                &err);
502        if (dbus_error_is_set(&err)) {
503            LOG_AND_FREE_DBUS_ERROR(&err);
504        }
505
506        dbus_connection_remove_filter(nat->conn, event_filter, nat);
507    }
508}
509
510
511#define EVENT_LOOP_EXIT 1
512#define EVENT_LOOP_ADD  2
513#define EVENT_LOOP_REMOVE 3
514#define EVENT_LOOP_WAKEUP 4
515
516dbus_bool_t dbusAddWatch(DBusWatch *watch, void *data) {
517    native_data_t *nat = (native_data_t *)data;
518
519    if (dbus_watch_get_enabled(watch)) {
520        // note that we can't just send the watch and inspect it later
521        // because we may get a removeWatch call before this data is reacted
522        // to by our eventloop and remove this watch..  reading the add first
523        // and then inspecting the recently deceased watch would be bad.
524        char control = EVENT_LOOP_ADD;
525        write(nat->controlFdW, &control, sizeof(char));
526
527        int fd = dbus_watch_get_fd(watch);
528        write(nat->controlFdW, &fd, sizeof(int));
529
530        unsigned int flags = dbus_watch_get_flags(watch);
531        write(nat->controlFdW, &flags, sizeof(unsigned int));
532
533        write(nat->controlFdW, &watch, sizeof(DBusWatch*));
534    }
535    return true;
536}
537
538void dbusRemoveWatch(DBusWatch *watch, void *data) {
539    native_data_t *nat = (native_data_t *)data;
540
541    char control = EVENT_LOOP_REMOVE;
542    write(nat->controlFdW, &control, sizeof(char));
543
544    int fd = dbus_watch_get_fd(watch);
545    write(nat->controlFdW, &fd, sizeof(int));
546
547    unsigned int flags = dbus_watch_get_flags(watch);
548    write(nat->controlFdW, &flags, sizeof(unsigned int));
549}
550
551void dbusToggleWatch(DBusWatch *watch, void *data) {
552    if (dbus_watch_get_enabled(watch)) {
553        dbusAddWatch(watch, data);
554    } else {
555        dbusRemoveWatch(watch, data);
556    }
557}
558
559void dbusWakeup(void *data) {
560    native_data_t *nat = (native_data_t *)data;
561
562    char control = EVENT_LOOP_WAKEUP;
563    write(nat->controlFdW, &control, sizeof(char));
564}
565
566static void handleWatchAdd(native_data_t *nat) {
567    DBusWatch *watch;
568    int newFD;
569    unsigned int flags;
570
571    read(nat->controlFdR, &newFD, sizeof(int));
572    read(nat->controlFdR, &flags, sizeof(unsigned int));
573    read(nat->controlFdR, &watch, sizeof(DBusWatch *));
574    short events = dbus_flags_to_unix_events(flags);
575
576    for (int y = 0; y<nat->pollMemberCount; y++) {
577        if ((nat->pollData[y].fd == newFD) &&
578                (nat->pollData[y].events == events)) {
579            LOGV("DBusWatch duplicate add");
580            return;
581        }
582    }
583    if (nat->pollMemberCount == nat->pollDataSize) {
584        LOGV("Bluetooth EventLoop poll struct growing");
585        struct pollfd *temp = (struct pollfd *)malloc(
586                sizeof(struct pollfd) * (nat->pollMemberCount+1));
587        if (!temp) {
588            return;
589        }
590        memcpy(temp, nat->pollData, sizeof(struct pollfd) *
591                nat->pollMemberCount);
592        free(nat->pollData);
593        nat->pollData = temp;
594        DBusWatch **temp2 = (DBusWatch **)malloc(sizeof(DBusWatch *) *
595                (nat->pollMemberCount+1));
596        if (!temp2) {
597            return;
598        }
599        memcpy(temp2, nat->watchData, sizeof(DBusWatch *) *
600                nat->pollMemberCount);
601        free(nat->watchData);
602        nat->watchData = temp2;
603        nat->pollDataSize++;
604    }
605    nat->pollData[nat->pollMemberCount].fd = newFD;
606    nat->pollData[nat->pollMemberCount].revents = 0;
607    nat->pollData[nat->pollMemberCount].events = events;
608    nat->watchData[nat->pollMemberCount] = watch;
609    nat->pollMemberCount++;
610}
611
612static void handleWatchRemove(native_data_t *nat) {
613    int removeFD;
614    unsigned int flags;
615
616    read(nat->controlFdR, &removeFD, sizeof(int));
617    read(nat->controlFdR, &flags, sizeof(unsigned int));
618    short events = dbus_flags_to_unix_events(flags);
619
620    for (int y = 0; y < nat->pollMemberCount; y++) {
621        if ((nat->pollData[y].fd == removeFD) &&
622                (nat->pollData[y].events == events)) {
623            int newCount = --nat->pollMemberCount;
624            // copy the last live member over this one
625            nat->pollData[y].fd = nat->pollData[newCount].fd;
626            nat->pollData[y].events = nat->pollData[newCount].events;
627            nat->pollData[y].revents = nat->pollData[newCount].revents;
628            nat->watchData[y] = nat->watchData[newCount];
629            return;
630        }
631    }
632    LOGW("WatchRemove given with unknown watch");
633}
634
635static void *eventLoopMain(void *ptr) {
636    native_data_t *nat = (native_data_t *)ptr;
637    JNIEnv *env;
638
639    JavaVMAttachArgs args;
640    char name[] = "BT EventLoop";
641    args.version = nat->envVer;
642    args.name = name;
643    args.group = NULL;
644
645    nat->vm->AttachCurrentThread(&env, &args);
646
647    dbus_connection_set_watch_functions(nat->conn, dbusAddWatch,
648            dbusRemoveWatch, dbusToggleWatch, ptr, NULL);
649    dbus_connection_set_wakeup_main_function(nat->conn, dbusWakeup, ptr, NULL);
650
651    nat->running = true;
652
653    while (1) {
654        for (int i = 0; i < nat->pollMemberCount; i++) {
655            if (!nat->pollData[i].revents) {
656                continue;
657            }
658            if (nat->pollData[i].fd == nat->controlFdR) {
659                char data;
660                while (recv(nat->controlFdR, &data, sizeof(char), MSG_DONTWAIT)
661                        != -1) {
662                    switch (data) {
663                    case EVENT_LOOP_EXIT:
664                    {
665                        dbus_connection_set_watch_functions(nat->conn,
666                                NULL, NULL, NULL, NULL, NULL);
667                        tearDownEventLoop(nat);
668                        nat->vm->DetachCurrentThread();
669
670                        int fd = nat->controlFdR;
671                        nat->controlFdR = 0;
672                        close(fd);
673                        return NULL;
674                    }
675                    case EVENT_LOOP_ADD:
676                    {
677                        handleWatchAdd(nat);
678                        break;
679                    }
680                    case EVENT_LOOP_REMOVE:
681                    {
682                        handleWatchRemove(nat);
683                        break;
684                    }
685                    case EVENT_LOOP_WAKEUP:
686                    {
687                        // noop
688                        break;
689                    }
690                    }
691                }
692            } else {
693                short events = nat->pollData[i].revents;
694                unsigned int flags = unix_events_to_dbus_flags(events);
695                dbus_watch_handle(nat->watchData[i], flags);
696                nat->pollData[i].revents = 0;
697                // can only do one - it may have caused a 'remove'
698                break;
699            }
700        }
701        while (dbus_connection_dispatch(nat->conn) ==
702                DBUS_DISPATCH_DATA_REMAINS) {
703        }
704
705        poll(nat->pollData, nat->pollMemberCount, -1);
706    }
707}
708#endif // HAVE_BLUETOOTH
709
710static jboolean startEventLoopNative(JNIEnv *env, jobject object) {
711    jboolean result = JNI_FALSE;
712#ifdef HAVE_BLUETOOTH
713    event_loop_native_data_t *nat = get_native_data(env, object);
714
715    pthread_mutex_lock(&(nat->thread_mutex));
716
717    nat->running = false;
718
719    if (nat->pollData) {
720        LOGW("trying to start EventLoop a second time!");
721        pthread_mutex_unlock( &(nat->thread_mutex) );
722        return JNI_FALSE;
723    }
724
725    nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) *
726            DEFAULT_INITIAL_POLLFD_COUNT);
727    if (!nat->pollData) {
728        LOGE("out of memory error starting EventLoop!");
729        goto done;
730    }
731
732    nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) *
733            DEFAULT_INITIAL_POLLFD_COUNT);
734    if (!nat->watchData) {
735        LOGE("out of memory error starting EventLoop!");
736        goto done;
737    }
738
739    memset(nat->pollData, 0, sizeof(struct pollfd) *
740            DEFAULT_INITIAL_POLLFD_COUNT);
741    memset(nat->watchData, 0, sizeof(DBusWatch *) *
742            DEFAULT_INITIAL_POLLFD_COUNT);
743    nat->pollDataSize = DEFAULT_INITIAL_POLLFD_COUNT;
744    nat->pollMemberCount = 1;
745
746    if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &(nat->controlFdR))) {
747        LOGE("Error getting BT control socket");
748        goto done;
749    }
750    nat->pollData[0].fd = nat->controlFdR;
751    nat->pollData[0].events = POLLIN;
752
753    env->GetJavaVM( &(nat->vm) );
754    nat->envVer = env->GetVersion();
755
756    nat->me = env->NewGlobalRef(object);
757
758    if (setUpEventLoop(nat) != JNI_TRUE) {
759        LOGE("failure setting up Event Loop!");
760        goto done;
761    }
762
763    pthread_create(&(nat->thread), NULL, eventLoopMain, nat);
764    result = JNI_TRUE;
765
766done:
767    if (JNI_FALSE == result) {
768        if (nat->controlFdW) {
769            close(nat->controlFdW);
770            nat->controlFdW = 0;
771        }
772        if (nat->controlFdR) {
773            close(nat->controlFdR);
774            nat->controlFdR = 0;
775        }
776        if (nat->me) env->DeleteGlobalRef(nat->me);
777        nat->me = NULL;
778        if (nat->pollData) free(nat->pollData);
779        nat->pollData = NULL;
780        if (nat->watchData) free(nat->watchData);
781        nat->watchData = NULL;
782        nat->pollDataSize = 0;
783        nat->pollMemberCount = 0;
784    }
785
786    pthread_mutex_unlock(&(nat->thread_mutex));
787#endif // HAVE_BLUETOOTH
788    return result;
789}
790
791static void stopEventLoopNative(JNIEnv *env, jobject object) {
792#ifdef HAVE_BLUETOOTH
793    native_data_t *nat = get_native_data(env, object);
794
795    pthread_mutex_lock(&(nat->thread_mutex));
796    if (nat->pollData) {
797        char data = EVENT_LOOP_EXIT;
798        ssize_t t = write(nat->controlFdW, &data, sizeof(char));
799        void *ret;
800        pthread_join(nat->thread, &ret);
801
802        env->DeleteGlobalRef(nat->me);
803        nat->me = NULL;
804        free(nat->pollData);
805        nat->pollData = NULL;
806        free(nat->watchData);
807        nat->watchData = NULL;
808        nat->pollDataSize = 0;
809        nat->pollMemberCount = 0;
810
811        int fd = nat->controlFdW;
812        nat->controlFdW = 0;
813        close(fd);
814    }
815    nat->running = false;
816    pthread_mutex_unlock(&(nat->thread_mutex));
817#endif // HAVE_BLUETOOTH
818}
819
820static jboolean isEventLoopRunningNative(JNIEnv *env, jobject object) {
821    jboolean result = JNI_FALSE;
822#ifdef HAVE_BLUETOOTH
823    native_data_t *nat = get_native_data(env, object);
824
825    pthread_mutex_lock(&(nat->thread_mutex));
826    if (nat->running) {
827        result = JNI_TRUE;
828    }
829    pthread_mutex_unlock(&(nat->thread_mutex));
830
831#endif // HAVE_BLUETOOTH
832    return result;
833}
834
835#ifdef HAVE_BLUETOOTH
836extern DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env);
837
838// Called by dbus during WaitForAndDispatchEventNative()
839static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
840                                      void *data) {
841    native_data_t *nat;
842    JNIEnv *env;
843    DBusError err;
844    DBusHandlerResult ret;
845
846    dbus_error_init(&err);
847
848    nat = (native_data_t *)data;
849    nat->vm->GetEnv((void**)&env, nat->envVer);
850    if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) {
851        LOGV("%s: not interested (not a signal).", __FUNCTION__);
852        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
853    }
854
855    LOGV("%s: Received signal %s:%s from %s", __FUNCTION__,
856        dbus_message_get_interface(msg), dbus_message_get_member(msg),
857        dbus_message_get_path(msg));
858
859    env->PushLocalFrame(EVENT_LOOP_REFS);
860    if (dbus_message_is_signal(msg,
861                               "org.bluez.Adapter",
862                               "DeviceFound")) {
863        char *c_address;
864        DBusMessageIter iter;
865        jobjectArray str_array = NULL;
866        if (dbus_message_iter_init(msg, &iter)) {
867            dbus_message_iter_get_basic(&iter, &c_address);
868            if (dbus_message_iter_next(&iter))
869                str_array =
870                    parse_remote_device_properties(env, &iter);
871        }
872        if (str_array != NULL) {
873            env->CallVoidMethod(nat->me,
874                                method_onDeviceFound,
875                                env->NewStringUTF(c_address),
876                                str_array);
877        } else
878            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
879        goto success;
880    } else if (dbus_message_is_signal(msg,
881                                     "org.bluez.Adapter",
882                                     "DeviceDisappeared")) {
883        char *c_address;
884        if (dbus_message_get_args(msg, &err,
885                                  DBUS_TYPE_STRING, &c_address,
886                                  DBUS_TYPE_INVALID)) {
887            LOGV("... address = %s", c_address);
888            env->CallVoidMethod(nat->me, method_onDeviceDisappeared,
889                                env->NewStringUTF(c_address));
890        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
891        goto success;
892    } else if (dbus_message_is_signal(msg,
893                                     "org.bluez.Adapter",
894                                     "DeviceCreated")) {
895        char *c_object_path;
896        if (dbus_message_get_args(msg, &err,
897                                  DBUS_TYPE_OBJECT_PATH, &c_object_path,
898                                  DBUS_TYPE_INVALID)) {
899            LOGV("... address = %s", c_object_path);
900            env->CallVoidMethod(nat->me,
901                                method_onDeviceCreated,
902                                env->NewStringUTF(c_object_path));
903        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
904        goto success;
905    } else if (dbus_message_is_signal(msg,
906                                     "org.bluez.Adapter",
907                                     "DeviceRemoved")) {
908        char *c_object_path;
909        if (dbus_message_get_args(msg, &err,
910                                 DBUS_TYPE_OBJECT_PATH, &c_object_path,
911                                 DBUS_TYPE_INVALID)) {
912           LOGV("... Object Path = %s", c_object_path);
913           env->CallVoidMethod(nat->me,
914                               method_onDeviceRemoved,
915                               env->NewStringUTF(c_object_path));
916        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
917        goto success;
918    } else if (dbus_message_is_signal(msg,
919                                      "org.bluez.Adapter",
920                                      "PropertyChanged")) {
921        jobjectArray str_array = parse_adapter_property_change(env, msg);
922        if (str_array != NULL) {
923            /* Check if bluetoothd has (re)started, if so update the path. */
924            jstring property =(jstring) env->GetObjectArrayElement(str_array, 0);
925            const char *c_property = env->GetStringUTFChars(property, NULL);
926            if (!strncmp(c_property, "Powered", strlen("Powered"))) {
927                jstring value =
928                    (jstring) env->GetObjectArrayElement(str_array, 1);
929                const char *c_value = env->GetStringUTFChars(value, NULL);
930                if (!strncmp(c_value, "true", strlen("true")))
931                    nat->adapter = get_adapter_path(nat->conn);
932                env->ReleaseStringUTFChars(value, c_value);
933            }
934            env->ReleaseStringUTFChars(property, c_property);
935
936            env->CallVoidMethod(nat->me,
937                              method_onPropertyChanged,
938                              str_array);
939        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
940        goto success;
941    } else if (dbus_message_is_signal(msg,
942                                      "org.bluez.Device",
943                                      "PropertyChanged")) {
944        jobjectArray str_array = parse_remote_device_property_change(env, msg);
945        if (str_array != NULL) {
946            const char *remote_device_path = dbus_message_get_path(msg);
947            env->CallVoidMethod(nat->me,
948                            method_onDevicePropertyChanged,
949                            env->NewStringUTF(remote_device_path),
950                            str_array);
951        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
952        goto success;
953    } else if (dbus_message_is_signal(msg,
954                                      "org.bluez.Device",
955                                      "DisconnectRequested")) {
956        const char *remote_device_path = dbus_message_get_path(msg);
957        env->CallVoidMethod(nat->me,
958                            method_onDeviceDisconnectRequested,
959                            env->NewStringUTF(remote_device_path));
960        goto success;
961    } else if (dbus_message_is_signal(msg,
962                                      "org.bluez.Input",
963                                      "PropertyChanged")) {
964
965        jobjectArray str_array =
966                    parse_input_property_change(env, msg);
967        if (str_array != NULL) {
968            const char *c_path = dbus_message_get_path(msg);
969            env->CallVoidMethod(nat->me,
970                                method_onInputDevicePropertyChanged,
971                                env->NewStringUTF(c_path),
972                                str_array);
973        } else {
974            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
975        }
976        goto success;
977    } else if (dbus_message_is_signal(msg,
978                                     "org.bluez.Network",
979                                     "PropertyChanged")) {
980
981       jobjectArray str_array =
982                   parse_pan_property_change(env, msg);
983       if (str_array != NULL) {
984           const char *c_path = dbus_message_get_path(msg);
985           env->CallVoidMethod(nat->me,
986                               method_onPanDevicePropertyChanged,
987                               env->NewStringUTF(c_path),
988                               str_array);
989       } else {
990           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
991       }
992       goto success;
993    } else if (dbus_message_is_signal(msg,
994                                     "org.bluez.NetworkServer",
995                                     "DeviceDisconnected")) {
996       char *c_address;
997       if (dbus_message_get_args(msg, &err,
998                                  DBUS_TYPE_STRING, &c_address,
999                                  DBUS_TYPE_INVALID)) {
1000           env->CallVoidMethod(nat->me,
1001                               method_onNetworkDeviceDisconnected,
1002                               env->NewStringUTF(c_address));
1003       } else {
1004           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
1005       }
1006       goto success;
1007    } else if (dbus_message_is_signal(msg,
1008                                     "org.bluez.NetworkServer",
1009                                     "DeviceConnected")) {
1010       char *c_address;
1011       char *c_iface;
1012       uint16_t uuid;
1013
1014       if (dbus_message_get_args(msg, &err,
1015                                  DBUS_TYPE_STRING, &c_address,
1016                                  DBUS_TYPE_STRING, &c_iface,
1017                                  DBUS_TYPE_UINT16, &uuid,
1018                                  DBUS_TYPE_INVALID)) {
1019           env->CallVoidMethod(nat->me,
1020                               method_onNetworkDeviceConnected,
1021                               env->NewStringUTF(c_address),
1022                               env->NewStringUTF(c_iface),
1023                               uuid);
1024       } else {
1025           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
1026       }
1027       goto success;
1028    } else if (dbus_message_is_signal(msg,
1029                                     "org.bluez.HealthDevice",
1030                                     "ChannelConnected")) {
1031       const char *c_path = dbus_message_get_path(msg);
1032       const char *c_channel_path;
1033       jboolean exists = JNI_TRUE;
1034       if (dbus_message_get_args(msg, &err,
1035                                  DBUS_TYPE_OBJECT_PATH, &c_channel_path,
1036                                  DBUS_TYPE_INVALID)) {
1037           env->CallVoidMethod(nat->me,
1038                               method_onHealthDeviceChannelChanged,
1039                               env->NewStringUTF(c_path),
1040                               env->NewStringUTF(c_channel_path),
1041                               exists);
1042       } else {
1043           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
1044       }
1045       goto success;
1046    } else if (dbus_message_is_signal(msg,
1047                                     "org.bluez.HealthDevice",
1048                                     "ChannelDeleted")) {
1049
1050       const char *c_path = dbus_message_get_path(msg);
1051       const char *c_channel_path;
1052       jboolean exists = JNI_FALSE;
1053       if (dbus_message_get_args(msg, &err,
1054                                  DBUS_TYPE_OBJECT_PATH, &c_channel_path,
1055                                  DBUS_TYPE_INVALID)) {
1056           env->CallVoidMethod(nat->me,
1057                               method_onHealthDeviceChannelChanged,
1058                               env->NewStringUTF(c_path),
1059                               env->NewStringUTF(c_channel_path),
1060                               exists);
1061       } else {
1062           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
1063       }
1064       goto success;
1065    } else if (dbus_message_is_signal(msg,
1066                                     "org.bluez.HealthDevice",
1067                                     "PropertyChanged")) {
1068        jobjectArray str_array =
1069                    parse_health_device_property_change(env, msg);
1070        if (str_array != NULL) {
1071            const char *c_path = dbus_message_get_path(msg);
1072            env->CallVoidMethod(nat->me,
1073                                method_onHealthDevicePropertyChanged,
1074                                env->NewStringUTF(c_path),
1075                                str_array);
1076       } else {
1077           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
1078       }
1079       goto success;
1080    }
1081
1082    ret = a2dp_event_filter(msg, env);
1083    env->PopLocalFrame(NULL);
1084    return ret;
1085
1086success:
1087    env->PopLocalFrame(NULL);
1088    return DBUS_HANDLER_RESULT_HANDLED;
1089}
1090
1091// Called by dbus during WaitForAndDispatchEventNative()
1092DBusHandlerResult agent_event_filter(DBusConnection *conn,
1093                                     DBusMessage *msg, void *data) {
1094    native_data_t *nat = (native_data_t *)data;
1095    JNIEnv *env;
1096    if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
1097        LOGV("%s: not interested (not a method call).", __FUNCTION__);
1098        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1099    }
1100    LOGI("%s: Received method %s:%s", __FUNCTION__,
1101         dbus_message_get_interface(msg), dbus_message_get_member(msg));
1102
1103    if (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED;
1104
1105    nat->vm->GetEnv((void**)&env, nat->envVer);
1106    env->PushLocalFrame(EVENT_LOOP_REFS);
1107
1108    if (dbus_message_is_method_call(msg,
1109            "org.bluez.Agent", "Cancel")) {
1110        env->CallVoidMethod(nat->me, method_onAgentCancel);
1111        // reply
1112        DBusMessage *reply = dbus_message_new_method_return(msg);
1113        if (!reply) {
1114            LOGE("%s: Cannot create message reply\n", __FUNCTION__);
1115            goto failure;
1116        }
1117        dbus_connection_send(nat->conn, reply, NULL);
1118        dbus_message_unref(reply);
1119        goto success;
1120
1121    } else if (dbus_message_is_method_call(msg,
1122            "org.bluez.Agent", "Authorize")) {
1123        char *object_path;
1124        const char *uuid;
1125        if (!dbus_message_get_args(msg, NULL,
1126                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1127                                   DBUS_TYPE_STRING, &uuid,
1128                                   DBUS_TYPE_INVALID)) {
1129            LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__);
1130            goto failure;
1131        }
1132
1133        LOGV("... object_path = %s", object_path);
1134        LOGV("... uuid = %s", uuid);
1135
1136        dbus_message_ref(msg);  // increment refcount because we pass to java
1137        env->CallVoidMethod(nat->me, method_onAgentAuthorize,
1138                env->NewStringUTF(object_path), env->NewStringUTF(uuid),
1139                int(msg));
1140
1141        goto success;
1142    } else if (dbus_message_is_method_call(msg,
1143            "org.bluez.Agent", "OutOfBandAvailable")) {
1144        char *object_path;
1145        if (!dbus_message_get_args(msg, NULL,
1146                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1147                                   DBUS_TYPE_INVALID)) {
1148            LOGE("%s: Invalid arguments for OutOfBandData available() method", __FUNCTION__);
1149            goto failure;
1150        }
1151
1152        LOGV("... object_path = %s", object_path);
1153
1154        bool available =
1155            env->CallBooleanMethod(nat->me, method_onAgentOutOfBandDataAvailable,
1156                env->NewStringUTF(object_path));
1157
1158
1159        // reply
1160        if (available) {
1161            DBusMessage *reply = dbus_message_new_method_return(msg);
1162            if (!reply) {
1163                LOGE("%s: Cannot create message reply\n", __FUNCTION__);
1164                goto failure;
1165            }
1166            dbus_connection_send(nat->conn, reply, NULL);
1167            dbus_message_unref(reply);
1168        } else {
1169            DBusMessage *reply = dbus_message_new_error(msg,
1170                    "org.bluez.Error.DoesNotExist", "OutofBand data not available");
1171            if (!reply) {
1172                LOGE("%s: Cannot create message reply\n", __FUNCTION__);
1173                goto failure;
1174            }
1175            dbus_connection_send(nat->conn, reply, NULL);
1176            dbus_message_unref(reply);
1177        }
1178        goto success;
1179    } else if (dbus_message_is_method_call(msg,
1180            "org.bluez.Agent", "RequestPinCode")) {
1181        char *object_path;
1182        if (!dbus_message_get_args(msg, NULL,
1183                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1184                                   DBUS_TYPE_INVALID)) {
1185            LOGE("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__);
1186            goto failure;
1187        }
1188
1189        dbus_message_ref(msg);  // increment refcount because we pass to java
1190        env->CallVoidMethod(nat->me, method_onRequestPinCode,
1191                                       env->NewStringUTF(object_path),
1192                                       int(msg));
1193        goto success;
1194    } else if (dbus_message_is_method_call(msg,
1195            "org.bluez.Agent", "RequestPasskey")) {
1196        char *object_path;
1197        if (!dbus_message_get_args(msg, NULL,
1198                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1199                                   DBUS_TYPE_INVALID)) {
1200            LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
1201            goto failure;
1202        }
1203
1204        dbus_message_ref(msg);  // increment refcount because we pass to java
1205        env->CallVoidMethod(nat->me, method_onRequestPasskey,
1206                                       env->NewStringUTF(object_path),
1207                                       int(msg));
1208        goto success;
1209    } else if (dbus_message_is_method_call(msg,
1210            "org.bluez.Agent", "RequestOobData")) {
1211        char *object_path;
1212        if (!dbus_message_get_args(msg, NULL,
1213                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1214                                   DBUS_TYPE_INVALID)) {
1215            LOGE("%s: Invalid arguments for RequestOobData() method", __FUNCTION__);
1216            goto failure;
1217        }
1218
1219        dbus_message_ref(msg);  // increment refcount because we pass to java
1220        env->CallVoidMethod(nat->me, method_onRequestOobData,
1221                                       env->NewStringUTF(object_path),
1222                                       int(msg));
1223        goto success;
1224    } else if (dbus_message_is_method_call(msg,
1225            "org.bluez.Agent", "DisplayPasskey")) {
1226        char *object_path;
1227        uint32_t passkey;
1228        if (!dbus_message_get_args(msg, NULL,
1229                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1230                                   DBUS_TYPE_UINT32, &passkey,
1231                                   DBUS_TYPE_INVALID)) {
1232            LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
1233            goto failure;
1234        }
1235
1236        dbus_message_ref(msg);  // increment refcount because we pass to java
1237        env->CallVoidMethod(nat->me, method_onDisplayPasskey,
1238                                       env->NewStringUTF(object_path),
1239                                       passkey,
1240                                       int(msg));
1241        goto success;
1242    } else if (dbus_message_is_method_call(msg,
1243            "org.bluez.Agent", "RequestConfirmation")) {
1244        char *object_path;
1245        uint32_t passkey;
1246        if (!dbus_message_get_args(msg, NULL,
1247                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1248                                   DBUS_TYPE_UINT32, &passkey,
1249                                   DBUS_TYPE_INVALID)) {
1250            LOGE("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__);
1251            goto failure;
1252        }
1253
1254        dbus_message_ref(msg);  // increment refcount because we pass to java
1255        env->CallVoidMethod(nat->me, method_onRequestPasskeyConfirmation,
1256                                       env->NewStringUTF(object_path),
1257                                       passkey,
1258                                       int(msg));
1259        goto success;
1260    } else if (dbus_message_is_method_call(msg,
1261            "org.bluez.Agent", "RequestPairingConsent")) {
1262        char *object_path;
1263        if (!dbus_message_get_args(msg, NULL,
1264                                   DBUS_TYPE_OBJECT_PATH, &object_path,
1265                                   DBUS_TYPE_INVALID)) {
1266            LOGE("%s: Invalid arguments for RequestPairingConsent() method", __FUNCTION__);
1267            goto failure;
1268        }
1269
1270        dbus_message_ref(msg);  // increment refcount because we pass to java
1271        env->CallVoidMethod(nat->me, method_onRequestPairingConsent,
1272                                       env->NewStringUTF(object_path),
1273                                       int(msg));
1274        goto success;
1275    } else if (dbus_message_is_method_call(msg,
1276                  "org.bluez.Agent", "Release")) {
1277        // reply
1278        DBusMessage *reply = dbus_message_new_method_return(msg);
1279        if (!reply) {
1280            LOGE("%s: Cannot create message reply\n", __FUNCTION__);
1281            goto failure;
1282        }
1283        dbus_connection_send(nat->conn, reply, NULL);
1284        dbus_message_unref(reply);
1285        goto success;
1286    } else {
1287        LOGV("%s:%s is ignored", dbus_message_get_interface(msg), dbus_message_get_member(msg));
1288    }
1289
1290failure:
1291    env->PopLocalFrame(NULL);
1292    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1293
1294success:
1295    env->PopLocalFrame(NULL);
1296    return DBUS_HANDLER_RESULT_HANDLED;
1297
1298}
1299#endif
1300
1301
1302#ifdef HAVE_BLUETOOTH
1303
1304void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) {
1305    LOGV("%s", __FUNCTION__);
1306
1307    native_data_t *nat = (native_data_t *)n;
1308    const char *address = (const char *)user;
1309    DBusError err;
1310    dbus_error_init(&err);
1311    JNIEnv *env;
1312    jstring addr;
1313
1314    nat->vm->GetEnv((void**)&env, nat->envVer);
1315
1316    LOGV("... address = %s", address);
1317
1318    jint result = BOND_RESULT_SUCCESS;
1319    if (dbus_set_error_from_message(&err, msg)) {
1320        if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) {
1321            // Pins did not match, or remote device did not respond to pin
1322            // request in time
1323            LOGV("... error = %s (%s)\n", err.name, err.message);
1324            result = BOND_RESULT_AUTH_FAILED;
1325        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationRejected")) {
1326            // We rejected pairing, or the remote side rejected pairing. This
1327            // happens if either side presses 'cancel' at the pairing dialog.
1328            LOGV("... error = %s (%s)\n", err.name, err.message);
1329            result = BOND_RESULT_AUTH_REJECTED;
1330        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationCanceled")) {
1331            // Not sure if this happens
1332            LOGV("... error = %s (%s)\n", err.name, err.message);
1333            result = BOND_RESULT_AUTH_CANCELED;
1334        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.ConnectionAttemptFailed")) {
1335            // Other device is not responding at all
1336            LOGV("... error = %s (%s)\n", err.name, err.message);
1337            result = BOND_RESULT_REMOTE_DEVICE_DOWN;
1338        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AlreadyExists")) {
1339            // already bonded
1340            LOGV("... error = %s (%s)\n", err.name, err.message);
1341            result = BOND_RESULT_SUCCESS;
1342        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") &&
1343                   !strcmp(err.message, "Bonding in progress")) {
1344            LOGV("... error = %s (%s)\n", err.name, err.message);
1345            goto done;
1346        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") &&
1347                   !strcmp(err.message, "Discover in progress")) {
1348            LOGV("... error = %s (%s)\n", err.name, err.message);
1349            result = BOND_RESULT_DISCOVERY_IN_PROGRESS;
1350        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.RepeatedAttempts")) {
1351            LOGV("... error = %s (%s)\n", err.name, err.message);
1352            result = BOND_RESULT_REPEATED_ATTEMPTS;
1353        } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationTimeout")) {
1354            LOGV("... error = %s (%s)\n", err.name, err.message);
1355            result = BOND_RESULT_AUTH_TIMEOUT;
1356        } else {
1357            LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
1358            result = BOND_RESULT_ERROR;
1359        }
1360    }
1361
1362    addr = env->NewStringUTF(address);
1363    env->CallVoidMethod(nat->me,
1364                        method_onCreatePairedDeviceResult,
1365                        addr,
1366                        result);
1367    env->DeleteLocalRef(addr);
1368done:
1369    dbus_error_free(&err);
1370    free(user);
1371}
1372
1373void onCreateDeviceResult(DBusMessage *msg, void *user, void *n) {
1374    LOGV("%s", __FUNCTION__);
1375
1376    native_data_t *nat = (native_data_t *)n;
1377    const char *address= (const char *)user;
1378    DBusError err;
1379    dbus_error_init(&err);
1380    JNIEnv *env;
1381    nat->vm->GetEnv((void**)&env, nat->envVer);
1382
1383    LOGV("... Address = %s", address);
1384
1385    jint result = CREATE_DEVICE_SUCCESS;
1386    if (dbus_set_error_from_message(&err, msg)) {
1387        if (dbus_error_has_name(&err, "org.bluez.Error.AlreadyExists")) {
1388            result = CREATE_DEVICE_ALREADY_EXISTS;
1389        } else {
1390            result = CREATE_DEVICE_FAILED;
1391        }
1392        LOG_AND_FREE_DBUS_ERROR(&err);
1393    }
1394    jstring addr = env->NewStringUTF(address);
1395    env->CallVoidMethod(nat->me,
1396                        method_onCreateDeviceResult,
1397                        addr,
1398                        result);
1399    env->DeleteLocalRef(addr);
1400    free(user);
1401}
1402
1403void onDiscoverServicesResult(DBusMessage *msg, void *user, void *n) {
1404    LOGV("%s", __FUNCTION__);
1405
1406    native_data_t *nat = (native_data_t *)n;
1407    const char *path = (const char *)user;
1408    DBusError err;
1409    dbus_error_init(&err);
1410    JNIEnv *env;
1411    nat->vm->GetEnv((void**)&env, nat->envVer);
1412
1413    LOGV("... Device Path = %s", path);
1414
1415    bool result = JNI_TRUE;
1416    if (dbus_set_error_from_message(&err, msg)) {
1417        LOG_AND_FREE_DBUS_ERROR(&err);
1418        result = JNI_FALSE;
1419    }
1420    jstring jPath = env->NewStringUTF(path);
1421    env->CallVoidMethod(nat->me,
1422                        method_onDiscoverServicesResult,
1423                        jPath,
1424                        result);
1425    env->DeleteLocalRef(jPath);
1426    free(user);
1427}
1428
1429void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) {
1430    LOGV("%s", __FUNCTION__);
1431
1432    const char *address = (const char *) user;
1433    native_data_t *nat = (native_data_t *) n;
1434
1435    DBusError err;
1436    dbus_error_init(&err);
1437    JNIEnv *env;
1438    nat->vm->GetEnv((void**)&env, nat->envVer);
1439
1440    jint channel = -2;
1441
1442    LOGV("... address = %s", address);
1443
1444    if (dbus_set_error_from_message(&err, msg) ||
1445        !dbus_message_get_args(msg, &err,
1446                               DBUS_TYPE_INT32, &channel,
1447                               DBUS_TYPE_INVALID)) {
1448        LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
1449        dbus_error_free(&err);
1450    }
1451
1452done:
1453    jstring addr = env->NewStringUTF(address);
1454    env->CallVoidMethod(nat->me,
1455                        method_onGetDeviceServiceChannelResult,
1456                        addr,
1457                        channel);
1458    env->DeleteLocalRef(addr);
1459    free(user);
1460}
1461
1462void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *n) {
1463    LOGV("%s", __FUNCTION__);
1464
1465    native_data_t *nat = (native_data_t *)n;
1466    const char *path = (const char *)user;
1467    DBusError err;
1468    dbus_error_init(&err);
1469    JNIEnv *env;
1470    nat->vm->GetEnv((void**)&env, nat->envVer);
1471
1472    jint result = INPUT_OPERATION_SUCCESS;
1473    if (dbus_set_error_from_message(&err, msg)) {
1474        if (!strcmp(err.name, BLUEZ_ERROR_IFC ".ConnectionAttemptFailed")) {
1475            result = INPUT_CONNECT_FAILED_ATTEMPT_FAILED;
1476        } else if (!strcmp(err.name, BLUEZ_ERROR_IFC ".AlreadyConnected")) {
1477            result = INPUT_CONNECT_FAILED_ALREADY_CONNECTED;
1478        } else if (!strcmp(err.name, BLUEZ_ERROR_IFC ".Failed")) {
1479            // TODO():This is flaky, need to change Bluez to add new error codes
1480            if (!strcmp(err.message, "Transport endpoint is not connected")) {
1481              result = INPUT_DISCONNECT_FAILED_NOT_CONNECTED;
1482            } else {
1483              result = INPUT_OPERATION_GENERIC_FAILURE;
1484            }
1485        } else {
1486            result = INPUT_OPERATION_GENERIC_FAILURE;
1487        }
1488        LOG_AND_FREE_DBUS_ERROR(&err);
1489    }
1490
1491    LOGV("... Device Path = %s, result = %d", path, result);
1492    jstring jPath = env->NewStringUTF(path);
1493    env->CallVoidMethod(nat->me,
1494                        method_onInputDeviceConnectionResult,
1495                        jPath,
1496                        result);
1497    env->DeleteLocalRef(jPath);
1498    free(user);
1499}
1500
1501void onPanDeviceConnectionResult(DBusMessage *msg, void *user, void *n) {
1502    LOGV("%s", __FUNCTION__);
1503
1504    native_data_t *nat = (native_data_t *)n;
1505    const char *path = (const char *)user;
1506    DBusError err;
1507    dbus_error_init(&err);
1508    JNIEnv *env;
1509    nat->vm->GetEnv((void**)&env, nat->envVer);
1510
1511    jint result = PAN_OPERATION_SUCCESS;
1512    if (dbus_set_error_from_message(&err, msg)) {
1513        if (!strcmp(err.name, BLUEZ_ERROR_IFC ".ConnectionAttemptFailed")) {
1514            result = PAN_CONNECT_FAILED_ATTEMPT_FAILED;
1515        } else if (!strcmp(err.name, BLUEZ_ERROR_IFC ".Failed")) {
1516            // TODO():This is flaky, need to change Bluez to add new error codes
1517            if (!strcmp(err.message, "Device already connected")) {
1518                result = PAN_CONNECT_FAILED_ALREADY_CONNECTED;
1519            } else if (!strcmp(err.message, "Device not connected")) {
1520                result = PAN_DISCONNECT_FAILED_NOT_CONNECTED;
1521            } else {
1522                result = PAN_OPERATION_GENERIC_FAILURE;
1523            }
1524        } else {
1525            result = PAN_OPERATION_GENERIC_FAILURE;
1526        }
1527        LOG_AND_FREE_DBUS_ERROR(&err);
1528    }
1529
1530    LOGV("... Pan Device Path = %s, result = %d", path, result);
1531    jstring jPath = env->NewStringUTF(path);
1532    env->CallVoidMethod(nat->me,
1533                        method_onPanDeviceConnectionResult,
1534                        jPath,
1535                        result);
1536    env->DeleteLocalRef(jPath);
1537    free(user);
1538}
1539
1540void onHealthDeviceConnectionResult(DBusMessage *msg, void *user, void *n) {
1541    LOGV("%s", __FUNCTION__);
1542
1543    native_data_t *nat = (native_data_t *)n;
1544    DBusError err;
1545    dbus_error_init(&err);
1546    JNIEnv *env;
1547    nat->vm->GetEnv((void**)&env, nat->envVer);
1548
1549    jint result = HEALTH_OPERATION_SUCCESS;
1550    if (dbus_set_error_from_message(&err, msg)) {
1551        if (!strcmp(err.name, BLUEZ_ERROR_IFC ".InvalidArgs")) {
1552            result = HEALTH_OPERATION_INVALID_ARGS;
1553        } else if (!strcmp(err.name, BLUEZ_ERROR_IFC ".HealthError")) {
1554            result = HEALTH_OPERATION_ERROR;
1555        } else if (!strcmp(err.name, BLUEZ_ERROR_IFC ".NotFound")) {
1556            result = HEALTH_OPERATION_NOT_FOUND;
1557        } else if (!strcmp(err.name, BLUEZ_ERROR_IFC ".NotAllowed")) {
1558            result = HEALTH_OPERATION_NOT_ALLOWED;
1559        } else {
1560            result = HEALTH_OPERATION_GENERIC_FAILURE;
1561        }
1562        LOG_AND_FREE_DBUS_ERROR(&err);
1563    }
1564
1565    jint code = *(int *) user;
1566    LOGV("... Health Device Code = %d, result = %d", code, result);
1567    env->CallVoidMethod(nat->me,
1568                        method_onHealthDeviceConnectionResult,
1569                        code,
1570                        result);
1571    free(user);
1572}
1573#endif
1574
1575static JNINativeMethod sMethods[] = {
1576     /* name, signature, funcPtr */
1577    {"classInitNative", "()V", (void *)classInitNative},
1578    {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
1579    {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
1580    {"startEventLoopNative", "()V", (void *)startEventLoopNative},
1581    {"stopEventLoopNative", "()V", (void *)stopEventLoopNative},
1582    {"isEventLoopRunningNative", "()Z", (void *)isEventLoopRunningNative}
1583};
1584
1585int register_android_server_BluetoothEventLoop(JNIEnv *env) {
1586    return AndroidRuntime::registerNativeMethods(env,
1587            "android/server/BluetoothEventLoop", sMethods, NELEM(sMethods));
1588}
1589
1590} /* namespace android */
1591